merge from master

This commit is contained in:
wiredfool 2013-12-19 20:48:49 -08:00
commit aa1c0fdfb6
30 changed files with 506 additions and 104 deletions

View File

@ -6,7 +6,7 @@ python:
- 3.2
- 3.3
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev"
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev ghostscript"
script:
- python setup.py clean

View File

@ -4,6 +4,30 @@ Changelog (Pillow)
2.3.0 (2014-01-01)
------------------
- 2gigapix image fixes
[wiredfool]
- Save arbitrary tags in Tiff image files
[wiredfool]
- Quote filenames and title before using on command line
[tmccombs]
- Fixed Viewer.show to return properly
[tmccombs]
- Documentation fixes
[wiredfool]
- Fixed memory leak saving images as webp when webpmux is available
[cezarsa]
- Fix compiling with FreeType 2.5.1
[stromnov]
- Adds directories for NetBSD.
[deepy]
- Support RGBA TIFF with missing ExtraSamples tag
[cgohlke]

View File

@ -1,36 +0,0 @@
Contributors (Pillow)
=====================
.. Note:: Contributors: please add your name here
- Alex Po <alex-86p __at__ yandex.ru>
- Anton Vlasenko <antares.spica __at__ gmail.com>
- Brian J. Crowell <brian __at__ fluggo.com>
- Bryant Mairs <bwmairs __at__ ucsc.edu>
- Christoph Gohlke <cgohlke __at__ uci.edu>
- Corey Richardson <corey __at__ octayn.net>
- Daniel Hahler <github __at__ thequod.de>
- David Schmidt <david.schmiddi.86 __at__ gmail.com>
- Eliot <saltycrane __at__ gmail.com>
- etienne <etienne.desautels __at__ gmail.com>
- Jannis Leidel <jannis __at__ leidel.info>
- Kyle MacFarlane <kyle __at__ deletethetrees.com>
- Lars Yencken <lars __at__ yencken.org>
- Liu Qishuai <lqs __at__ lqs.me>
- Manuel Ebert <Maebert __at__ UOS.de>
- Marc Abramowitz <marc __at__ marc-abramowitz.com>
- Matti Picus <gitmatti __at__ picus.org.il>
- Mikhail Korobov <kmike84 __at__ gmail.com>
- OCHIAI, Gouji <gjo.ext __at__ gmail.com>
- Oliver Tonnhofer <olt __at__ bogosoft.com>
- Phil Elson <pelson.pub __at__ gmail.com>
- Sandro Mani <manisandro __at__ gmail.com>
- Simon Law <simon.law __at__ ecometrica.com>
- Stéphane Klein <stephane __at__ harobed.org>
- Steve Johnson <steve __at__ steveasleep.com>
- Takeshi KOMIYA <i.tkomiya __at__ gmail.com>
- Tom Gross <tom __at__ toms-projekte.de>
- Tom Payne <twpayne __at__ gmail.com>
- Tyler Garner <garnertb __at__ gmail.com>
- tdesvenain <thomas.desvenain __at__ gmail.com>
- wiredfool <eric-github __at__ soroos.net>

View File

@ -27,6 +27,8 @@ recursive-include Sane README
recursive-include Scripts *.py
recursive-include Scripts README
recursive-include Tests *.bin
recursive-include Tests *.eps
recursive-include Tests *.gnuplot
recursive-include Tests *.icm
recursive-include Tests *.jpg
recursive-include Tests *.pcf
@ -42,8 +44,11 @@ recursive-include Tk *.c
recursive-include Tk *.txt
recursive-include docs *.bat
recursive-include docs *.gitignore
recursive-include docs *.html
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs *.txt
recursive-include docs Guardfile
recursive-include docs Makefile
recursive-include docs BUILDME
recursive-include docs COPYING

View File

