mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-14 01:04:45 +03:00
Merge branch 'master' into tests-for-open-bugs
This commit is contained in:
commit
1284f1682f
10
.travis.yml
10
.travis.yml
|
@ -24,7 +24,6 @@ install:
|
|||
- "travis_retry pip install cffi"
|
||||
- "travis_retry pip install nose"
|
||||
- "travis_retry pip install check-manifest"
|
||||
- "travis_retry pip install Sphinx"
|
||||
# Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those
|
||||
- if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi
|
||||
|
||||
|
@ -33,6 +32,9 @@ install:
|
|||
# Coverage 4.0 doesn't support Python 3.2
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi
|
||||
|
||||
# docs only on python 2.7
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi
|
||||
|
||||
# clean checkout for manifest
|
||||
- mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest
|
||||
|
@ -51,9 +53,9 @@ script:
|
|||
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi
|
||||
- pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
|
||||
# Sphinx
|
||||
- make install
|
||||
- pushd docs; make html; make linkcheck; popd
|
||||
|
||||
# Docs
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi
|
||||
|
||||
after_success:
|
||||
# gather the coverage data
|
||||
|
|
47
CHANGES.rst
47
CHANGES.rst
|
@ -4,6 +4,51 @@ Changelog (Pillow)
|
|||
3.3.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Jpeg qtables are unsigned chars #1814
|
||||
[thebostik]
|
||||
|
||||
- Added additional EXIF tags #1841, TIFF Tags #1821
|
||||
[radarhere]
|
||||
|
||||
- Changed documentation to refer to ImageSequence Iterator #1833
|
||||
[radarhere]
|
||||
|
||||
- Fix Fedora prerequisites in installation docs, depends script #1842
|
||||
[living180]
|
||||
|
||||
- Added _accept hook for PixarImagePlugin #1843
|
||||
[radarhere]
|
||||
|
||||
- Removed outdated scanner classifier #1823
|
||||
[radarhere]
|
||||
|
||||
- Combined identical error messages in _imaging #1825
|
||||
[radarhere]
|
||||
|
||||
- Added debug option for setup.py to trace header and library finding #1790
|
||||
[wiredfool]
|
||||
|
||||
- Fix doc building on travis #1820, #1844
|
||||
[wiredfool]
|
||||
|
||||
- Fix for DIB/BMP images #1813
|
||||
[wiredfool]
|
||||
|
||||
- Add PixarImagePlugin file extension #1809
|
||||
[radarhere]
|
||||
|
||||
- Catch struct.errors when verifying png files #1805
|
||||
[wiredfool]
|
||||
|
||||
- SpiderImagePlugin: raise an error when seeking in a non-stack file #1794
|
||||
[radarhere, jmichalon]
|
||||
|
||||
- Added Support for 2/4 bpp Tiff Grayscale Images #1789
|
||||
[zwhfly]
|
||||
|
||||
- Removed unused variable from selftest #1788
|
||||
[radarhere]
|
||||
|
||||
- Added warning for as_dict method (deprecated in 3.0.0) #1799
|
||||
[radarhere]
|
||||
|
||||
|
@ -34,7 +79,7 @@ Changelog (Pillow)
|
|||
- Fixed documented name of JPEG property #1783
|
||||
[radarhere]
|
||||
|
||||
- Fixed UnboundLocalErrorwhen loading a corrupt jpeg2k file #1780
|
||||
- Fixed UnboundLocalError when loading a corrupt jpeg2k file #1780
|
||||
[wiredfool]
|
||||
|
||||
- Fixed integer overflow in path.c #1773
|
||||
|
|
6
Makefile
6
Makefile
|
@ -26,6 +26,12 @@ coverage:
|
|||
doc:
|
||||
$(MAKE) -C docs html
|
||||
|
||||
doccheck:
|
||||
$(MAKE) -C docs html
|
||||
# Don't make our test rely on the links in the docs being up every single build.
|
||||
# We don't control them. But do check, and update them to the target of their redirectes.
|
||||
$(MAKE) -C docs linkcheck || true
|
||||
|
||||
docserve:
|
||||
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&
|
||||
|
||||
|
|
|
@ -109,7 +109,12 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
|
||||
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
|
||||
else:
|
||||
for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
|
||||
# 40 byte headers only have the three components in the bitfields masks,
|
||||
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||
# See also https://github.com/python-pillow/Pillow/issues/1293
|
||||
# Note below, None is a key in the SUPPORTED and MASK_MODES structures.
|
||||
file_info['a_mask'] = None
|
||||
for mask in ['r_mask', 'g_mask', 'b_mask']:
|
||||
file_info[mask] = i32(read(4))
|
||||
file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
|
||||
file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
|
||||
|
@ -130,12 +135,22 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
# ----------------- Process BMP with Bitfields compression (not palette)
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
SUPPORTED = {
|
||||
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
|
||||
32: [(0xff0000, 0xff00, 0xff, 0x0),
|
||||
(0xff0000, 0xff00, 0xff, None),
|
||||
(0xff0000, 0xff00, 0xff, 0xff000000),
|
||||
(0x0, 0x0, 0x0, 0x0)],
|
||||
24: [(0xff0000, 0xff00, 0xff)],
|
||||
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
|
||||
}
|
||||
# From inspecting DIB files from Vista screenshots, the
|
||||
# file format is a 32 bit color image, where the 'Alpha'
|
||||
# channel for screenshots is actually a transparency mask
|
||||
# (255-alpha) where 00 is solid and ff is transparent. I
|
||||
# can't find documentation of this, but it matches
|
||||
# imagemagick's conversion of the .dib file into a .png.
|
||||
MASK_MODES = {
|
||||
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
||||
(32, (0xff0000, 0xff00, 0xff, None)): "BGRT",
|
||||
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
||||
|
@ -145,7 +160,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
if file_info['bits'] in SUPPORTED:
|
||||
if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
|
||||
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
|
||||
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
|
||||
self.mode = "RGBA" if raw_mode in ("BGRA", "BGRT") else self.mode
|
||||
elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
|
||||
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
|
||||
else:
|
||||
|
|
126
PIL/ExifTags.py
126
PIL/ExifTags.py
|
@ -20,6 +20,7 @@
|
|||
TAGS = {
|
||||
|
||||
# possibly incomplete
|
||||
0x000b: "ProcessingSoftware",
|
||||
0x00fe: "NewSubfileType",
|
||||
0x00ff: "SubfileType",
|
||||
0x0100: "ImageWidth",
|
||||
|
@ -32,7 +33,6 @@ TAGS = {
|
|||
0x0109: "CellLength",
|
||||
0x010a: "FillOrder",
|
||||
0x010d: "DocumentName",
|
||||
0x011d: "PageName",
|
||||
0x010e: "ImageDescription",
|
||||
0x010f: "Make",
|
||||
0x0110: "Model",
|
||||
|
@ -46,35 +46,74 @@ TAGS = {
|
|||
0x011a: "XResolution",
|
||||
0x011b: "YResolution",
|
||||
0x011c: "PlanarConfiguration",
|
||||
0x011d: "PageName",
|
||||
0x0120: "FreeOffsets",
|
||||
0x0121: "FreeByteCounts",
|
||||
0x0122: "GrayResponseUnit",
|
||||
0x0123: "GrayResponseCurve",
|
||||
0x0124: "T4Options",
|
||||
0x0125: "T6Options",
|
||||
0x0128: "ResolutionUnit",
|
||||
0x0129: "PageNumber",
|
||||
0x012d: "TransferFunction",
|
||||
0x0131: "Software",
|
||||
0x0132: "DateTime",
|
||||
0x013b: "Artist",
|
||||
0x013c: "HostComputer",
|
||||
0x013d: "Predictor",
|
||||
0x013e: "WhitePoint",
|
||||
0x013f: "PrimaryChromaticities",
|
||||
0x0140: "ColorMap",
|
||||
0x0141: "HalftoneHints",
|
||||
0x0142: "TileWidth",
|
||||
0x0143: "TileLength",
|
||||
0x0144: "TileOffsets",
|
||||
0x0145: "TileByteCounts",
|
||||
0x014a: "SubIFDs",
|
||||
0x014c: "InkSet",
|
||||
0x014d: "InkNames",
|
||||
0x014e: "NumberOfInks",
|
||||
0x0150: "DotRange",
|
||||
0x0151: "TargetPrinter",
|
||||
0x0152: "ExtraSamples",
|
||||
0x0153: "SampleFormat",
|
||||
0x0154: "SMinSampleValue",
|
||||
0x0155: "SMaxSampleValue",
|
||||
0x0156: "TransferRange",
|
||||
0x0157: "ClipPath",
|
||||
0x0158: "XClipPathUnits",
|
||||
0x0159: "YClipPathUnits",
|
||||
0x015a: "Indexed",
|
||||
0x015b: "JPEGTables",
|
||||
0x015f: "OPIProxy",
|
||||
0x0200: "JPEGProc",
|
||||
0x0201: "JpegIFOffset",
|
||||
0x0202: "JpegIFByteCount",
|
||||
0x0203: "JpegRestartInterval",
|
||||
0x0205: "JpegLosslessPredictors",
|
||||
0x0206: "JpegPointTransforms",
|
||||
0x0207: "JpegQTables",
|
||||
0x0208: "JpegDCTables",
|
||||
0x0209: "JpegACTables",
|
||||
0x0211: "YCbCrCoefficients",
|
||||
0x0212: "YCbCrSubSampling",
|
||||
0x0213: "YCbCrPositioning",
|
||||
0x0214: "ReferenceBlackWhite",
|
||||
0x02bc: "XMLPacket",
|
||||
0x1000: "RelatedImageFileFormat",
|
||||
0x1001: "RelatedImageWidth",
|
||||
0x1002: "RelatedImageLength",
|
||||
0x4746: "Rating",
|
||||
0x4749: "RatingPercent",
|
||||
0x800d: "ImageID",
|
||||
0x828d: "CFARepeatPatternDim",
|
||||
0x828e: "CFAPattern",
|
||||
0x828f: "BatteryLevel",
|
||||
0x8298: "Copyright",
|
||||
0x829a: "ExposureTime",
|
||||
0x829d: "FNumber",
|
||||
0x83bb: "IPTCNAA",
|
||||
0x8649: "ImageResources",
|
||||
0x8769: "ExifOffset",
|
||||
0x8773: "InterColorProfile",
|
||||
0x8822: "ExposureProgram",
|
||||
|
@ -114,6 +153,11 @@ TAGS = {
|
|||
0x9290: "SubsecTime",
|
||||
0x9291: "SubsecTimeOriginal",
|
||||
0x9292: "SubsecTimeDigitized",
|
||||
0x9c9b: "XPTitle",
|
||||
0x9c9c: "XPComment",
|
||||
0x9c9d: "XPAuthor",
|
||||
0x9c9e: "XPKeywords",
|
||||
0x9c9f: "XPSubject",
|
||||
0xa000: "FlashPixVersion",
|
||||
0xa001: "ColorSpace",
|
||||
0xa002: "ExifImageWidth",
|
||||
|
@ -151,7 +195,85 @@ TAGS = {
|
|||
0xa434: "LensModel",
|
||||
0xa435: "LensSerialNumber",
|
||||
0xa500: "Gamma",
|
||||
|
||||
0xc4a5: "PrintImageMatching",
|
||||
0xc612: "DNGVersion",
|
||||
0xc613: "DNGBackwardVersion",
|
||||
0xc614: "UniqueCameraModel",
|
||||
0xc615: "LocalizedCameraModel",
|
||||
0xc616: "CFAPlaneColor",
|
||||
0xc617: "CFALayout",
|
||||
0xc618: "LinearizationTable",
|
||||
0xc619: "BlackLevelRepeatDim",
|
||||
0xc61a: "BlackLevel",
|
||||
0xc61b: "BlackLevelDeltaH",
|
||||
0xc61c: "BlackLevelDeltaV",
|
||||
0xc61d: "WhiteLevel",
|
||||
0xc61e: "DefaultScale",
|
||||
0xc61f: "DefaultCropOrigin",
|
||||
0xc620: "DefaultCropSize",
|
||||
0xc621: "ColorMatrix1",
|
||||
0xc622: "ColorMatrix2",
|
||||
0xc623: "CameraCalibration1",
|
||||
0xc624: "CameraCalibration2",
|
||||
0xc625: "ReductionMatrix1",
|
||||
0xc626: "ReductionMatrix2",
|
||||
0xc627: "AnalogBalance",
|
||||
0xc628: "AsShotNeutral",
|
||||
0xc629: "AsShotWhiteXY",
|
||||
0xc62a: "BaselineExposure",
|
||||
0xc62b: "BaselineNoise",
|
||||
0xc62c: "BaselineSharpness",
|
||||
0xc62d: "BayerGreenSplit",
|
||||
0xc62e: "LinearResponseLimit",
|
||||
0xc62f: "CameraSerialNumber",
|
||||
0xc630: "LensInfo",
|
||||
0xc631: "ChromaBlurRadius",
|
||||
0xc632: "AntiAliasStrength",
|
||||
0xc633: "ShadowScale",
|
||||
0xc634: "DNGPrivateData",
|
||||
0xc635: "MakerNoteSafety",
|
||||
0xc65a: "CalibrationIlluminant1",
|
||||
0xc65b: "CalibrationIlluminant2",
|
||||
0xc65c: "BestQualityScale",
|
||||
0xc65d: "RawDataUniqueID",
|
||||
0xc68b: "OriginalRawFileName",
|
||||
0xc68c: "OriginalRawFileData",
|
||||
0xc68d: "ActiveArea",
|
||||
0xc68e: "MaskedAreas",
|
||||
0xc68f: "AsShotICCProfile",
|
||||
0xc690: "AsShotPreProfileMatrix",
|
||||
0xc691: "CurrentICCProfile",
|
||||
0xc692: "CurrentPreProfileMatrix",
|
||||
0xc6bf: "ColorimetricReference",
|
||||
0xc6f3: "CameraCalibrationSignature",
|
||||
0xc6f4: "ProfileCalibrationSignature",
|
||||
0xc6f6: "AsShotProfileName",
|
||||
0xc6f7: "NoiseReductionApplied",
|
||||
0xc6f8: "ProfileName",
|
||||
0xc6f9: "ProfileHueSatMapDims",
|
||||
0xc6fa: "ProfileHueSatMapData1",
|
||||
0xc6fb: "ProfileHueSatMapData2",
|
||||
0xc6fc: "ProfileToneCurve",
|
||||
0xc6fd: "ProfileEmbedPolicy",
|
||||
0xc6fe: "ProfileCopyright",
|
||||
0xc714: "ForwardMatrix1",
|
||||
0xc715: "ForwardMatrix2",
|
||||
0xc716: "PreviewApplicationName",
|
||||
0xc717: "PreviewApplicationVersion",
|
||||
0xc718: "PreviewSettingsName",
|
||||
0xc719: "PreviewSettingsDigest",
|
||||
0xc71a: "PreviewColorSpace",
|
||||
0xc71b: "PreviewDateTime",
|
||||
0xc71c: "RawImageDigest",
|
||||
0xc71d: "OriginalRawFileDigest",
|
||||
0xc71e: "SubTileBlockSize",
|
||||
0xc71f: "RowInterleaveFactor",
|
||||
0xc725: "ProfileLookTableDims",
|
||||
0xc726: "ProfileLookTableData",
|
||||
0xc740: "OpcodeList1",
|
||||
0xc741: "OpcodeList2",
|
||||
0xc74e: "OpcodeList3",
|
||||
0xc761: "NoiseProfile"
|
||||
}
|
||||
|
||||
##
|
||||
|
|
|
@ -55,7 +55,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
if width <= 0 or height <= 0:
|
||||
raise SyntaxError("not a GIMP brush")
|
||||
if color_depth not in (1, 4):
|
||||
raise SyntaxError("Unsupported GMP brush color depth: %s" % color_depth)
|
||||
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
|
||||
|
||||
if version == 1:
|
||||
comment_length = header_size-20
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# The Python Imaging Library.
|
||||
# $Id$
|
||||
|
||||
# Optional color managment support, based on Kevin Cazabon's PyCMS
|
||||
# Optional color management support, based on Kevin Cazabon's PyCMS
|
||||
# library.
|
||||
|
||||
# History:
|
||||
|
|
|
@ -640,7 +640,7 @@ def _save(im, fp, filename):
|
|||
try:
|
||||
if len(table) != 64:
|
||||
raise
|
||||
table = array.array('b', table)
|
||||
table = array.array('B', table)
|
||||
except TypeError:
|
||||
raise ValueError("Invalid quantization table")
|
||||
else:
|
||||
|
|
|
@ -540,16 +540,16 @@ class OleMetadata(object):
|
|||
OLE file.
|
||||
|
||||
References for SummaryInformation stream:
|
||||
- http://msdn.microsoft.com/en-us/library/dd942545.aspx
|
||||
- http://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx
|
||||
- http://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx
|
||||
- http://msdn.microsoft.com/en-us/library/aa372045.aspx
|
||||
- http://sedna-soft.de/summary-information-stream/
|
||||
- https://msdn.microsoft.com/en-us/library/dd942545.aspx
|
||||
- https://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx
|
||||
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx
|
||||
- https://msdn.microsoft.com/en-us/library/aa372045.aspx
|
||||
- http://sedna-soft.de/articles/summary-information-stream/
|
||||
- http://poi.apache.org/apidocs/org/apache/poi/hpsf/SummaryInformation.html
|
||||
|
||||
References for DocumentSummaryInformation stream:
|
||||
- http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx
|
||||
- http://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx
|
||||
- https://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx
|
||||
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx
|
||||
- http://poi.apache.org/apidocs/org/apache/poi/hpsf/DocumentSummaryInformation.html
|
||||
|
||||
new in version 0.25
|
||||
|
|
|
@ -29,6 +29,10 @@ __version__ = "0.1"
|
|||
i16 = _binary.i16le
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:4] == b"\200\350\000\000"
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for PIXAR raster images.
|
||||
|
||||
|
@ -39,7 +43,7 @@ class PixarImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self):
|
||||
|
||||
# assuming a 4-byte magic label (FIXME: add "_accept" hook)
|
||||
# assuming a 4-byte magic label
|
||||
s = self.fp.read(4)
|
||||
if s != b"\200\350\000\000":
|
||||
raise SyntaxError("not a PIXAR file")
|
||||
|
@ -62,7 +66,6 @@ class PixarImageFile(ImageFile.ImageFile):
|
|||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
Image.register_open(PixarImageFile.format, PixarImageFile)
|
||||
Image.register_open(PixarImageFile.format, PixarImageFile, _accept)
|
||||
|
||||
#
|
||||
# FIXME: what's the standard extension?
|
||||
Image.register_extension(PixarImageFile.format, ".pxr")
|
||||
|
|
|
@ -36,6 +36,7 @@ from __future__ import print_function
|
|||
import logging
|
||||
import re
|
||||
import zlib
|
||||
import struct
|
||||
|
||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
||||
|
||||
|
@ -106,6 +107,7 @@ class ChunkStream(object):
|
|||
|
||||
def read(self):
|
||||
"Fetch a new chunk. Returns header information."
|
||||
cid = None
|
||||
|
||||
if self.queue:
|
||||
cid, pos, length = self.queue[-1]
|
||||
|
@ -116,7 +118,7 @@ class ChunkStream(object):
|
|||
cid = s[4:]
|
||||
pos = self.fp.tell()
|
||||
length = i32(s)
|
||||
|
||||
|
||||
if not is_cid(cid):
|
||||
raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
|
||||
|
||||
|
@ -138,11 +140,15 @@ class ChunkStream(object):
|
|||
def crc(self, cid, data):
|
||||
"Read and verify checksum"
|
||||
|
||||
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
|
||||
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
|
||||
if crc1 != crc2:
|
||||
raise SyntaxError("broken PNG file"
|
||||
"(bad header checksum in %s)" % cid)
|
||||
try:
|
||||
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
|
||||
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
|
||||
if crc1 != crc2:
|
||||
raise SyntaxError("broken PNG file (bad header checksum in %s)"
|
||||
% cid)
|
||||
except struct.error:
|
||||
raise SyntaxError("broken PNG file (incomplete checksum in %s)"
|
||||
% cid)
|
||||
|
||||
def crc_skip(self, cid, data):
|
||||
"Read checksum. Used if the C module is not present"
|
||||
|
@ -157,7 +163,11 @@ class ChunkStream(object):
|
|||
cids = []
|
||||
|
||||
while True:
|
||||
cid, pos, length = self.read()
|
||||
try:
|
||||
cid, pos, length = self.read()
|
||||
except struct.error:
|
||||
raise IOError("truncated PNG file")
|
||||
|
||||
if cid == endchunk:
|
||||
break
|
||||
self.crc(cid, ImageFile._safe_read(self.fp, length))
|
||||
|
|
|
@ -173,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
def seek(self, frame):
|
||||
if self.istack == 0:
|
||||
return
|
||||
raise EOFError("attempt to seek in a non-stack file")
|
||||
if frame >= self._nimages:
|
||||
raise EOFError("attempt to seek past end of file")
|
||||
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
|
||||
|
|
|
@ -141,34 +141,56 @@ OPEN_INFO = {
|
|||
(MM, 0, (1,), 1, (1,), ()): ("1", "1;I"),
|
||||
(II, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
|
||||
(MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
|
||||
(II, 1, (1,), 1, (1,), ()): ("1", "1"),
|
||||
(MM, 1, (1,), 1, (1,), ()): ("1", "1"),
|
||||
(II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
|
||||
(MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
|
||||
|
||||
(II, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
|
||||
(MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
|
||||
(II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
|
||||
(MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
|
||||
(II, 1, (1,), 1, (2,), ()): ("L", "L;2"),
|
||||
(MM, 1, (1,), 1, (2,), ()): ("L", "L;2"),
|
||||
(II, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
|
||||
(MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
|
||||
|
||||
(II, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
|
||||
(MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
|
||||
(II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
|
||||
(MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
|
||||
(II, 1, (1,), 1, (4,), ()): ("L", "L;4"),
|
||||
(MM, 1, (1,), 1, (4,), ()): ("L", "L;4"),
|
||||
(II, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
|
||||
(MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
|
||||
|
||||
(II, 0, (1,), 1, (8,), ()): ("L", "L;I"),
|
||||
(MM, 0, (1,), 1, (8,), ()): ("L", "L;I"),
|
||||
(II, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
|
||||
(MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
|
||||
(II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
|
||||
(MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
|
||||
(II, 1, (1,), 1, (1,), ()): ("1", "1"),
|
||||
(MM, 1, (1,), 1, (1,), ()): ("1", "1"),
|
||||
(II, 1, (1,), 1, (4,), ()): ("L", "L;4"),
|
||||
# ?
|
||||
(II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
|
||||
(MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
|
||||
(II, 1, (1,), 1, (8,), ()): ("L", "L"),
|
||||
(MM, 1, (1,), 1, (8,), ()): ("L", "L"),
|
||||
(II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
|
||||
(MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
|
||||
(II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
||||
(MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
||||
|
||||
(II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
|
||||
|
||||
(II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
|
||||
(MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
|
||||
(II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"),
|
||||
(MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"),
|
||||
|
||||
(II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
|
||||
(MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
|
||||
(II, 1, (1,), 1, (32,), ()): ("I", "I;32N"),
|
||||
(II, 1, (2,), 1, (32,), ()): ("I", "I;32S"),
|
||||
(MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"),
|
||||
(II, 1, (3,), 1, (32,), ()): ("F", "F;32F"),
|
||||
(MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"),
|
||||
|
||||
(II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
|
||||
(MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
|
||||
|
||||
(II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
|
||||
(MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
|
||||
(II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"),
|
||||
|
@ -183,6 +205,7 @@ OPEN_INFO = {
|
|||
(MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
|
||||
(II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
|
||||
(MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
|
||||
|
||||
(II, 3, (1,), 1, (1,), ()): ("P", "P;1"),
|
||||
(MM, 3, (1,), 1, (1,), ()): ("P", "P;1"),
|
||||
(II, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
|
||||
|
@ -201,10 +224,13 @@ OPEN_INFO = {
|
|||
(MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
|
||||
(II, 3, (1,), 2, (8,), ()): ("P", "P;R"),
|
||||
(MM, 3, (1,), 2, (8,), ()): ("P", "P;R"),
|
||||
|
||||
(II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
|
||||
(MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
|
||||
|
||||
(II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
|
||||
(MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
|
||||
|
||||
(II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
||||
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
||||
}
|
||||
|
|
|
@ -188,10 +188,28 @@ TAGS = {347: 'JPEGTables',
|
|||
700: 'XMP',
|
||||
|
||||
# Additional Exif Info
|
||||
32932: 'Wang Annotation',
|
||||
33434: 'ExposureTime',
|
||||
33437: 'FNumber',
|
||||
33445: 'MD FileTag',
|
||||
33446: 'MD ScalePixel',
|
||||
33447: 'MD ColorTable',
|
||||
33448: 'MD LabName',
|
||||
33449: 'MD SampleInfo',
|
||||
33450: 'MD PrepDate',
|
||||
33451: 'MD PrepTime',
|
||||
33452: 'MD FileUnits',
|
||||
33550: 'ModelPixelScaleTag',
|
||||
33723: 'IptcNaaInfo',
|
||||
33918: 'INGR Packet Data Tag',
|
||||
33919: 'INGR Flag Registers',
|
||||
33920: 'IrasB Transformation Matrix',
|
||||
33922: 'ModelTiepointTag',
|
||||
34264: 'ModelTransformationTag',
|
||||
34377: 'PhotoshopInfo',
|
||||
34735: 'GeoKeyDirectoryTag',
|
||||
34736: 'GeoDoubleParamsTag',
|
||||
34737: 'GeoAsciiParamsTag',
|
||||
34850: 'ExposureProgram',
|
||||
34852: 'SpectralSensitivity',
|
||||
34855: 'ISOSpeedRatings',
|
||||
|
@ -202,11 +220,15 @@ TAGS = {347: 'JPEGTables',
|
|||
34867: 'ISOSpeed',
|
||||
34868: 'ISOSpeedLatitudeyyy',
|
||||
34869: 'ISOSpeedLatitudezzz',
|
||||
34908: 'HylaFAX FaxRecvParams',
|
||||
34909: 'HylaFAX FaxSubAddress',
|
||||
34910: 'HylaFAX FaxRecvTime',
|
||||
36864: 'ExifVersion',
|
||||
36867: 'DateTimeOriginal',
|
||||
36868: 'DateTImeDigitized',
|
||||
37121: 'ComponentsConfiguration',
|
||||
37122: 'CompressedBitsPerPixel',
|
||||
37724: 'ImageSourceData',
|
||||
37377: 'ShutterSpeedValue',
|
||||
37378: 'ApertureValue',
|
||||
37379: 'BrightnessValue',
|
||||
|
@ -259,7 +281,13 @@ TAGS = {347: 'JPEGTables',
|
|||
42035: 'LensMake',
|
||||
42036: 'LensModel',
|
||||
42037: 'LensSerialNumber',
|
||||
42112: 'GDAL_METADATA',
|
||||
42113: 'GDAL_NODATA',
|
||||
42240: 'Gamma',
|
||||
50215: 'Oce Scanjob Description',
|
||||
50216: 'Oce Application Selector',
|
||||
50217: 'Oce Identification Number',
|
||||
50218: 'Oce ImageLogic Characteristics',
|
||||
|
||||
# Adobe DNG
|
||||
50706: 'DNGVersion',
|
||||
|
@ -298,6 +326,7 @@ TAGS = {347: 'JPEGTables',
|
|||
50740: 'DNGPrivateData',
|
||||
50778: 'CalibrationIlluminant1',
|
||||
50779: 'CalibrationIlluminant2',
|
||||
50784: 'Alias Layer Metadata'
|
||||
}
|
||||
|
||||
|
||||
|
|
49
README.rst
49
README.rst
|
@ -6,41 +6,56 @@ Python Imaging Library (Fork)
|
|||
|
||||
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
|
||||
|
||||
.. image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
|
||||
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
|
||||
.. start-badges
|
||||
|
||||
.. image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
||||
.. list-table::
|
||||
:stub-columns: 1
|
||||
|
||||
* - docs
|
||||
- |docs|
|
||||
* - tests
|
||||
- | |linux| |osx| |windows| |coverage| |health|
|
||||
* - package
|
||||
- |zenodo| |version| |downloads|
|
||||
|
||||
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
||||
:target: http://pillow.readthedocs.org/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
|
||||
.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
|
||||
:target: https://travis-ci.org/python-pillow/Pillow
|
||||
:alt: Travis CI build status (Linux)
|
||||
|
||||
.. image:: https://img.shields.io/travis/python-pillow/pillow-wheels/latest.svg?label=OS%20X%20build
|
||||
.. |osx| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/latest.svg?label=OS%20X%20build
|
||||
:target: https://travis-ci.org/python-pillow/pillow-wheels
|
||||
:alt: Travis CI build status (OS X)
|
||||
|
||||
.. image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
|
||||
.. |windows| image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
|
||||
:target: https://ci.appveyor.com/project/python-pillow/Pillow
|
||||
:alt: AppVeyor CI build status (Windows)
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/pillow.svg
|
||||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Latest PyPI version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/pillow.svg
|
||||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Number of PyPI downloads
|
||||
|
||||
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
|
||||
.. |coverage| image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
|
||||
:target: https://coveralls.io/github/python-pillow/Pillow?branch=master
|
||||
:alt: Code coverage
|
||||
|
||||
.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg
|
||||
.. |health| image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg
|
||||
:target: https://landscape.io/github/python-pillow/Pillow/master
|
||||
:alt: Code health
|
||||
|
||||
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
|
||||
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
|
||||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Latest PyPI version
|
||||
|
||||
.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg
|
||||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Number of PyPI downloads
|
||||
|
||||
.. end-badges
|
||||
|
||||
|
||||
|
||||
More Information
|
||||
----------------
|
||||
|
@ -50,7 +65,7 @@ More Information
|
|||
- `Installation <https://pillow.readthedocs.org/en/latest/installation.html>`_
|
||||
- `Handbook <https://pillow.readthedocs.org/en/latest/handbook/index.html>`_
|
||||
|
||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/CONTRIBUTING.md>`_
|
||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
||||
|
||||
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
||||
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
||||
|
|
BIN
Tests/images/clipboard.dib
Normal file
BIN
Tests/images/clipboard.dib
Normal file
Binary file not shown.
BIN
Tests/images/clipboard_target.png
Normal file
BIN
Tests/images/clipboard_target.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
BIN
Tests/images/hopper_jpg.tif
Normal file
BIN
Tests/images/hopper_jpg.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2I.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2I.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2R.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper2R.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4I.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4I.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif
Normal file
Binary file not shown.
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4R.tif
Normal file
BIN
Tests/images/tiff_gray_2_4_bpp/hopper4R.tif
Normal file
Binary file not shown.
|
@ -24,7 +24,7 @@ class TestBmpReference(PillowTestCase):
|
|||
# print ("Bad Image %s: %s" %(f,msg))
|
||||
|
||||
def test_questionable(self):
|
||||
""" These shouldn't crash/dos, but its not well defined that these
|
||||
""" These shouldn't crash/dos, but it's not well defined that these
|
||||
are in spec """
|
||||
for f in self.get_files('q'):
|
||||
try:
|
||||
|
|
|
@ -70,6 +70,13 @@ class TestFileBmp(PillowTestCase):
|
|||
self.assertEqual(im.size, reloaded.size)
|
||||
self.assertEqual(reloaded.format, "JPEG")
|
||||
|
||||
def test_load_dib(self):
|
||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
||||
im = BmpImagePlugin.DibImageFile('Tests/images/clipboard.dib')
|
||||
target = Image.open('Tests/images/clipboard_target.png')
|
||||
self.assert_image_equal(im.convert('RGB'), target.convert('RGB'))
|
||||
self.assert_image_similar(im, target, 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -7,18 +7,6 @@ TEST_FILE = "Tests/images/iptc.jpg"
|
|||
|
||||
class TestFileIptc(PillowTestCase):
|
||||
|
||||
# Helpers
|
||||
|
||||
def dummy_IptcImagePlugin(self):
|
||||
# Create an IptcImagePlugin object without initializing it
|
||||
class FakeImage(object):
|
||||
pass
|
||||
im = FakeImage()
|
||||
im.__class__ = IptcImagePlugin.IptcImageFile
|
||||
return im
|
||||
|
||||
# Tests
|
||||
|
||||
def test_getiptcinfo_jpg_none(self):
|
||||
# Arrange
|
||||
im = hopper()
|
||||
|
|
|
@ -314,6 +314,10 @@ class TestFileJpeg(PillowTestCase):
|
|||
30)
|
||||
self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)
|
||||
|
||||
# valid bounds for baseline qtable
|
||||
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
|
||||
self.roundtrip(im, qtables=[bounds_qtable])
|
||||
|
||||
# values from wizard.txt in jpeg9-a src package.
|
||||
standard_l_qtable = [int(s) for s in """
|
||||
16 11 10 16 24 40 51 61
|
||||
|
|
|
@ -419,6 +419,39 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
self.assertEqual(im.mode, "L")
|
||||
self.assert_image_similar(im, original, 7.3)
|
||||
|
||||
def test_gray_semibyte_per_pixel(self):
|
||||
test_files = (
|
||||
(
|
||||
24.8,#epsilon
|
||||
(#group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif",
|
||||
)
|
||||
),
|
||||
(
|
||||
7.3,#epsilon
|
||||
(#group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif",
|
||||
)
|
||||
),
|
||||
)
|
||||
original = hopper("L")
|
||||
for epsilon, group in test_files:
|
||||
im = Image.open(group[0])
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.mode, "L")
|
||||
self.assert_image_similar(im, original, epsilon)
|
||||
for file in group[1:]:
|
||||
im2 = Image.open(file)
|
||||
self.assertEqual(im2.size, (128, 128))
|
||||
self.assertEqual(im2.mode, "L")
|
||||
self.assert_image_equal(im, im2)
|
||||
|
||||
def test_save_bytesio(self):
|
||||
# PR 1011
|
||||
# Test TIFF saving to io.BytesIO() object.
|
||||
|
|
|
@ -253,6 +253,22 @@ class TestFilePng(PillowTestCase):
|
|||
im.load()
|
||||
self.assertRaises(RuntimeError, im.verify)
|
||||
|
||||
def test_verify_struct_error(self):
|
||||
# Check open/load/verify exception (#1755)
|
||||
|
||||
# offsets to test, -10: breaks in i32() in read. (IOError)
|
||||
# -13: breaks in crc, txt chunk.
|
||||
# -14: malformed chunk
|
||||
|
||||
for offset in (-10, -13, -14):
|
||||
with open(TEST_PNG_FILE,'rb') as f:
|
||||
test_file = f.read()[:offset]
|
||||
|
||||
im = Image.open(BytesIO(test_file))
|
||||
self.assertTrue(im.fp is not None)
|
||||
self.assertRaises((IOError, SyntaxError), im.verify)
|
||||
|
||||
|
||||
def test_roundtrip_dpi(self):
|
||||
# Check dpi roundtripping
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageSequence
|
||||
from PIL import SpiderImagePlugin
|
||||
|
||||
TEST_FILE = "Tests/images/hopper.spider"
|
||||
|
@ -85,6 +86,17 @@ class TestImageSpider(PillowTestCase):
|
|||
|
||||
self.assertRaises(IOError, lambda: Image.open(invalid_file))
|
||||
|
||||
def test_nonstack_file(self):
|
||||
im = Image.open(TEST_FILE)
|
||||
|
||||
self.assertRaises(EOFError, lambda: im.seek(0))
|
||||
|
||||
def test_nonstack_dos(self):
|
||||
im = Image.open(TEST_FILE)
|
||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||
if i > 1:
|
||||
self.fail("Non-stack DOS file test failed")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -337,6 +337,39 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assertEqual(im.mode, "L")
|
||||
self.assert_image_similar(im, original, 7.3)
|
||||
|
||||
def test_gray_semibyte_per_pixel(self):
|
||||
test_files = (
|
||||
(
|
||||
24.8,#epsilon
|
||||
(#group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif",
|
||||
)
|
||||
),
|
||||
(
|
||||
7.3,#epsilon
|
||||
(#group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif",
|
||||
)
|
||||
),
|
||||
)
|
||||
original = hopper("L")
|
||||
for epsilon, group in test_files:
|
||||
im = Image.open(group[0])
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.mode, "L")
|
||||
self.assert_image_similar(im, original, epsilon)
|
||||
for file in group[1:]:
|
||||
im2 = Image.open(file)
|
||||
self.assertEqual(im2.size, (128, 128))
|
||||
self.assertEqual(im2.mode, "L")
|
||||
self.assert_image_equal(im, im2)
|
||||
|
||||
def test_page_number_x_0(self):
|
||||
# Issue 973
|
||||
# Test TIFF with tag 297 (Page Number) having value of 0 0.
|
||||
|
@ -404,6 +437,19 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assertEqual(im.size, (10, 10))
|
||||
im.load()
|
||||
|
||||
def test_save_tiff_with_jpegtables(self):
|
||||
# Arrange
|
||||
outfile = self.tempfile("temp.tif")
|
||||
|
||||
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
|
||||
# Contains JPEGTables (347) tag
|
||||
infile = "Tests/images/hopper_jpg.tif"
|
||||
im = Image.open(infile)
|
||||
|
||||
# Act / Assert
|
||||
# Should not raise UnicodeDecodeError or anything else
|
||||
im.save(outfile)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
22
_imaging.c
22
_imaging.c
|
@ -280,6 +280,8 @@ int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view)
|
|||
|
||||
/* error messages */
|
||||
static const char* must_be_sequence = "argument must be a sequence";
|
||||
static const char* must_be_two_coordinates =
|
||||
"coordinate list must contain exactly 2 coordinates";
|
||||
static const char* wrong_mode = "unrecognized image mode";
|
||||
static const char* wrong_raw_mode = "unrecognized raw mode";
|
||||
static const char* outside_image = "image index out of range";
|
||||
|
@ -2398,9 +2400,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
|
|||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2472,9 +2472,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
|
|||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2509,9 +2507,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
|
|||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2692,9 +2688,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
|
|||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2774,9 +2768,7 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
|
|||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
15
appveyor.yml
15
appveyor.yml
|
@ -21,6 +21,7 @@ install:
|
|||
build_script:
|
||||
- '%PYTHON%\python.exe c:\pillow\winbuild\build.py'
|
||||
- cd c:\pillow
|
||||
- dir dist\*.egg
|
||||
- '%PYTHON%\python.exe selftest.py --installed'
|
||||
test_script:
|
||||
- cd c:\pillow
|
||||
|
@ -28,3 +29,17 @@ test_script:
|
|||
- '%PYTHON%\python.exe test-installed.py'
|
||||
matrix:
|
||||
fast_finish: true
|
||||
artifacts:
|
||||
- path: pillow\dist\*.egg
|
||||
name: build
|
||||
after_test:
|
||||
- ps: Get-ChildItem .\dist\*.egg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
deploy:
|
||||
provider: S3
|
||||
access_key_id: AKIAIRAXC62ZNTVQJMOQ
|
||||
secret_access_key:
|
||||
secure: Hwb6klTqtBeMgxAjRoDltiiqpuH8xbwD4UooDzBSiCWXjuFj1lyl4kHgHwTCCGqi
|
||||
bucket: pillow-nightly
|
||||
folder: win/$(APPVEYOR_BUILD_NUMBER)/
|
||||
artifact: /.*egg/
|
||||
|
||||
|
|
|
@ -13,6 +13,6 @@ sudo dnf install redhat-rpm-config
|
|||
|
||||
sudo dnf install python-devel python3-devel python-virtualenv make gcc
|
||||
|
||||
sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
|
||||
sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \
|
||||
lcms2-devel libwebp-devel openjpeg2-devel tkinter python3-tkinter \
|
||||
tcl-devel tk-devel
|
|
@ -24,7 +24,7 @@ To get the number and names of bands in an image, use the
|
|||
Modes
|
||||
-----
|
||||
|
||||
The :term:`mode` of an image defines the type and depth of a pixel in the
|
||||
The ``mode`` of an image defines the type and depth of a pixel in the
|
||||
image. The current release supports the following standard modes:
|
||||
|
||||
* ``1`` (1-bit pixels, black and white, stored with one pixel per byte)
|
||||
|
|
|
@ -400,26 +400,15 @@ Note that most drivers in the current version of the library only allow you to
|
|||
seek to the next frame (as in the above example). To rewind the file, you may
|
||||
have to reopen it.
|
||||
|
||||
The following iterator class lets you use the for-statement to loop over the
|
||||
sequence:
|
||||
The following class lets you use the for-statement to loop over the sequence:
|
||||
|
||||
A sequence iterator class
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Using the ImageSequence Iterator class
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
class ImageSequence:
|
||||
def __init__(self, im):
|
||||
self.im = im
|
||||
def __getitem__(self, ix):
|
||||
try:
|
||||
if ix:
|
||||
self.im.seek(ix)
|
||||
return self.im
|
||||
except EOFError:
|
||||
raise IndexError # end of sequence
|
||||
|
||||
for frame in ImageSequence(im):
|
||||
from PIL import ImageSequence
|
||||
for frame in ImageSequence.Iterator(im):
|
||||
# ...do something to frame...
|
||||
|
||||
|
||||
|
|
|
@ -202,6 +202,11 @@ Build Options
|
|||
the libraries are not found. Webpmux (WebP metadata) relies on WebP
|
||||
support. Tcl and Tk also must be used together.
|
||||
|
||||
* Build flags: ``--debug``. Adds a debugging flag to the include and
|
||||
library search process to dump all paths searched for and found to
|
||||
stdout.
|
||||
|
||||
|
||||
Sample Usage::
|
||||
|
||||
$ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install
|
||||
|
@ -288,7 +293,7 @@ Prerequisites are installed on **Ubuntu 14.04 LTS** with::
|
|||
|
||||
Prerequisites are installed on **Fedora 23** with::
|
||||
|
||||
$ sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
|
||||
$ sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \
|
||||
lcms2-devel libwebp-devel tcl-devel tk-devel
|
||||
|
||||
|
||||
|
|
|
@ -81,13 +81,13 @@ Bitwise operators don’t work on floating point images.
|
|||
Logical Operators
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Logical operators like :keyword:`and`, :keyword:`or`, and :keyword:`not` work
|
||||
Logical operators like ``and``, ``or``, and ``not`` work
|
||||
on entire images, rather than individual pixels.
|
||||
|
||||
An empty image (all pixels zero) is treated as false. All other images are
|
||||
treated as true.
|
||||
|
||||
Note that :keyword:`and` and :keyword:`or` return the last evaluated operand,
|
||||
Note that ``and`` and ``or`` return the last evaluated operand,
|
||||
while not always returns a boolean value.
|
||||
|
||||
Built-in Functions
|
||||
|
|
|
@ -48,7 +48,7 @@ off the end of the memory buffer, causing a segfault.
|
|||
This issue was found by Alyssa Besseling at Atlassian
|
||||
|
||||
CVE-2016-2533 -- Buffer overflow in PcdDecode.c
|
||||
----------------------------------------------
|
||||
-----------------------------------------------
|
||||
|
||||
In all versions of Pillow, dating back at least to the last PIL 1.1.7
|
||||
release, ``PcdDecode.c`` has a buffer overflow error.
|
||||
|
@ -64,7 +64,7 @@ Integer overflow in Resample.c
|
|||
|
||||
If a large value was passed into the new size for an image, it is
|
||||
possible to overflow an int32 value passed into malloc.
|
||||
|
||||
|
||||
kk = malloc(xsize * kmax * sizeof(float));
|
||||
...
|
||||
xbounds = malloc(xsize * 2 * sizeof(int));
|
||||
|
|
|
@ -191,14 +191,64 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels)
|
|||
static void
|
||||
unpackL2(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles */
|
||||
/* nibbles (msb first, white is non-zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
switch (pixels) {
|
||||
default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
|
||||
case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
|
||||
case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
|
||||
case 1: *out++ = ((byte >> 6) & 3) * 255 / 3;
|
||||
default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U;
|
||||
}
|
||||
pixels -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL2I(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (msb first, white is zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
switch (pixels) {
|
||||
default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U);
|
||||
}
|
||||
pixels -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL2R(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (bit order reversed, white is non-zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
byte = BITFLIP[byte];
|
||||
switch (pixels) {
|
||||
default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
|
||||
case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U;
|
||||
}
|
||||
pixels -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL2IR(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (bit order reversed, white is zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
byte = BITFLIP[byte];
|
||||
switch (pixels) {
|
||||
default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
|
||||
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U);
|
||||
}
|
||||
pixels -= 4;
|
||||
}
|
||||
|
@ -207,12 +257,56 @@ unpackL2(UINT8* out, const UINT8* in, int pixels)
|
|||
static void
|
||||
unpackL4(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles */
|
||||
/* nibbles (msb first, white is non-zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
switch (pixels) {
|
||||
default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4;
|
||||
case 1: *out++ = ((byte >> 4) & 15) * 255 / 15;
|
||||
default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4;
|
||||
case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U;
|
||||
}
|
||||
pixels -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL4I(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (msb first, white is zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
switch (pixels) {
|
||||
default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4;
|
||||
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U);
|
||||
}
|
||||
pixels -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL4R(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (bit order reversed, white is non-zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
byte = BITFLIP[byte];
|
||||
switch (pixels) {
|
||||
default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4;
|
||||
case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U;
|
||||
}
|
||||
pixels -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackL4IR(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* nibbles (bit order reversed, white is zero) */
|
||||
while (pixels > 0) {
|
||||
UINT8 byte = *in++;
|
||||
byte = BITFLIP[byte];
|
||||
switch (pixels) {
|
||||
default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4;
|
||||
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U);
|
||||
}
|
||||
pixels -= 2;
|
||||
}
|
||||
|
@ -738,6 +832,21 @@ unpackBGRA(UINT8* out, const UINT8* in, int pixels)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackBGRT(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
int i;
|
||||
/* RGBA, reversed bytes */
|
||||
/* Transparency map, rather than an alpha channel */
|
||||
for (i = 0; i < pixels; i++) {
|
||||
out[R] = in[2];
|
||||
out[G] = in[1];
|
||||
out[B] = in[0];
|
||||
out[A] = 255 - in[3];
|
||||
out += 4; in += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Unpack to "CMYK" image */
|
||||
|
||||
|
@ -1053,7 +1162,15 @@ static struct {
|
|||
|
||||
/* greyscale */
|
||||
{"L", "L;2", 2, unpackL2},
|
||||
{"L", "L;2I", 2, unpackL2I},
|
||||
{"L", "L;2R", 2, unpackL2R},
|
||||
{"L", "L;2IR", 2, unpackL2IR},
|
||||
|
||||
{"L", "L;4", 4, unpackL4},
|
||||
{"L", "L;4I", 4, unpackL4I},
|
||||
{"L", "L;4R", 4, unpackL4R},
|
||||
{"L", "L;4IR", 4, unpackL4IR},
|
||||
|
||||
{"L", "L", 8, copy1},
|
||||
{"L", "L;I", 8, unpackLI},
|
||||
{"L", "L;R", 8, unpackLR},
|
||||
|
@ -1111,6 +1228,7 @@ static struct {
|
|||
{"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B},
|
||||
{"RGBA", "RGBA;16B", 64, unpackRGBA16B},
|
||||
{"RGBA", "BGRA", 32, unpackBGRA},
|
||||
{"RGBA", "BGRT", 32, unpackBGRT},
|
||||
{"RGBA", "ARGB", 32, unpackARGB},
|
||||
{"RGBA", "ABGR", 32, unpackABGR},
|
||||
{"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
|
||||
|
|
152
setup.py
152
setup.py
|
@ -37,21 +37,35 @@ _LIB_IMAGING = (
|
|||
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
|
||||
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur")
|
||||
|
||||
DEBUG = False
|
||||
|
||||
def _add_directory(path, dir, where=None):
|
||||
if dir is None:
|
||||
|
||||
def _dbg(s, tp=None):
|
||||
if DEBUG:
|
||||
if tp:
|
||||
print(s % tp)
|
||||
return
|
||||
print(s)
|
||||
|
||||
|
||||
def _add_directory(path, subdir, where=None):
|
||||
if subdir is None:
|
||||
return
|
||||
dir = os.path.realpath(dir)
|
||||
if os.path.isdir(dir) and dir not in path:
|
||||
subdir = os.path.realpath(subdir)
|
||||
if os.path.isdir(subdir) and subdir not in path:
|
||||
if where is None:
|
||||
path.append(dir)
|
||||
_dbg('Appending path %s', subdir)
|
||||
path.append(subdir)
|
||||
else:
|
||||
path.insert(where, dir)
|
||||
_dbg('Inserting path %s', subdir)
|
||||
path.insert(where, subdir)
|
||||
|
||||
|
||||
def _find_include_file(self, include):
|
||||
for directory in self.compiler.include_dirs:
|
||||
_dbg('Checking for include file %s in %s', (include, directory))
|
||||
if os.path.isfile(os.path.join(directory, include)):
|
||||
_dbg('Found %s', include)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
@ -61,15 +75,22 @@ def _find_library_file(self, library):
|
|||
# lib extension, not the system shared lib extension: e.g. .cpython-33.so
|
||||
# vs .so. See Python bug http://bugs.python.org/16754
|
||||
if 'cpython' in self.compiler.shared_lib_extension:
|
||||
_dbg('stripping cpython from shared library extension %s',
|
||||
self.compiler.shared_lib_extension)
|
||||
existing = self.compiler.shared_lib_extension
|
||||
self.compiler.shared_lib_extension = "." + existing.split('.')[-1]
|
||||
ret = self.compiler.find_library_file(self.compiler.library_dirs,
|
||||
library)
|
||||
self.compiler.shared_lib_extension = existing
|
||||
return ret
|
||||
else:
|
||||
return self.compiler.find_library_file(self.compiler.library_dirs,
|
||||
library)
|
||||
ret = self.compiler.find_library_file(self.compiler.library_dirs,
|
||||
library)
|
||||
if ret:
|
||||
_dbg('Found library %s at %s', (library, ret))
|
||||
else:
|
||||
_dbg("Couldn't find library %s in %s",
|
||||
(library, self.compiler.library_dirs))
|
||||
return ret
|
||||
|
||||
|
||||
def _lib_include(root):
|
||||
|
@ -100,10 +121,15 @@ LCMS_ROOT = None
|
|||
|
||||
class pil_build_ext(build_ext):
|
||||
class feature:
|
||||
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
|
||||
jpeg2000 = None
|
||||
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'tcl', 'tk', 'lcms',
|
||||
'webp', 'webpmux', 'jpeg2000']
|
||||
|
||||
required = set(['jpeg', 'zlib'])
|
||||
|
||||
def __init__(self):
|
||||
for f in self.features:
|
||||
setattr(self, f, None)
|
||||
|
||||
def require(self, feat):
|
||||
return feat in self.required
|
||||
|
||||
|
@ -111,9 +137,8 @@ class pil_build_ext(build_ext):
|
|||
return getattr(self, feat) is None
|
||||
|
||||
def __iter__(self):
|
||||
for x in dir(self):
|
||||
if x[1] != '_':
|
||||
yield x
|
||||
for x in self.features:
|
||||
yield x
|
||||
|
||||
feature = feature()
|
||||
|
||||
|
@ -121,7 +146,7 @@ class pil_build_ext(build_ext):
|
|||
('disable-%s' % x, None, 'Disable support for %s' % x) for x in feature
|
||||
] + [
|
||||
('enable-%s' % x, None, 'Enable support for %s' % x) for x in feature
|
||||
]
|
||||
] + [('debug', None, 'Debug logging')]
|
||||
|
||||
def initialize_options(self):
|
||||
build_ext.initialize_options(self)
|
||||
|
@ -131,15 +156,20 @@ class pil_build_ext(build_ext):
|
|||
|
||||
def finalize_options(self):
|
||||
build_ext.finalize_options(self)
|
||||
if self.debug:
|
||||
global DEBUG
|
||||
DEBUG = True
|
||||
for x in self.feature:
|
||||
if getattr(self, 'disable_%s' % x):
|
||||
setattr(self.feature, x, False)
|
||||
self.feature.required.discard(x)
|
||||
_dbg('Disabling %s', x)
|
||||
if getattr(self, 'enable_%s' % x):
|
||||
raise ValueError(
|
||||
'Conflicting options: --enable-%s and --disable-%s' %
|
||||
(x, x))
|
||||
if getattr(self, 'enable_%s' % x):
|
||||
_dbg('Requiring %s', x)
|
||||
self.feature.required.add(x)
|
||||
|
||||
def build_extensions(self):
|
||||
|
@ -314,6 +344,9 @@ class pil_build_ext(build_ext):
|
|||
|
||||
if _tkinter:
|
||||
TCL_VERSION = _tkinter.TCL_VERSION[:3]
|
||||
_dbg('Tkinter found, will check for Tcl/Tk')
|
||||
else:
|
||||
_dbg('Tkinter not found')
|
||||
|
||||
if _tkinter and not TCL_ROOT:
|
||||
# we have Tkinter but the TCL_ROOT variable was not set;
|
||||
|
@ -334,6 +367,7 @@ class pil_build_ext(build_ext):
|
|||
]
|
||||
for TCL_ROOT in roots:
|
||||
TCL_ROOT = os.path.abspath(TCL_ROOT)
|
||||
_dbg('Checking %s for tk.h', TCL_ROOT)
|
||||
if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")):
|
||||
# FIXME: use distutils logging (?)
|
||||
print("--- using Tcl/Tk libraries at", TCL_ROOT)
|
||||
|
@ -342,6 +376,7 @@ class pil_build_ext(build_ext):
|
|||
break
|
||||
else:
|
||||
TCL_ROOT = None
|
||||
_dbg('Tcl/tk not found')
|
||||
|
||||
# add standard directories
|
||||
|
||||
|
@ -373,6 +408,7 @@ class pil_build_ext(build_ext):
|
|||
best_path = os.path.join(program_files, name)
|
||||
|
||||
if best_path:
|
||||
_dbg('Adding %s to search list', best_path)
|
||||
_add_directory(library_dirs, os.path.join(best_path, 'lib'))
|
||||
_add_directory(include_dirs,
|
||||
os.path.join(best_path, 'include'))
|
||||
|
@ -390,6 +426,7 @@ class pil_build_ext(build_ext):
|
|||
feature = self.feature
|
||||
|
||||
if feature.want('zlib'):
|
||||
_dbg('Looking for zlib')
|
||||
if _find_include_file(self, "zlib.h"):
|
||||
if _find_library_file(self, "z"):
|
||||
feature.zlib = "z"
|
||||
|
@ -398,6 +435,7 @@ class pil_build_ext(build_ext):
|
|||
feature.zlib = "zlib" # alternative name
|
||||
|
||||
if feature.want('jpeg'):
|
||||
_dbg('Looking for jpeg')
|
||||
if _find_include_file(self, "jpeglib.h"):
|
||||
if _find_library_file(self, "jpeg"):
|
||||
feature.jpeg = "jpeg"
|
||||
|
@ -407,11 +445,13 @@ class pil_build_ext(build_ext):
|
|||
|
||||
feature.openjpeg_version = None
|
||||
if feature.want('jpeg2000'):
|
||||
_dbg('Looking for jpeg2000')
|
||||
best_version = None
|
||||
best_path = None
|
||||
|
||||
# Find the best version
|
||||
for directory in self.compiler.include_dirs:
|
||||
_dbg('Checking for openjpeg-#.# in %s', directory)
|
||||
try:
|
||||
listdir = os.listdir(directory)
|
||||
except Exception:
|
||||
|
@ -421,10 +461,13 @@ class pil_build_ext(build_ext):
|
|||
if name.startswith('openjpeg-') and \
|
||||
os.path.isfile(os.path.join(directory, name,
|
||||
'openjpeg.h')):
|
||||
_dbg('Found openjpeg.h in %s/%s', (directory, name))
|
||||
version = tuple([int(x) for x in name[9:].split('.')])
|
||||
if best_version is None or version > best_version:
|
||||
best_version = version
|
||||
best_path = os.path.join(directory, name)
|
||||
_dbg('Best openjpeg version %s so far in %s',
|
||||
(best_version, best_path))
|
||||
|
||||
if best_version and _find_library_file(self, 'openjp2'):
|
||||
# Add the directory to the include path so we can include
|
||||
|
@ -436,34 +479,42 @@ class pil_build_ext(build_ext):
|
|||
best_version])
|
||||
|
||||
if feature.want('tiff'):
|
||||
if _find_library_file(self, "tiff"):
|
||||
feature.tiff = "tiff"
|
||||
if sys.platform == "win32" and _find_library_file(self, "libtiff"):
|
||||
feature.tiff = "libtiff"
|
||||
if (sys.platform == "darwin" and
|
||||
_find_library_file(self, "libtiff")):
|
||||
feature.tiff = "libtiff"
|
||||
_dbg('Looking for tiff')
|
||||
if _find_include_file(self, 'tiff.h'):
|
||||
if _find_library_file(self, "tiff"):
|
||||
feature.tiff = "tiff"
|
||||
if sys.platform == "win32" and _find_library_file(self, "libtiff"):
|
||||
feature.tiff = "libtiff"
|
||||
if (sys.platform == "darwin" and
|
||||
_find_library_file(self, "libtiff")):
|
||||
feature.tiff = "libtiff"
|
||||
|
||||
if feature.want('freetype'):
|
||||
_dbg('Looking for freetype')
|
||||
if _find_library_file(self, "freetype"):
|
||||
# look for freetype2 include files
|
||||
freetype_version = 0
|
||||
for dir in self.compiler.include_dirs:
|
||||
if os.path.isfile(os.path.join(dir, "ft2build.h")):
|
||||
for subdir in self.compiler.include_dirs:
|
||||
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
|
||||
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
|
||||
_dbg('Found %s in %s', ("ft2build.h", subdir))
|
||||
freetype_version = 21
|
||||
dir = os.path.join(dir, "freetype2")
|
||||
subdir = os.path.join(subdir, "freetype2")
|
||||
break
|
||||
dir = os.path.join(dir, "freetype2")
|
||||
if os.path.isfile(os.path.join(dir, "ft2build.h")):
|
||||
subdir = os.path.join(subdir, "freetype2")
|
||||
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
|
||||
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
|
||||
_dbg('Found %s in %s', ("ft2build.h", subdir))
|
||||
freetype_version = 21
|
||||
break
|
||||
if freetype_version:
|
||||
feature.freetype = "freetype"
|
||||
feature.freetype_version = freetype_version
|
||||
if dir:
|
||||
_add_directory(self.compiler.include_dirs, dir, 0)
|
||||
if subdir:
|
||||
_add_directory(self.compiler.include_dirs, subdir, 0)
|
||||
|
||||
if feature.want('lcms'):
|
||||
_dbg('Looking for lcms')
|
||||
if _find_include_file(self, "lcms2.h"):
|
||||
if _find_library_file(self, "lcms2"):
|
||||
feature.lcms = "lcms2"
|
||||
|
@ -475,17 +526,20 @@ class pil_build_ext(build_ext):
|
|||
# the library names may vary somewhat (e.g. tcl85 or tcl8.5)
|
||||
version = TCL_VERSION[0] + TCL_VERSION[2]
|
||||
if feature.want('tcl'):
|
||||
_dbg('Looking for TCL')
|
||||
if _find_library_file(self, "tcl" + version):
|
||||
feature.tcl = "tcl" + version
|
||||
elif _find_library_file(self, "tcl" + TCL_VERSION):
|
||||
feature.tcl = "tcl" + TCL_VERSION
|
||||
if feature.want('tk'):
|
||||
_dbg('Looking for TK')
|
||||
if _find_library_file(self, "tk" + version):
|
||||
feature.tk = "tk" + version
|
||||
elif _find_library_file(self, "tk" + TCL_VERSION):
|
||||
feature.tk = "tk" + TCL_VERSION
|
||||
|
||||
if feature.want('webp'):
|
||||
_dbg('Looking for webp')
|
||||
if (_find_include_file(self, "webp/encode.h") and
|
||||
_find_include_file(self, "webp/decode.h")):
|
||||
# In Google's precompiled zip it is call "libwebp":
|
||||
|
@ -495,6 +549,7 @@ class pil_build_ext(build_ext):
|
|||
feature.webp = "libwebp"
|
||||
|
||||
if feature.want('webpmux'):
|
||||
_dbg('Looking for webpmux')
|
||||
if (_find_include_file(self, "webp/mux.h") and
|
||||
_find_include_file(self, "webp/demux.h")):
|
||||
if (_find_library_file(self, "webpmux") and
|
||||
|
@ -518,10 +573,10 @@ class pil_build_ext(build_ext):
|
|||
# core library
|
||||
|
||||
files = ["_imaging.c"]
|
||||
for file in _IMAGING:
|
||||
files.append(file + ".c")
|
||||
for file in _LIB_IMAGING:
|
||||
files.append(os.path.join("libImaging", file + ".c"))
|
||||
for src_file in _IMAGING:
|
||||
files.append(src_file + ".c")
|
||||
for src_file in _LIB_IMAGING:
|
||||
files.append(os.path.join("libImaging", src_file + ".c"))
|
||||
|
||||
libs = []
|
||||
defs = []
|
||||
|
@ -557,7 +612,7 @@ class pil_build_ext(build_ext):
|
|||
["_imagingft.c"],
|
||||
libraries=["freetype"]))
|
||||
|
||||
if os.path.isfile("_imagingcms.c") and feature.lcms:
|
||||
if feature.lcms:
|
||||
extra = []
|
||||
if sys.platform == "win32":
|
||||
extra.extend(["user32", "gdi32"])
|
||||
|
@ -565,7 +620,7 @@ class pil_build_ext(build_ext):
|
|||
["_imagingcms.c"],
|
||||
libraries=[feature.lcms] + extra))
|
||||
|
||||
if os.path.isfile("_webp.c") and feature.webp:
|
||||
if feature.webp:
|
||||
libs = [feature.webp]
|
||||
defs = []
|
||||
|
||||
|
@ -586,16 +641,17 @@ class pil_build_ext(build_ext):
|
|||
framework_roots = [
|
||||
"/Library/Frameworks", "/System/Library/Frameworks"
|
||||
]
|
||||
_dbg('Looking for TclTk Framework Build')
|
||||
for root in framework_roots:
|
||||
root_tcl = os.path.join(root, "Tcl.framework")
|
||||
root_tk = os.path.join(root, "Tk.framework")
|
||||
if (os.path.exists(root_tcl) and os.path.exists(root_tk)):
|
||||
print("--- using frameworks at %s" % root)
|
||||
frameworks = ["-framework", "Tcl", "-framework", "Tk"]
|
||||
dir = os.path.join(root_tcl, "Headers")
|
||||
_add_directory(self.compiler.include_dirs, dir, 0)
|
||||
dir = os.path.join(root_tk, "Headers")
|
||||
_add_directory(self.compiler.include_dirs, dir, 1)
|
||||
subdir = os.path.join(root_tcl, "Headers")
|
||||
_add_directory(self.compiler.include_dirs, subdir, 0)
|
||||
subdir = os.path.join(root_tk, "Headers")
|
||||
_add_directory(self.compiler.include_dirs, subdir, 1)
|
||||
break
|
||||
if frameworks:
|
||||
exts.append(Extension("PIL._imagingtk",
|
||||
|
@ -607,11 +663,8 @@ class pil_build_ext(build_ext):
|
|||
["_imagingtk.c", "Tk/tkImaging.c"],
|
||||
libraries=[feature.tcl, feature.tk]))
|
||||
|
||||
if os.path.isfile("_imagingmath.c"):
|
||||
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
|
||||
|
||||
if os.path.isfile("_imagingmorph.c"):
|
||||
exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"]))
|
||||
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
|
||||
exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"]))
|
||||
|
||||
self.extensions[:] = exts
|
||||
|
||||
|
@ -680,8 +733,8 @@ class pil_build_ext(build_ext):
|
|||
|
||||
if not all:
|
||||
print("To add a missing option, make sure you have the required")
|
||||
print("library, and set the corresponding ROOT variable in the")
|
||||
print("setup.py script.")
|
||||
print("library and headers.")
|
||||
print("See https://pillow.readthedocs.org/en/latest/installation.html#building-from-source")
|
||||
print("")
|
||||
|
||||
print("To check the build, run the selftest.py script.")
|
||||
|
@ -689,8 +742,8 @@ class pil_build_ext(build_ext):
|
|||
|
||||
def check_zlib_version(self, include_dirs):
|
||||
# look for unsafe versions of zlib
|
||||
for dir in include_dirs:
|
||||
zlibfile = os.path.join(dir, "zlib.h")
|
||||
for subdir in include_dirs:
|
||||
zlibfile = os.path.join(subdir, "zlib.h")
|
||||
if os.path.isfile(zlibfile):
|
||||
break
|
||||
else:
|
||||
|
@ -702,7 +755,7 @@ class pil_build_ext(build_ext):
|
|||
if m.group(1) < "1.2.3":
|
||||
return m.group(1)
|
||||
|
||||
# http://hg.python.org/users/barry/rev/7e8deab93d5a
|
||||
# https://hg.python.org/users/barry/rev/7e8deab93d5a
|
||||
def add_multiarch_paths(self):
|
||||
# Debian/Ubuntu multiarch support.
|
||||
# https://wiki.ubuntu.com/MultiarchSpec
|
||||
|
@ -741,7 +794,6 @@ setup(name=NAME,
|
|||
"Development Status :: 6 - Mature",
|
||||
"Topic :: Multimedia :: Graphics",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Scanners",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
|
||||
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
||||
"Topic :: Multimedia :: Graphics :: Viewers",
|
||||
|
|
Loading…
Reference in New Issue
Block a user