mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-13 05:06:49 +03:00
Merge from master
This commit is contained in:
commit
886b075526
|
@ -10,7 +10,7 @@ python:
|
||||||
- 3.2
|
- 3.2
|
||||||
- 3.3
|
- 3.3
|
||||||
|
|
||||||
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4"
|
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript""
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- python setup.py clean
|
- python setup.py clean
|
||||||
|
|
|
@ -4,6 +4,9 @@ Changelog (Pillow)
|
||||||
2.3.0 (2014-01-01)
|
2.3.0 (2014-01-01)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Adds directories for NetBSD.
|
||||||
|
[deepy]
|
||||||
|
|
||||||
- Support RGBA TIFF with missing ExtraSamples tag
|
- Support RGBA TIFF with missing ExtraSamples tag
|
||||||
[cgohlke]
|
[cgohlke]
|
||||||
|
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -475,7 +475,7 @@ class Image:
|
||||||
new.mode = im.mode
|
new.mode = im.mode
|
||||||
new.size = im.size
|
new.size = im.size
|
||||||
new.palette = self.palette
|
new.palette = self.palette
|
||||||
if im.mode == "P":
|
if im.mode == "P" and not new.palette:
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
new.palette = ImagePalette.ImagePalette()
|
new.palette = ImagePalette.ImagePalette()
|
||||||
try:
|
try:
|
||||||
|
|
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/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 |
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)
|
||||||
|
|
|
@ -27,3 +27,22 @@ def test_optimize():
|
||||||
return len(file.getvalue())
|
return len(file.getvalue())
|
||||||
assert_equal(test(0), 800)
|
assert_equal(test(0), 800)
|
||||||
assert_equal(test(1), 38)
|
assert_equal(test(1), 38)
|
||||||
|
|
||||||
|
def test_roundtrip():
|
||||||
|
out = tempfile('temp.gif')
|
||||||
|
im = lena()
|
||||||
|
im.save(out)
|
||||||
|
reread = Image.open(out)
|
||||||
|
|
||||||
|
assert_image_similar(reread.convert('RGB'), im, 50)
|
||||||
|
|
||||||
|
def test_roundtrip2():
|
||||||
|
#see https://github.com/python-imaging/Pillow/issues/403
|
||||||
|
out = 'temp.gif'#tempfile('temp.gif')
|
||||||
|
im = Image.open('Images/lena.gif')
|
||||||
|
im2 = im.copy()
|
||||||
|
im2.save(out)
|
||||||
|
reread = Image.open(out)
|
||||||
|
|
||||||
|
assert_image_similar(reread.convert('RGB'), lena(), 50)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
|
@ -273,19 +287,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.
|
||||||
|
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -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?
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user