@ -50,14 +50,22 @@ if sys.platform.startswith('win'):
else:
gs_windows_binary = False
def Ghostscript(tile, size, fp):
def Ghostscript(tile, size, fp, scale=1):
"""Render an image using Ghostscript"""
# Unpack decoder tile
decoder, tile, offset, data = tile[0]
length, bbox = data
import tempfile, os
#Hack to support hi-res rendering
scale = int(scale) or 1
orig_size = size
orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale]
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox)
import tempfile, os, subprocess
file = tempfile.mktemp()
@ -65,33 +73,32 @@ def Ghostscript(tile, size, fp):
command = ["gs",
"-q", # quite mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%d" % (72*scale), # set input DPI (dots per inch)
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % file,# output file
"- >/dev/null 2>/dev/null"]
]
if gs_windows_binary is not None:
if gs_windows_binary is False:
raise WindowsError('Unable to locate Ghostscript on paths')
command[0] = gs_windows_binary
command[-1] = '- >nul 2>nul'
command = " ".join(command)
# push data through ghostscript
try:
gs = os.popen(command, "w")
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# adjust for image origin
if bbox[0] != 0 or bbox[1] != 0:
gs.write("%d %d translate\n" % (-bbox[0], -bbox[1]))
gs.stdin.write(("%d %d translate\n" % (-bbox[0], -bbox[1])).encode('ascii'))
fp.seek(offset)
while length > 0:
s = fp.read(8192)
if not s:
break
length = length - len(s)
gs.write(s)
status = gs.close()
gs.stdin.write(s)
gs.stdin.close()
status = gs.wait()
if status:
raise IOError("gs failed (status %d)" % status)
im = Image.core.open_ppm(file)
@ -304,11 +311,11 @@ class EpsImageFile(ImageFile.ImageFile):
if not box:
raise IOError("cannot determine EPS bounding box")
def load(self):
def load(self, scale=1):
# Load EPS via Ghostscript
if not self.tile:
return
self.im = Ghostscript(self.tile, self.size, self.fp)
self.im = Ghostscript(self.tile, self.size, self.fp, scale)
self.mode = self.im.mode
self.size = self.im.size
self.tile = []

View File

@ -675,15 +675,18 @@ class Image:
L = R * 299/1000 + G * 587/1000 + B * 114/1000
When translating a greyscale image into a bilevel image (mode
"1"), all non-zero values are set to 255 (white). To use other
thresholds, use the :py:meth:`~PIL.Image.Image.point` method.
The default method of converting a greyscale ("L") or "RGB"
image into a bilevel (mode "1") image uses Floyd-Steinberg
dither to approximate the original image luminosity levels. If
dither is NONE, all non-zero values are set to 255 (white). To
use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
method.
:param mode: The requested mode.
:param matrix: An optional conversion matrix. If given, this
should be 4- or 16-tuple containing floating point values.
:param dither: Dithering method, used when converting from
mode "RGB" to "P".
mode "RGB" to "P" or from "RGB" or "L" to "1".
Available methods are NONE or FLOYDSTEINBERG (default).
:param palette: Palette to use when converting from mode "RGB"
to "P". Available palettes are WEB or ADAPTIVE.

View File

@ -17,6 +17,11 @@ from __future__ import print_function
from PIL import Image
import os, sys
if(sys.version_info >= (3, 3)):
from shlex import quote
else:
from pipes import quote
_viewers = []
def register(viewer, order=1):
@ -65,7 +70,7 @@ class Viewer:
if base != image.mode and image.mode != "1":
image = image.convert(base)
self.show_image(image, **options)
return self.show_image(image, **options)
# hook methods
@ -99,7 +104,7 @@ if sys.platform == "win32":
format = "BMP"
def get_command(self, file, **options):
return ("start /wait %s && ping -n 2 127.0.0.1 >NUL "
"&& del /f %s" % (file, file))
"&& del /f %s" % (quote(file), quote(file)))
register(WindowsViewer)
@ -111,7 +116,7 @@ elif sys.platform == "darwin":
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a /Applications/Preview.app"
command = "(%s %s; sleep 20; rm -f %s)&" % (command, file, file)
command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file))
return command
register(MacViewer)
@ -134,7 +139,7 @@ else:
class UnixViewer(Viewer):
def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options)
command = "(%s %s; rm -f %s)&" % (command, file, file)
command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
os.system(command)
return 1
@ -154,8 +159,7 @@ else:
# imagemagick's display command instead.
command = executable = "xv"
if title:
# FIXME: do full escaping
command = command + " -name \"%s\"" % title
command = command + " -name %s" % quote(title)
return command, executable
if which("xv"):

View File

