mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 09:44:31 +03:00
merge from master
This commit is contained in:
commit
aa1c0fdfb6
|
@ -6,7 +6,7 @@ python:
|
||||||
- 3.2
|
- 3.2
|
||||||
- 3.3
|
- 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:
|
script:
|
||||||
- python setup.py clean
|
- python setup.py clean
|
||||||
|
|
24
CHANGES.rst
24
CHANGES.rst
|
@ -4,6 +4,30 @@ Changelog (Pillow)
|
||||||
2.3.0 (2014-01-01)
|
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
|
- Support RGBA TIFF with missing ExtraSamples tag
|
||||||
[cgohlke]
|
[cgohlke]
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
|
@ -27,6 +27,8 @@ recursive-include Sane README
|
||||||
recursive-include Scripts *.py
|
recursive-include Scripts *.py
|
||||||
recursive-include Scripts README
|
recursive-include Scripts README
|
||||||
recursive-include Tests *.bin
|
recursive-include Tests *.bin
|
||||||
|
recursive-include Tests *.eps
|
||||||
|
recursive-include Tests *.gnuplot
|
||||||
recursive-include Tests *.icm
|
recursive-include Tests *.icm
|
||||||
recursive-include Tests *.jpg
|
recursive-include Tests *.jpg
|
||||||
recursive-include Tests *.pcf
|
recursive-include Tests *.pcf
|
||||||
|
@ -42,8 +44,11 @@ recursive-include Tk *.c
|
||||||
recursive-include Tk *.txt
|
recursive-include Tk *.txt
|
||||||
recursive-include docs *.bat
|
recursive-include docs *.bat
|
||||||
recursive-include docs *.gitignore
|
recursive-include docs *.gitignore
|
||||||
|
recursive-include docs *.html
|
||||||
recursive-include docs *.py
|
recursive-include docs *.py
|
||||||
recursive-include docs *.rst
|
recursive-include docs *.rst
|
||||||
|
recursive-include docs *.txt
|
||||||
|
recursive-include docs Guardfile
|
||||||
recursive-include docs Makefile
|
recursive-include docs Makefile
|
||||||
recursive-include docs BUILDME
|
recursive-include docs BUILDME
|
||||||
recursive-include docs COPYING
|
recursive-include docs COPYING
|
||||||
|
|
|
@ -50,14 +50,22 @@ if sys.platform.startswith('win'):
|
||||||
else:
|
else:
|
||||||
gs_windows_binary = False
|
gs_windows_binary = False
|
||||||
|
|
||||||
def Ghostscript(tile, size, fp):
|
def Ghostscript(tile, size, fp, scale=1):
|
||||||
"""Render an image using Ghostscript"""
|
"""Render an image using Ghostscript"""
|
||||||
|
|
||||||
# Unpack decoder tile
|
# Unpack decoder tile
|
||||||
decoder, tile, offset, data = tile[0]
|
decoder, tile, offset, data = tile[0]
|
||||||
length, bbox = data
|
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()
|
file = tempfile.mktemp()
|
||||||
|
|
||||||
|
@ -65,33 +73,32 @@ def Ghostscript(tile, size, fp):
|
||||||
command = ["gs",
|
command = ["gs",
|
||||||
"-q", # quite mode
|
"-q", # quite mode
|
||||||
"-g%dx%d" % size, # set output geometry (pixels)
|
"-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
|
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
|
||||||
"-sDEVICE=ppmraw", # ppm driver
|
"-sDEVICE=ppmraw", # ppm driver
|
||||||
"-sOutputFile=%s" % file,# output file
|
"-sOutputFile=%s" % file,# output file
|
||||||
"- >/dev/null 2>/dev/null"]
|
]
|
||||||
|
|
||||||
if gs_windows_binary is not None:
|
if gs_windows_binary is not None:
|
||||||
if gs_windows_binary is False:
|
if gs_windows_binary is False:
|
||||||
raise WindowsError('Unable to locate Ghostscript on paths')
|
raise WindowsError('Unable to locate Ghostscript on paths')
|
||||||
command[0] = gs_windows_binary
|
command[0] = gs_windows_binary
|
||||||
command[-1] = '- >nul 2>nul'
|
|
||||||
|
|
||||||
command = " ".join(command)
|
|
||||||
|
|
||||||
# push data through ghostscript
|
# push data through ghostscript
|
||||||
try:
|
try:
|
||||||
gs = os.popen(command, "w")
|
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
# adjust for image origin
|
# adjust for image origin
|
||||||
if bbox[0] != 0 or bbox[1] != 0:
|
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)
|
fp.seek(offset)
|
||||||
while length > 0:
|
while length > 0:
|
||||||
s = fp.read(8192)
|
s = fp.read(8192)
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
length = length - len(s)
|
length = length - len(s)
|
||||||
gs.write(s)
|
gs.stdin.write(s)
|
||||||
status = gs.close()
|
gs.stdin.close()
|
||||||
|
status = gs.wait()
|
||||||
if status:
|
if status:
|
||||||
raise IOError("gs failed (status %d)" % status)
|
raise IOError("gs failed (status %d)" % status)
|
||||||
im = Image.core.open_ppm(file)
|
im = Image.core.open_ppm(file)
|
||||||
|
@ -304,11 +311,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
if not box:
|
if not box:
|
||||||
raise IOError("cannot determine EPS bounding box")
|
raise IOError("cannot determine EPS bounding box")
|
||||||
|
|
||||||
def load(self):
|
def load(self, scale=1):
|
||||||
# Load EPS via Ghostscript
|
# Load EPS via Ghostscript
|
||||||
if not self.tile:
|
if not self.tile:
|
||||||
return
|
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.mode = self.im.mode
|
||||||
self.size = self.im.size
|
self.size = self.im.size
|
||||||
self.tile = []
|
self.tile = []
|
||||||
|
|
11
PIL/Image.py
11
PIL/Image.py
|
@ -675,15 +675,18 @@ class Image:
|
||||||
|
|
||||||
L = R * 299/1000 + G * 587/1000 + B * 114/1000
|
L = R * 299/1000 + G * 587/1000 + B * 114/1000
|
||||||
|
|
||||||
When translating a greyscale image into a bilevel image (mode
|
The default method of converting a greyscale ("L") or "RGB"
|
||||||
"1"), all non-zero values are set to 255 (white). To use other
|
image into a bilevel (mode "1") image uses Floyd-Steinberg
|
||||||
thresholds, use the :py:meth:`~PIL.Image.Image.point` method.
|
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 mode: The requested mode.
|
||||||
:param matrix: An optional conversion matrix. If given, this
|
:param matrix: An optional conversion matrix. If given, this
|
||||||
should be 4- or 16-tuple containing floating point values.
|
should be 4- or 16-tuple containing floating point values.
|
||||||
:param dither: Dithering method, used when converting from
|
: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).
|
Available methods are NONE or FLOYDSTEINBERG (default).
|
||||||
:param palette: Palette to use when converting from mode "RGB"
|
:param palette: Palette to use when converting from mode "RGB"
|
||||||
to "P". Available palettes are WEB or ADAPTIVE.
|
to "P". Available palettes are WEB or ADAPTIVE.
|
||||||
|
|
|
@ -17,6 +17,11 @@ from __future__ import print_function
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import os, sys
|
import os, sys
|
||||||
|
|
||||||
|
if(sys.version_info >= (3, 3)):
|
||||||
|
from shlex import quote
|
||||||
|
else:
|
||||||
|
from pipes import quote
|
||||||
|
|
||||||
_viewers = []
|
_viewers = []
|
||||||
|
|
||||||
def register(viewer, order=1):
|
def register(viewer, order=1):
|
||||||
|
@ -65,7 +70,7 @@ class Viewer:
|
||||||
if base != image.mode and image.mode != "1":
|
if base != image.mode and image.mode != "1":
|
||||||
image = image.convert(base)
|
image = image.convert(base)
|
||||||
|
|
||||||
self.show_image(image, **options)
|
return self.show_image(image, **options)
|
||||||
|
|
||||||
# hook methods
|
# hook methods
|
||||||
|
|
||||||
|
@ -99,7 +104,7 @@ if sys.platform == "win32":
|
||||||
format = "BMP"
|
format = "BMP"
|
||||||
def get_command(self, file, **options):
|
def get_command(self, file, **options):
|
||||||
return ("start /wait %s && ping -n 2 127.0.0.1 >NUL "
|
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)
|
register(WindowsViewer)
|
||||||
|
|
||||||
|
@ -111,7 +116,7 @@ elif sys.platform == "darwin":
|
||||||
# on darwin open returns immediately resulting in the temp
|
# on darwin open returns immediately resulting in the temp
|
||||||
# file removal while app is opening
|
# file removal while app is opening
|
||||||
command = "open -a /Applications/Preview.app"
|
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
|
return command
|
||||||
|
|
||||||
register(MacViewer)
|
register(MacViewer)
|
||||||
|
@ -134,7 +139,7 @@ else:
|
||||||
class UnixViewer(Viewer):
|
class UnixViewer(Viewer):
|
||||||
def show_file(self, file, **options):
|
def show_file(self, file, **options):
|
||||||
command, executable = self.get_command_ex(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)
|
os.system(command)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@ -154,8 +159,7 @@ else:
|
||||||
# imagemagick's display command instead.
|
# imagemagick's display command instead.
|
||||||
command = executable = "xv"
|
command = executable = "xv"
|
||||||
if title:
|
if title:
|
||||||
# FIXME: do full escaping
|
command = command + " -name %s" % quote(title)
|
||||||
command = command + " -name \"%s\"" % title
|
|
||||||
return command, executable
|
return command, executable
|
||||||
|
|
||||||
if which("xv"):
|
if which("xv"):
|
||||||
|
|
|
@ -262,7 +262,7 @@ def getiptcinfo(im):
|
||||||
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
|
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
|
||||||
# as 4-byte integers, so we cannot use the get method...)
|
# as 4-byte integers, so we cannot use the get method...)
|
||||||
try:
|
try:
|
||||||
type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
|
data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
|
||||||
except (AttributeError, KeyError):
|
except (AttributeError, KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -220,11 +220,45 @@ def _accept(prefix):
|
||||||
# Wrapper for TIFF IFDs.
|
# Wrapper for TIFF IFDs.
|
||||||
|
|
||||||
class ImageFileDirectory(collections.MutableMapping):
|
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,
|
Exposes a dictionary interface of the tags in the directory
|
||||||
# we don't decode tags unless they're asked for.
|
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]
|
self.prefix = prefix[:2]
|
||||||
if self.prefix == MM:
|
if self.prefix == MM:
|
||||||
self.i16, self.i32 = ib16, ib32
|
self.i16, self.i32 = ib16, ib32
|
||||||
|
@ -270,7 +304,8 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
try:
|
try:
|
||||||
return self.tags[tag]
|
return self.tags[tag]
|
||||||
except KeyError:
|
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]
|
size, handler = self.load_dispatch[type]
|
||||||
self.tags[tag] = data = handler(self, data)
|
self.tags[tag] = data = handler(self, data)
|
||||||
del self.tagdata[tag]
|
del self.tagdata[tag]
|
||||||
|
@ -299,6 +334,9 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
return tag in self
|
return tag in self
|
||||||
|
|
||||||
def __setitem__(self, tag, value):
|
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):
|
if not isinstance(value, tuple):
|
||||||
value = (value,)
|
value = (value,)
|
||||||
self.tags[tag] = 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))
|
warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" % (size, len(data), tag))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.tagdata[tag] = typ, data
|
self.tagdata[tag] = data
|
||||||
self.tagtype[tag] = typ
|
self.tagtype[tag] = typ
|
||||||
|
|
||||||
if Image.DEBUG:
|
if Image.DEBUG:
|
||||||
|
@ -450,25 +488,42 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
|
|
||||||
if tag in self.tagtype:
|
if tag in self.tagtype:
|
||||||
typ = self.tagtype[tag]
|
typ = self.tagtype[tag]
|
||||||
|
|
||||||
|
if Image.DEBUG:
|
||||||
|
print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
|
||||||
|
|
||||||
if typ == 1:
|
if typ == 1:
|
||||||
# byte data
|
# byte data
|
||||||
data = value
|
if isinstance(value, tuple):
|
||||||
|
data = value = value[-1]
|
||||||
|
else:
|
||||||
|
data = value
|
||||||
elif typ == 7:
|
elif typ == 7:
|
||||||
# untyped data
|
# untyped data
|
||||||
data = value = b"".join(value)
|
data = value = b"".join(value)
|
||||||
elif isinstance(value[0], str):
|
elif isStringType(value[0]):
|
||||||
# string data
|
# string data
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
value = value[-1]
|
||||||
typ = 2
|
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:
|
else:
|
||||||
# integer data
|
# integer data
|
||||||
if tag == STRIPOFFSETS:
|
if tag == STRIPOFFSETS:
|
||||||
stripoffsets = len(directory)
|
stripoffsets = len(directory)
|
||||||
typ = 4 # to avoid catch-22
|
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
|
# identify rational data fields
|
||||||
typ = 5
|
typ = 5
|
||||||
|
if isinstance(value[0], tuple):
|
||||||
|
# long name for flatten
|
||||||
|
value = tuple(itertools.chain.from_iterable(value))
|
||||||
elif not typ:
|
elif not typ:
|
||||||
typ = 3
|
typ = 3
|
||||||
for v in value:
|
for v in value:
|
||||||
|
@ -500,6 +555,7 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
count = len(value)
|
count = len(value)
|
||||||
if typ == 5:
|
if typ == 5:
|
||||||
count = count // 2 # adjust for rational data field
|
count = count // 2 # adjust for rational data field
|
||||||
|
|
||||||
append((tag, typ, count, o32(offset), data))
|
append((tag, typ, count, o32(offset), data))
|
||||||
offset = offset + len(data)
|
offset = offset + len(data)
|
||||||
if offset & 1:
|
if offset & 1:
|
||||||
|
@ -941,23 +997,34 @@ def _save(im, fp, filename):
|
||||||
ifd[IMAGEWIDTH] = im.size[0]
|
ifd[IMAGEWIDTH] = im.size[0]
|
||||||
ifd[IMAGELENGTH] = im.size[1]
|
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
|
# additions written by Greg Couch, gregc@cgl.ucsf.edu
|
||||||
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
|
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
|
||||||
if hasattr(im, 'tag'):
|
if hasattr(im, 'tag'):
|
||||||
# preserve tags from original TIFF image file
|
# preserve tags from original TIFF image file
|
||||||
for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
|
for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
|
||||||
if key in im.tag.tagdata:
|
IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
|
||||||
ifd[key] = im.tag.tagdata.get(key)
|
|
||||||
# preserve some more tags from original TIFF image file
|
|
||||||
# -- 2008-06-06 Florian Hoech
|
|
||||||
ifd.tagtype = im.tag.tagtype
|
|
||||||
for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
|
|
||||||
if key in im.tag:
|
if key in im.tag:
|
||||||
ifd[key] = im.tag[key]
|
ifd[key] = im.tag[key]
|
||||||
|
ifd.tagtype[key] = im.tag.tagtype.get(key, None)
|
||||||
|
|
||||||
# preserve ICC profile (should also work when saving other formats
|
# preserve ICC profile (should also work when saving other formats
|
||||||
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
||||||
if "icc_profile" in im.info:
|
if "icc_profile" in im.info:
|
||||||
ifd[ICCPROFILE] = im.info["icc_profile"]
|
ifd[ICCPROFILE] = im.info["icc_profile"]
|
||||||
|
|
||||||
if "description" in im.encoderinfo:
|
if "description" in im.encoderinfo:
|
||||||
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
|
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
|
||||||
if "resolution" in im.encoderinfo:
|
if "resolution" in im.encoderinfo:
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# ;-)
|
# ;-)
|
||||||
|
|
||||||
VERSION = '1.1.7' # PIL version
|
VERSION = '1.1.7' # PIL version
|
||||||
PILLOW_VERSION = '2.2.1' # Pillow
|
PILLOW_VERSION = '2.3.0' # Pillow
|
||||||
|
|
||||||
_plugins = ['ArgImagePlugin',
|
_plugins = ['ArgImagePlugin',
|
||||||
'BmpImagePlugin',
|
'BmpImagePlugin',
|
||||||
|
|
30
Tests/images/create_eps.gnuplot
Normal file
30
Tests/images/create_eps.gnuplot
Normal 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
BIN
Tests/images/lena.tif
Normal file
Binary file not shown.
BIN
Tests/images/non_zero_bb.eps
Normal file
BIN
Tests/images/non_zero_bb.eps
Normal file
Binary file not shown.
BIN
Tests/images/non_zero_bb.png
Normal file
BIN
Tests/images/non_zero_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
Tests/images/non_zero_bb_scale2.png
Normal file
BIN
Tests/images/non_zero_bb_scale2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
Tests/images/zero_bb.eps
Normal file
BIN
Tests/images/zero_bb.eps
Normal file
Binary file not shown.
BIN
Tests/images/zero_bb.png
Normal file
BIN
Tests/images/zero_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
Tests/images/zero_bb_scale2.png
Normal file
BIN
Tests/images/zero_bb_scale2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
27
Tests/large_memory_test.py
Normal file
27
Tests/large_memory_test.py
Normal 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
82
Tests/test_file_eps.py
Normal 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)
|
||||||
|
|
|
@ -107,6 +107,29 @@ def test_adobe_deflate_tiff():
|
||||||
assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
|
assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
|
||||||
assert_no_exception(lambda: im.load())
|
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():
|
def test_g3_compression():
|
||||||
i = Image.open('Tests/images/lena_g4_500.tif')
|
i = Image.open('Tests/images/lena_g4_500.tif')
|
||||||
|
@ -116,7 +139,7 @@ def test_g3_compression():
|
||||||
reread = Image.open(out)
|
reread = Image.open(out)
|
||||||
assert_equal(reread.info['compression'], 'group3')
|
assert_equal(reread.info['compression'], 'group3')
|
||||||
assert_image_equal(reread, i)
|
assert_image_equal(reread, i)
|
||||||
|
|
||||||
def test_little_endian():
|
def test_little_endian():
|
||||||
im = Image.open('Tests/images/16bit.deflate.tif')
|
im = Image.open('Tests/images/16bit.deflate.tif')
|
||||||
assert_equal(im.getpixel((0,0)), 480)
|
assert_equal(im.getpixel((0,0)), 480)
|
||||||
|
|
80
Tests/test_file_tiff_metadata.py
Normal file
80
Tests/test_file_tiff_metadata.py
Normal 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)
|
19
_imaging.c
19
_imaging.c
|
@ -71,7 +71,7 @@
|
||||||
* See the README file for information on usage and redistribution.
|
* 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"
|
#include "Python.h"
|
||||||
|
|
||||||
|
@ -1234,7 +1234,8 @@ static PyObject*
|
||||||
_putdata(ImagingObject* self, PyObject* args)
|
_putdata(ImagingObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
Imaging image;
|
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;
|
PyObject* data;
|
||||||
double scale = 1.0;
|
double scale = 1.0;
|
||||||
|
@ -1244,16 +1245,16 @@ _putdata(ImagingObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!PySequence_Check(data)) {
|
if (!PySequence_Check(data)) {
|
||||||
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
image = self->image;
|
image = self->image;
|
||||||
|
|
||||||
n = PyObject_Length(data);
|
n = PyObject_Length(data);
|
||||||
if (n > (int) (image->xsize * image->ysize)) {
|
if (n > (Py_ssize_t) (image->xsize * image->ysize)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "too many data entries");
|
PyErr_SetString(PyExc_TypeError, "too many data entries");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image->image8) {
|
if (image->image8) {
|
||||||
|
@ -1648,7 +1649,7 @@ _stretch(ImagingObject* self, PyObject* args)
|
||||||
imIn = self->image;
|
imIn = self->image;
|
||||||
|
|
||||||
/* two-pass resize: minimize size of intermediate 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);
|
imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize);
|
||||||
else
|
else
|
||||||
imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
|
imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
|
||||||
|
@ -3073,7 +3074,7 @@ image_length(ImagingObject *self)
|
||||||
{
|
{
|
||||||
Imaging im = self->image;
|
Imaging im = self->image;
|
||||||
|
|
||||||
return im->xsize * im->ysize;
|
return (Py_ssize_t) im->xsize * im->ysize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -59,7 +59,11 @@ struct {
|
||||||
const char* message;
|
const char* message;
|
||||||
} ft_errors[] =
|
} ft_errors[] =
|
||||||
|
|
||||||
|
#if defined(USE_FREETYPE_2_1)
|
||||||
|
#include FT_ERRORS_H
|
||||||
|
#else
|
||||||
#include <freetype/fterrors.h>
|
#include <freetype/fterrors.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* font objects */
|
/* font objects */
|
||||||
|
|
4
_webp.c
4
_webp.c
|
@ -113,11 +113,11 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
WebPMuxAssemble(mux, &output_data);
|
WebPMuxAssemble(mux, &output_data);
|
||||||
WebPMuxDelete(mux);
|
WebPMuxDelete(mux);
|
||||||
|
free(output);
|
||||||
|
|
||||||
output = (uint8_t*)output_data.bytes;
|
|
||||||
ret_size = output_data.size;
|
ret_size = output_data.size;
|
||||||
if (ret_size > 0) {
|
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);
|
WebPDataClear(&output_data);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
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.
|
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
|
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
|
tuples. Note that only short, long and ASCII tags are correctly unpacked by
|
||||||
this release.
|
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
|
WebP
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
@ -273,19 +338,19 @@ format are currently undocumented.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
|
|
||||||
**lossless**
|
**lossless**
|
||||||
If present, instructs the WEBP writer to use lossless
|
If present, instructs the WEBP writer to use lossless
|
||||||
compression.
|
compression.
|
||||||
|
|
||||||
**quality**
|
**quality**
|
||||||
Integer, 1-100, Defaults to 80. Sets the quality level for
|
Integer, 1-100, Defaults to 80. Sets the quality level for
|
||||||
lossy compression.
|
lossy compression.
|
||||||
|
|
||||||
**icc_procfile**
|
**icc_procfile**
|
||||||
The ICC Profile to include in the saved file. Only supported if
|
The ICC Profile to include in the saved file. Only supported if
|
||||||
the system webp library was built with webpmux support.
|
the system webp library was built with webpmux support.
|
||||||
|
|
||||||
**exif**
|
**exif**
|
||||||
The exif data to include in the saved file. Only supported if
|
The exif data to include in the saved file. Only supported if
|
||||||
the system webp library was built with webpmux support.
|
the system webp library was built with webpmux support.
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
Writing your own file decoder
|
Writing your own file decoder
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
The Python Imaging Library uses a plug-in model which allows you to add your
|
The Python Imaging Library uses a plug-in model which allows you to
|
||||||
own decoders to the library, without any changes to the library itself. Such
|
add your own decoders to the library, without any changes to the
|
||||||
plug-ins have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique
|
library itself. Such plug-ins usually have names like
|
||||||
format name (usually an abbreviation).
|
: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
|
A decoder plug-in should contain a decoder class, based on the
|
||||||
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
|
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
|
||||||
|
|
|
@ -60,9 +60,13 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
* **littlecms** provides color management
|
* **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.
|
* **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.
|
* **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::
|
Prerequisites are installed on **Ubuntu 10.04 LTS** with::
|
||||||
|
|
||||||
$ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \
|
$ 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
|
Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy
|
||||||
7.0** with::
|
7.0** with::
|
||||||
|
|
||||||
$ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \
|
$ 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
|
Mac OS X installation
|
||||||
---------------------
|
---------------------
|
||||||
|
|
|
@ -15,3 +15,9 @@ to this::
|
||||||
The :py:mod:`_imaging` module has been moved. You can now import it like this::
|
The :py:mod:`_imaging` module has been moved. You can now import it like this::
|
||||||
|
|
||||||
from PIL.Image import core as _imaging
|
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.
|
||||||
|
|
||||||
|
|
9
setup.py
9
setup.py
|
@ -82,7 +82,7 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
NAME = 'Pillow'
|
NAME = 'Pillow'
|
||||||
VERSION = '2.2.1'
|
VERSION = '2.3.0'
|
||||||
TCL_ROOT = None
|
TCL_ROOT = None
|
||||||
JPEG_ROOT = None
|
JPEG_ROOT = None
|
||||||
ZLIB_ROOT = None
|
ZLIB_ROOT = None
|
||||||
|
@ -211,6 +211,10 @@ class pil_build_ext(build_ext):
|
||||||
# work ;-)
|
# work ;-)
|
||||||
self.add_multiarch_paths()
|
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")
|
_add_directory(library_dirs, "/usr/local/lib")
|
||||||
# FIXME: check /opt/stuff directories here?
|
# FIXME: check /opt/stuff directories here?
|
||||||
|
|
||||||
|
@ -583,8 +587,7 @@ setup(
|
||||||
description='Python Imaging Library (Fork)',
|
description='Python Imaging Library (Fork)',
|
||||||
long_description=(
|
long_description=(
|
||||||
_read('README.rst') + b'\n' +
|
_read('README.rst') + b'\n' +
|
||||||
_read('CHANGES.rst') + b'\n' +
|
_read('CHANGES.rst')),
|
||||||
_read('CONTRIBUTORS.rst')).decode('utf-8'),
|
|
||||||
author='Alex Clark (fork author)',
|
author='Alex Clark (fork author)',
|
||||||
author_email='aclark@aclark.net',
|
author_email='aclark@aclark.net',
|
||||||
url='http://python-imaging.github.io/',
|
url='http://python-imaging.github.io/',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user