@ -262,7 +262,7 @@ def getiptcinfo(im):
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
# as 4-byte integers, so we cannot use the get method...)
try:
type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
except (AttributeError, KeyError):
pass

View File

@ -220,11 +220,45 @@ def _accept(prefix):
# Wrapper for TIFF IFDs.
class ImageFileDirectory(collections.MutableMapping):
""" This class represents a TIFF tag directory. To speed things
up, we don't decode tags unless they're asked for.
# represents a TIFF tag directory. to speed things up,
# we don't decode tags unless they're asked for.
Exposes a dictionary interface of the tags in the directory
ImageFileDirectory[key] = value
value = ImageFileDirectory[key]
def __init__(self, prefix):
Also contains a dictionary of tag types as read from the tiff
image file, 'ImageFileDirectory.tagtype'
Data Structures:
'public'
* self.tagtype = {} Key: numerical tiff tag number
Value: integer corresponding to the data type from
`TiffTags.TYPES`
'internal'
* self.tags = {} Key: numerical tiff tag number
Value: Decoded data, Generally a tuple.
* If set from __setval__ -- always a tuple
* Numeric types -- always a tuple
* String type -- not a tuple, returned as string
* Undefined data -- not a tuple, returned as bytes
* Byte -- not a tuple, returned as byte.
* self.tagdata = {} Key: numerical tiff tag number
Value: undecoded byte string from file
Tags will be found in either self.tags or self.tagdata, but
not both. The union of the two should contain all the tags
from the Tiff image file. External classes shouldn't
reference these unless they're really sure what they're doing.
"""
def __init__(self, prefix=II):
"""
:prefix: 'II'|'MM' tiff endianness
"""
self.prefix = prefix[:2]
if self.prefix == MM:
self.i16, self.i32 = ib16, ib32
@ -270,7 +304,8 @@ class ImageFileDirectory(collections.MutableMapping):
try:
return self.tags[tag]
except KeyError:
type, data = self.tagdata[tag] # unpack on the fly
data = self.tagdata[tag] # unpack on the fly
type = self.tagtype[tag]
size, handler = self.load_dispatch[type]
self.tags[tag] = data = handler(self, data)
del self.tagdata[tag]
@ -299,6 +334,9 @@ class ImageFileDirectory(collections.MutableMapping):
return tag in self
def __setitem__(self, tag, value):
# tags are tuples for integers
# tags are not tuples for byte, string, and undefined data.
# see load_*
if not isinstance(value, tuple):
value = (value,)
self.tags[tag] = value
@ -413,7 +451,7 @@ class ImageFileDirectory(collections.MutableMapping):
warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" % (size, len(data), tag))
continue
self.tagdata[tag] = typ, data
self.tagdata[tag] = data
self.tagtype[tag] = typ
if Image.DEBUG:
@ -450,25 +488,42 @@ class ImageFileDirectory(collections.MutableMapping):
if tag in self.tagtype:
typ = self.tagtype[tag]
if Image.DEBUG:
print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
if typ == 1:
# byte data
data = value
if isinstance(value, tuple):
data = value = value[-1]
else:
data = value
elif typ == 7:
# untyped data
data = value = b"".join(value)
elif isinstance(value[0], str):
elif isStringType(value[0]):
# string data
if isinstance(value, tuple):
value = value[-1]
typ = 2
data = value = b"\0".join(value.encode('ascii', 'replace')) + b"\0"
# was b'\0'.join(str), which led to \x00a\x00b sorts
# of strings which I don't see in in the wild tiffs
# and doesn't match the tiff spec: 8-bit byte that
# contains a 7-bit ASCII code; the last byte must be
# NUL (binary zero). Also, I don't think this was well
# excersized before.
data = value = b"" + value.encode('ascii', 'replace') + b"\0"
else:
# integer data
if tag == STRIPOFFSETS:
stripoffsets = len(directory)
typ = 4 # to avoid catch-22
elif tag in (X_RESOLUTION, Y_RESOLUTION):
elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ==5:
# identify rational data fields
typ = 5
if isinstance(value[0], tuple):
# long name for flatten
value = tuple(itertools.chain.from_iterable(value))
elif not typ:
typ = 3
for v in value:
@ -500,6 +555,7 @@ class ImageFileDirectory(collections.MutableMapping):
count = len(value)
if typ == 5:
count = count // 2 # adjust for rational data field
append((tag, typ, count, o32(offset), data))
offset = offset + len(data)
if offset & 1:
@ -941,23 +997,34 @@ def _save(im, fp, filename):
ifd[IMAGEWIDTH] = im.size[0]
ifd[IMAGELENGTH] = im.size[1]
# write any arbitrary tags passed in as an ImageFileDirectory
info = im.encoderinfo.get("tiffinfo",{})
if Image.DEBUG:
print ("Tiffinfo Keys: %s"% info.keys)
keys = list(info.keys())
for key in keys:
ifd[key] = info.get(key)
try:
ifd.tagtype[key] = info.tagtype[key]
except:
pass # might not be an IFD, Might not have populated type
# additions written by Greg Couch, gregc@cgl.ucsf.edu
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
if hasattr(im, 'tag'):
# preserve tags from original TIFF image file
for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
if key in im.tag.tagdata:
ifd[key] = im.tag.tagdata.get(key)
# preserve some more tags from original TIFF image file
# -- 2008-06-06 Florian Hoech
ifd.tagtype = im.tag.tagtype
for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
if key in im.tag:
ifd[key] = im.tag[key]
ifd.tagtype[key] = im.tag.tagtype.get(key, None)
# preserve ICC profile (should also work when saving other formats
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
if "icc_profile" in im.info:
ifd[ICCPROFILE] = im.info["icc_profile"]
if "description" in im.encoderinfo:
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
if "resolution" in im.encoderinfo:

View File

@ -12,7 +12,7 @@
# ;-)
VERSION = '1.1.7' # PIL version
PILLOW_VERSION = '2.2.1' # Pillow
PILLOW_VERSION = '2.3.0' # Pillow
_plugins = ['ArgImagePlugin',
'BmpImagePlugin',

View File

@ -0,0 +1,30 @@
#!/usr/bin/gnuplot
#This is the script that was used to create our sample EPS files
#We used the following version of the gnuplot program
#G N U P L O T
#Version 4.6 patchlevel 3 last modified 2013-04-12
#Build System: Darwin x86_64
#This file will generate the non_zero_bb.eps variant, in order to get the
#zero_bb.eps variant you will need to edit line6 in the result file to
#be "%%BoundingBox: 0 0 460 352" instead of "%%BoundingBox: 50 50 410 302"
set t postscript eps color
set o "sample.eps"
set dummy u,v
set key bmargin center horizontal Right noreverse enhanced autotitles nobox
set parametric
set view 50, 30, 1, 1
set isosamples 10, 10
set hidden3d back offset 1 trianglepattern 3 undefined 1 altdiagonal bentover
set ticslevel 0
set title "Interlocking Tori"
set style line 1 lt 1 lw 1 pt 3 lc rgb "red"
set style line 2 lt 1 lw 1 pt 3 lc rgb "blue"
set urange [ -3.14159 : 3.14159 ] noreverse nowriteback
set vrange [ -3.14159 : 3.14159 ] noreverse nowriteback
splot cos(u)+.5*cos(u)*cos(v),sin(u)+.5*sin(u)*cos(v),.5*sin(v) ls 1,\
1+cos(u)+.5*cos(u)*cos(v),.5*sin(v),sin(u)+.5*sin(u)*cos(v) ls 2

BIN
Tests/images/lena.tif Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
Tests/images/zero_bb.eps Normal file

Binary file not shown.

BIN
Tests/images/zero_bb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,27 @@
from tester import *
# This test is not run automatically.
#
# It requires > 2gb memory for the >2 gigapixel image generated in the
# second test. Running this automatically would amount to a denial of
# service on our testing infrastructure. I expect this test to fail
# on any 32 bit machine, as well as any smallish things (like
# raspberrypis). It does succeed on a 3gb Ubuntu 12.04x64 VM on python
# 2.7 an 3.2
from PIL import Image
ydim = 32769
xdim = 48000
f = tempfile('temp.png')
def _write_png(xdim,ydim):
im = Image.new('L',(xdim,ydim),(0))
im.save(f)
success()
def test_large():
""" succeeded prepatch"""
_write_png(xdim,ydim)
def test_2gpx():
"""failed prepatch"""
_write_png(xdim,xdim)

82
Tests/test_file_eps.py Normal file
View File

@ -0,0 +1,82 @@
from tester import *
from PIL import Image
#Our two EPS test files (they are identical except for their bounding boxes)
file1 = "Tests/images/zero_bb.eps"
file2 = "Tests/images/non_zero_bb.eps"
#Due to palletization, we'll need to convert these to RGB after load
file1_compare = "Tests/images/zero_bb.png"
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
file2_compare = "Tests/images/non_zero_bb.png"
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
def test_sanity():
#Regular scale
image1 = Image.open(file1)
image1.load()
assert_equal(image1.mode, "RGB")
assert_equal(image1.size, (460, 352))
assert_equal(image1.format, "EPS")
image2 = Image.open(file2)
image2.load()
assert_equal(image2.mode, "RGB")
assert_equal(image2.size, (360, 252))
assert_equal(image2.format, "EPS")
#Double scale
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
assert_equal(image1_scale2.mode, "RGB")
assert_equal(image1_scale2.size, (920, 704))
assert_equal(image1_scale2.format, "EPS")
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
assert_equal(image2_scale2.mode, "RGB")
assert_equal(image2_scale2.size, (720, 504))
assert_equal(image2_scale2.format, "EPS")
def test_render_scale1():
#We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
image1_scale1 = Image.open(file1)
image1_scale1.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
#Non-Zero bounding box
image2_scale1 = Image.open(file2)
image2_scale1.load()
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
image2_scale1_compare.load()
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
def test_render_scale2():
#We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
image1_scale2_compare.load()
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
#Non-Zero bounding box
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
image2_scale2_compare.load()
assert_image_similar(image2_scale2, image2_scale2_compare, 10)

View File

@ -107,6 +107,29 @@ def test_adobe_deflate_tiff():
assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
assert_no_exception(lambda: im.load())
def test_write_metadata():
""" Test metadata writing through libtiff """
img = Image.open('Tests/images/lena_g4.tif')
f = tempfile('temp.tiff')
img.save(f, tiffinfo = img.tag)
loaded = Image.open(f)
original = img.tag.named()
reloaded = loaded.tag.named()
# PhotometricInterpretation is set from SAVE_INFO, not the original image.
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation']
for tag, value in reloaded.items():
if tag not in ignored:
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
for tag, value in original.items():
if tag not in ignored:
assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
def test_g3_compression():
i = Image.open('Tests/images/lena_g4_500.tif')
@ -116,7 +139,7 @@ def test_g3_compression():
reread = Image.open(out)
assert_equal(reread.info['compression'], 'group3')
assert_image_equal(reread, i)
def test_little_endian():
im = Image.open('Tests/images/16bit.deflate.tif')
assert_equal(im.getpixel((0,0)), 480)

View File

@ -0,0 +1,80 @@
from tester import *
from PIL import Image, TiffImagePlugin, TiffTags
tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
def test_rt_metadata():
""" Test writing arbitray metadata into the tiff image directory
Use case is ImageJ private tags, one numeric, one arbitrary
data. https://github.com/python-imaging/Pillow/issues/291
"""
img = lena()
textdata = "This is some arbitrary metadata for a text field"
info = TiffImagePlugin.ImageFileDirectory()
info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata)
info[tag_ids['ImageJMetaData']] = textdata
f = tempfile("temp.tif")
img.save(f, tiffinfo=info)
loaded = Image.open(f)
assert_equal(loaded.tag[50838], (len(textdata),))
assert_equal(loaded.tag[50839], textdata)
def test_read_metadata():
img = Image.open('Tests/images/lena_g4.tif')
known = {'YResolution': ((1207959552, 16777216),),
'PlanarConfiguration': (1,),
'BitsPerSample': (1,),
'ImageLength': (128,),
'Compression': (4,),
'FillOrder': (1,),
'DocumentName': 'lena.g4.tif',
'RowsPerStrip': (128,),
'ResolutionUnit': (1,),
'PhotometricInterpretation': (0,),
'PageNumber': (0, 1),
'XResolution': ((1207959552, 16777216),),
'ImageWidth': (128,),
'Orientation': (1,),
'StripByteCounts': (1796,),
'SamplesPerPixel': (1,),
'StripOffsets': (8,),
'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'}
# assert_equal is equivalent, but less helpful in telling what's wrong.
named = img.tag.named()
for tag, value in named.items():
assert_equal(known[tag], value)
for tag, value in known.items():
assert_equal(value, named[tag])
def test_write_metadata():
""" Test metadata writing through the python code """
img = Image.open('Tests/images/lena.tif')
f = tempfile('temp.tiff')
img.save(f, tiffinfo = img.tag)
loaded = Image.open(f)
original = img.tag.named()
reloaded = loaded.tag.named()
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets']
for tag, value in reloaded.items():
if tag not in ignored:
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
for tag, value in original.items():
if tag not in ignored:
assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)

View File

@ -71,7 +71,7 @@
* See the README file for information on usage and redistribution.
*/
#define PILLOW_VERSION "2.2.1"
#define PILLOW_VERSION "2.3.0"
#include "Python.h"
@ -1234,7 +1234,8 @@ static PyObject*
_putdata(ImagingObject* self, PyObject* args)
{
Imaging image;
int n, i, x, y;
// i & n are # pixels, require py_ssize_t. x can be as large as n. y, just because.
Py_ssize_t n, i, x, y;
PyObject* data;
double scale = 1.0;
@ -1244,16 +1245,16 @@ _putdata(ImagingObject* self, PyObject* args)
return NULL;
if (!PySequence_Check(data)) {
PyErr_SetString(PyExc_TypeError, must_be_sequence);
return NULL;
PyErr_SetString(PyExc_TypeError, must_be_sequence);
return NULL;
}
image = self->image;
n = PyObject_Length(data);
if (n > (int) (image->xsize * image->ysize)) {
PyErr_SetString(PyExc_TypeError, "too many data entries");
return NULL;
if (n > (Py_ssize_t) (image->xsize * image->ysize)) {
PyErr_SetString(PyExc_TypeError, "too many data entries");
return NULL;
}
if (image->image8) {
@ -1648,7 +1649,7 @@ _stretch(ImagingObject* self, PyObject* args)
imIn = self->image;
/* two-pass resize: minimize size of intermediate image */
if (imIn->xsize * ysize < xsize * imIn->ysize)
if ((Py_ssize_t) imIn->xsize * ysize < (Py_ssize_t) xsize * imIn->ysize)
imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize);
else
imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
@ -3073,7 +3074,7 @@ image_length(ImagingObject *self)
{
Imaging im = self->image;
return im->xsize * im->ysize;
return (Py_ssize_t) im->xsize * im->ysize;
}
static PyObject *

View File

@ -59,7 +59,11 @@ struct {
const char* message;
} ft_errors[] =
#if defined(USE_FREETYPE_2_1)
#include FT_ERRORS_H
#else
#include <freetype/fterrors.h>
#endif
/* -------------------------------------------------------------------- */
/* font objects */

View File

@ -113,11 +113,11 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
WebPMuxAssemble(mux, &output_data);
WebPMuxDelete(mux);
free(output);
output = (uint8_t*)output_data.bytes;
ret_size = output_data.size;
if (ret_size > 0) {
PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);
PyObject *ret = PyBytes_FromStringAndSize((char*)output_data.bytes, ret_size);
WebPDataClear(&output_data);
return ret;
}

View File

@ -36,6 +36,20 @@ PIL identifies EPS files containing image data, and can read files that contain
embedded raster images (ImageData descriptors). If Ghostscript is available,
other EPS files can be read as well. The EPS driver can also write EPS images.
If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load`
method with the following parameter to affect how Ghostscript renders the EPS
**scale**
Affects the scale of the resultant rasterized image. If the EPS suggests
that the image be rendered at 100px x 100px, setting this parameter to
2 will make the Ghostscript render a 200px x 200px image instead. The
relative position of the bounding box is maintained::
im = Image.open(...)
im.size #(100,100)
im.load(scale=2)
im.size #(200,200)
GIF
^^^
@ -265,6 +279,57 @@ dictionary of decoded TIFF fields. Values are stored as either strings or
tuples. Note that only short, long and ASCII tags are correctly unpacked by
this release.
Saving Tiff Images
~~~~~~~~~~~~~~~~~~
The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
**tiffinfo**
A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict
object containing tiff tags and values. The TIFF field type is
autodetected for Numeric and string values, any other types
require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory`
object and setting the type in
:py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with
the appropriate numerical value from
``TiffTags.TYPES``.
.. versionadded:: 2.3.0
**compression**
A string containing the desired compression method for the
file. (valid only with libtiff installed) Valid compression
methods are: ``[None, "tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan",
"tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]``
These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo.
**description**
**software**
**date time**
**artist**
**copyright**
Strings
**resolution unit**
A string of "inch", "centimeter" or "cm"
**resolution**
**x resolution**
**y resolution**
**dpi**
Either a Float, Integer, or 2 tuple of (numerator,
denominator). Resolution implies an equal x and y resolution, dpi
also implies a unit of inches.
WebP
^^^^
@ -273,19 +338,19 @@ format are currently undocumented.
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**lossless**
**lossless**
If present, instructs the WEBP writer to use lossless
compression.
**quality**
**quality**
Integer, 1-100, Defaults to 80. Sets the quality level for
lossy compression.
**icc_procfile**
**icc_procfile**
The ICC Profile to include in the saved file. Only supported if
the system webp library was built with webpmux support.
**exif**
**exif**
The exif data to include in the saved file. Only supported if
the system webp library was built with webpmux support.

View File

@ -1,10 +1,13 @@
Writing your own file decoder
=============================
The Python Imaging Library uses a plug-in model which allows you to add your
own decoders to the library, without any changes to the library itself. Such
plug-ins have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique
format name (usually an abbreviation).
The Python Imaging Library uses a plug-in model which allows you to
add your own decoders to the library, without any changes to the
library itself. Such plug-ins usually have names like
:file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name
(usually an abbreviation).
.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually.
A decoder plug-in should contain a decoder class, based on the
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an

View File

@ -60,9 +60,13 @@ Many of Pillow's features require external libraries:
* **littlecms** provides color management
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
above uses liblcms2. Tested with **1.19** and **2.2**.
* **libwebp** provides the Webp format.
* Pillow has been tested with version **0.1.3**, which does not read transparent webp files. Version **0.3.0** supports transparency.
* Pillow has been tested with version **0.1.3**, which does not read
transparent webp files. Version **0.3.0** supports transparency.
* **tcl/tk** provides support for tkinter bitmap and photo images.
@ -101,13 +105,13 @@ Or for Python 3::
Prerequisites are installed on **Ubuntu 10.04 LTS** with::
$ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \
libfreetype6-dev liblcms1-dev tcl8.5-dev tk8.5-dev
libfreetype6-dev tcl8.5-dev tk8.5-dev
Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy
7.0** with::
$ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
Mac OS X installation
---------------------

View File

@ -15,3 +15,9 @@ to this::
The :py:mod:`_imaging` module has been moved. You can now import it like this::
from PIL.Image import core as _imaging
The image plugin loading mechanisim has changed. Pillow no longer
automatically imports any file in the Python path with a name ending
in :file:`ImagePlugin.py`. You will need to import your image plugin
manually.

View File

@ -82,7 +82,7 @@ except ImportError:
NAME = 'Pillow'
VERSION = '2.2.1'
VERSION = '2.3.0'
TCL_ROOT = None
JPEG_ROOT = None
ZLIB_ROOT = None
@ -211,6 +211,10 @@ class pil_build_ext(build_ext):
# work ;-)
self.add_multiarch_paths()
elif sys.platform.startswith("netbsd"):
_add_directory(library_dirs, "/usr/pkg/lib")
_add_directory(include_dirs, "/usr/pkg/include")
_add_directory(library_dirs, "/usr/local/lib")
# FIXME: check /opt/stuff directories here?
@ -583,8 +587,7 @@ setup(
description='Python Imaging Library (Fork)',
long_description=(
_read('README.rst') + b'\n' +
_read('CHANGES.rst') + b'\n' +
_read('CONTRIBUTORS.rst')).decode('utf-8'),
_read('CHANGES.rst')),
author='Alex Clark (fork author)',
author_email='aclark@aclark.net',
url='http://python-imaging.github.io/',