mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge branch 'master' into pre-commit-diff
This commit is contained in:
commit
2d3fd6ea02
|
@ -5,6 +5,15 @@ Changelog (Pillow)
|
|||
7.2.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Deprecated _showxv #4714
|
||||
[radarhere]
|
||||
|
||||
- Deprecate Image.show(command="...") #4646
|
||||
[nulano, hugovk, radarhere]
|
||||
|
||||
- Updated JPEG magic number #4707
|
||||
[Cykooz, radarhere]
|
||||
|
||||
- Change STRIPBYTECOUNTS to LONG if necessary when saving #4626
|
||||
[radarhere, hugovk]
|
||||
|
||||
|
|
|
@ -165,12 +165,6 @@ def assert_tuple_approx_equal(actuals, targets, threshold, msg):
|
|||
assert value, msg + ": " + repr(actuals) + " != " + repr(targets)
|
||||
|
||||
|
||||
def skip_known_bad_test(msg=None):
|
||||
# Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
||||
if not os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
|
||||
pytest.skip(msg or "Known bad test")
|
||||
|
||||
|
||||
def skip_unless_feature(feature):
|
||||
reason = "%s not available" % feature
|
||||
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
||||
|
|
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 B |
|
@ -3,7 +3,14 @@ import re
|
|||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, features
|
||||
from PIL import (
|
||||
ExifTags,
|
||||
Image,
|
||||
ImageFile,
|
||||
JpegImagePlugin,
|
||||
UnidentifiedImageError,
|
||||
features,
|
||||
)
|
||||
|
||||
from .helper import (
|
||||
assert_image,
|
||||
|
@ -709,6 +716,24 @@ class TestFileJpeg:
|
|||
with Image.open("Tests/images/icc-after-SOF.jpg") as im:
|
||||
assert im.info["icc_profile"] == b"profile"
|
||||
|
||||
def test_jpeg_magic_number(self):
|
||||
size = 4097
|
||||
buffer = BytesIO(b"\xFF" * size) # Many xFF bytes
|
||||
buffer.max_pos = 0
|
||||
orig_read = buffer.read
|
||||
|
||||
def read(n=-1):
|
||||
res = orig_read(n)
|
||||
buffer.max_pos = max(buffer.max_pos, buffer.tell())
|
||||
return res
|
||||
|
||||
buffer.read = read
|
||||
with pytest.raises(UnidentifiedImageError):
|
||||
Image.open(buffer)
|
||||
|
||||
# Assert the entire file has not been read
|
||||
assert 0 < buffer.max_pos < size
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
||||
@skip_unless_feature("jpg")
|
||||
|
|
|
@ -207,6 +207,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
del core_items[tag]
|
||||
except KeyError:
|
||||
pass
|
||||
del core_items[320] # colormap is special, tested below
|
||||
|
||||
# Type codes:
|
||||
# 2: "ascii",
|
||||
|
@ -491,6 +492,18 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open(out) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
|
||||
def test_palette_save(self, tmp_path):
|
||||
im = hopper("P")
|
||||
out = str(tmp_path / "temp.tif")
|
||||
|
||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||
im.save(out)
|
||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
# colormap/palette tag
|
||||
assert len(reloaded.tag_v2[320]) == 768
|
||||
|
||||
def xtest_bw_compression_w_rgb(self, tmp_path):
|
||||
""" This test passes, but when running all tests causes a failure due
|
||||
to output on stderr from the error thrown by libtiff. We need to
|
||||
|
|
|
@ -4,13 +4,7 @@ import subprocess
|
|||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from .helper import (
|
||||
IMCONVERT,
|
||||
assert_image_equal,
|
||||
hopper,
|
||||
imagemagick_available,
|
||||
skip_known_bad_test,
|
||||
)
|
||||
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
|
||||
|
||||
_roundtrip = imagemagick_available()
|
||||
|
||||
|
@ -62,13 +56,13 @@ def test_monochrome(tmp_path):
|
|||
roundtrip(tmp_path, mode)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="Palm P image is wrong")
|
||||
def test_p_mode(tmp_path):
|
||||
# Arrange
|
||||
mode = "P"
|
||||
|
||||
# Act / Assert
|
||||
helper_save_as_palm(tmp_path, mode)
|
||||
skip_known_bad_test("Palm P image is wrong")
|
||||
roundtrip(tmp_path, mode)
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import tempfile
|
|||
|
||||
import PIL
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError
|
||||
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -585,6 +585,22 @@ class TestImage:
|
|||
expected = Image.new(mode, (100, 100), color)
|
||||
assert_image_equal(im.convert(mode), expected)
|
||||
|
||||
def test_showxv_deprecation(self):
|
||||
class TestViewer(ImageShow.Viewer):
|
||||
def show_image(self, image, **options):
|
||||
return True
|
||||
|
||||
viewer = TestViewer()
|
||||
ImageShow.register(viewer, -1)
|
||||
|
||||
im = Image.new("RGB", (50, 50), "white")
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
Image._showxv(im)
|
||||
|
||||
# Restore original state
|
||||
ImageShow._viewers.pop(0)
|
||||
|
||||
def test_no_resource_warning_on_save(self, tmp_path):
|
||||
# https://github.com/python-pillow/Pillow/issues/835
|
||||
# Arrange
|
||||
|
@ -664,6 +680,18 @@ class TestImage:
|
|||
except OSError as e:
|
||||
assert str(e) == "buffer overrun when reading image file"
|
||||
|
||||
def test_show_deprecation(self, monkeypatch):
|
||||
monkeypatch.setattr(Image, "_show", lambda *args, **kwargs: None)
|
||||
|
||||
im = Image.new("RGB", (50, 50), "white")
|
||||
|
||||
with pytest.warns(None) as raised:
|
||||
im.show()
|
||||
assert not raised
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
im.show(command="mock")
|
||||
|
||||
|
||||
class MockEncoder:
|
||||
pass
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
from PIL import Image, ImageMath, ImageMode
|
||||
|
||||
from .helper import convert_to_comparable
|
||||
from .helper import convert_to_comparable, skip_unless_feature
|
||||
|
||||
codecs = dir(Image.core)
|
||||
|
||||
|
@ -254,9 +254,7 @@ def test_mode_F():
|
|||
compare_reduce_with_box(im, factor)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"jpeg2k_decoder" not in codecs, reason="JPEG 2000 support not available"
|
||||
)
|
||||
@skip_unless_feature("jpg_2000")
|
||||
def test_jpeg2k():
|
||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||
assert im.reduce(2).size == (320, 240)
|
||||
|
|
|
@ -218,7 +218,7 @@ class TestImagingCoreResampleAccuracy:
|
|||
assert_image_equal(im, ref)
|
||||
|
||||
|
||||
class CoreResampleConsistencyTest:
|
||||
class TestCoreResampleConsistency:
|
||||
def make_case(self, mode, fill):
|
||||
im = Image.new(mode, (512, 9), fill)
|
||||
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
||||
|
@ -253,7 +253,7 @@ class CoreResampleConsistencyTest:
|
|||
self.run_case(self.make_case("F", 1.192093e-07))
|
||||
|
||||
|
||||
class CoreResampleAlphaCorrectTest:
|
||||
class TestCoreResampleAlphaCorrect:
|
||||
def make_levels_case(self, mode):
|
||||
i = Image.new(mode, (256, 16))
|
||||
px = i.load()
|
||||
|
@ -274,7 +274,7 @@ class CoreResampleAlphaCorrectTest:
|
|||
len(used_colors), y
|
||||
)
|
||||
|
||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
||||
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||
def test_levels_rgba(self):
|
||||
case = self.make_levels_case("RGBA")
|
||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||
|
@ -283,7 +283,7 @@ class CoreResampleAlphaCorrectTest:
|
|||
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
||||
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
||||
|
||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
||||
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||
def test_levels_la(self):
|
||||
case = self.make_levels_case("LA")
|
||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||
|
@ -329,7 +329,7 @@ class CoreResampleAlphaCorrectTest:
|
|||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
||||
|
||||
|
||||
class CoreResamplePassesTest:
|
||||
class TestCoreResamplePasses:
|
||||
@contextmanager
|
||||
def count(self, diff):
|
||||
count = Image.core.get_stats()["new_count"]
|
||||
|
@ -372,7 +372,7 @@ class CoreResamplePassesTest:
|
|||
assert_image_similar(with_box, cropped, 0.1)
|
||||
|
||||
|
||||
class CoreResampleCoefficientsTest:
|
||||
class TestCoreResampleCoefficients:
|
||||
def test_reduce(self):
|
||||
test_color = 254
|
||||
|
||||
|
@ -401,7 +401,7 @@ class CoreResampleCoefficientsTest:
|
|||
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
||||
|
||||
|
||||
class CoreResampleBoxTest:
|
||||
class TestCoreResampleBox:
|
||||
def test_wrong_arguments(self):
|
||||
im = hopper()
|
||||
for resample in (
|
||||
|
|
|
@ -5,7 +5,7 @@ from PIL import Image, ImageColor, ImageDraw, ImageFont
|
|||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
hopper,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
@ -71,7 +71,7 @@ def helper_arc(bbox, start, end):
|
|||
draw.arc(bbox, start, end)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1)
|
||||
|
||||
|
||||
def test_arc1():
|
||||
|
@ -110,20 +110,19 @@ def test_arc_no_loops():
|
|||
draw.arc(BBOX1, start=start, end=end)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1)
|
||||
|
||||
|
||||
def test_arc_width():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_arc_width.png"
|
||||
|
||||
# Act
|
||||
draw.arc(BBOX1, 10, 260, width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1)
|
||||
|
||||
|
||||
def test_arc_width_pieslice_large():
|
||||
|
@ -131,26 +130,24 @@ def test_arc_width_pieslice_large():
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_arc_width_pieslice.png"
|
||||
|
||||
# Act
|
||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1)
|
||||
|
||||
|
||||
def test_arc_width_fill():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_arc_width_fill.png"
|
||||
|
||||
# Act
|
||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1)
|
||||
|
||||
|
||||
def test_arc_width_non_whole_angle():
|
||||
|
@ -163,7 +160,7 @@ def test_arc_width_non_whole_angle():
|
|||
draw.arc(BBOX1, 10, 259.5, width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
def test_bitmap():
|
||||
|
@ -190,7 +187,7 @@ def helper_chord(mode, bbox, start, end):
|
|||
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
def test_chord1():
|
||||
|
@ -209,26 +206,24 @@ def test_chord_width():
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_chord_width.png"
|
||||
|
||||
# Act
|
||||
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1)
|
||||
|
||||
|
||||
def test_chord_width_fill():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_chord_width_fill.png"
|
||||
|
||||
# Act
|
||||
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1)
|
||||
|
||||
|
||||
def test_chord_zero_width():
|
||||
|
@ -254,7 +249,7 @@ def helper_ellipse(mode, bbox):
|
|||
draw.ellipse(bbox, fill="green", outline="blue")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
def test_ellipse1():
|
||||
|
@ -276,8 +271,8 @@ def test_ellipse_translucent():
|
|||
draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
|
||||
|
||||
# Assert
|
||||
expected = Image.open("Tests/images/imagedraw_ellipse_translucent.png")
|
||||
assert_image_similar(im, expected, 1)
|
||||
expected = "Tests/images/imagedraw_ellipse_translucent.png"
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
def test_ellipse_edge():
|
||||
|
@ -289,7 +284,7 @@ def test_ellipse_edge():
|
|||
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
|
||||
|
||||
|
||||
def test_ellipse_symmetric():
|
||||
|
@ -304,39 +299,36 @@ def test_ellipse_width():
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_ellipse_width.png"
|
||||
|
||||
# Act
|
||||
draw.ellipse(BBOX1, outline="blue", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1)
|
||||
|
||||
|
||||
def test_ellipse_width_large():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (500, 500))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_ellipse_width_large.png"
|
||||
|
||||
# Act
|
||||
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1)
|
||||
|
||||
|
||||
def test_ellipse_width_fill():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_ellipse_width_fill.png"
|
||||
|
||||
# Act
|
||||
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1)
|
||||
|
||||
|
||||
def test_ellipse_zero_width():
|
||||
|
@ -423,7 +415,7 @@ def helper_pieslice(bbox, start, end):
|
|||
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1)
|
||||
|
||||
|
||||
def test_pieslice1():
|
||||
|
@ -440,13 +432,12 @@ def test_pieslice_width():
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_pieslice_width.png"
|
||||
|
||||
# Act
|
||||
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1)
|
||||
|
||||
|
||||
def test_pieslice_width_fill():
|
||||
|
@ -459,7 +450,7 @@ def test_pieslice_width_fill():
|
|||
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
def test_pieslice_zero_width():
|
||||
|
@ -571,13 +562,12 @@ def test_big_rectangle():
|
|||
im = Image.new("RGB", (W, H))
|
||||
bbox = [(-1, -1), (W + 1, H + 1)]
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_big_rectangle.png"
|
||||
|
||||
# Act
|
||||
draw.rectangle(bbox, fill="orange")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1)
|
||||
|
||||
|
||||
def test_rectangle_width():
|
||||
|
@ -878,13 +868,25 @@ def test_wide_line_dot():
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_wide_line_dot.png"
|
||||
|
||||
# Act
|
||||
draw.line([(50, 50), (50, 50)], width=3)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_wide_line_dot.png", 1)
|
||||
|
||||
|
||||
def test_wide_line_larger_than_int():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_wide_line_larger_than_int.png"
|
||||
|
||||
# Act
|
||||
draw.line([(0, 0), (32768, 32768)], width=3)
|
||||
|
||||
# Assert
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -971,13 +973,12 @@ def test_wide_line_dot():
|
|||
def test_line_joint(xy):
|
||||
im = Image.new("RGB", (500, 325))
|
||||
draw = ImageDraw.Draw(im)
|
||||
expected = "Tests/images/imagedraw_line_joint_curve.png"
|
||||
|
||||
# Act
|
||||
draw.line(xy, GRAY, 50, "curve")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(im, Image.open(expected), 3)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_line_joint_curve.png", 3)
|
||||
|
||||
|
||||
def test_textsize_empty_string():
|
||||
|
@ -1018,8 +1019,8 @@ def test_stroke():
|
|||
draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1
|
||||
assert_image_similar_tofile(
|
||||
im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1
|
||||
)
|
||||
|
||||
|
||||
|
@ -1034,9 +1035,7 @@ def test_stroke_descender():
|
|||
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
|
||||
|
||||
# Assert
|
||||
assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_stroke_descender.png"), 6.76
|
||||
)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76)
|
||||
|
||||
|
||||
@skip_unless_feature("freetype2")
|
||||
|
@ -1052,9 +1051,7 @@ def test_stroke_multiline():
|
|||
)
|
||||
|
||||
# Assert
|
||||
assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3
|
||||
)
|
||||
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3)
|
||||
|
||||
|
||||
def test_same_color_outline():
|
||||
|
@ -1093,4 +1090,4 @@ def test_same_color_outline():
|
|||
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
|
||||
operation, mode
|
||||
)
|
||||
assert_image_similar(im, Image.open(expected), 1)
|
||||
assert_image_similar_tofile(im, expected, 1)
|
||||
|
|
|
@ -455,7 +455,7 @@ class TestImageFont:
|
|||
with pytest.raises(UnicodeEncodeError):
|
||||
font.getsize("’")
|
||||
|
||||
@pytest.mark.skipif(is_pypy(), reason="failing on PyPy")
|
||||
@pytest.mark.xfail(is_pypy(), reason="failing on PyPy with Raqm")
|
||||
def test_unicode_extended(self):
|
||||
# issue #3777
|
||||
text = "A\u278A\U0001F12B"
|
||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
|||
import pytest
|
||||
from PIL import Image, ImageGrab
|
||||
|
||||
from .helper import assert_image, assert_image_equal_tofile
|
||||
from .helper import assert_image, assert_image_equal_tofile, skip_unless_feature
|
||||
|
||||
|
||||
class TestImageGrab:
|
||||
|
@ -23,7 +23,7 @@ class TestImageGrab:
|
|||
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
|
||||
assert_image(im, im.mode, (40, 60))
|
||||
|
||||
@pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB")
|
||||
@skip_unless_feature("xcb")
|
||||
def test_grab_x11(self):
|
||||
try:
|
||||
if sys.platform not in ("win32", "darwin"):
|
||||
|
@ -46,7 +46,7 @@ class TestImageGrab:
|
|||
ImageGrab.grab(xdisplay="")
|
||||
assert str(e.value).startswith("Pillow was built without XCB support")
|
||||
|
||||
@pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB")
|
||||
@skip_unless_feature("xcb")
|
||||
def test_grab_invalid_xdisplay(self):
|
||||
with pytest.raises(OSError) as e:
|
||||
ImageGrab.grab(xdisplay="error.test:0.0")
|
||||
|
|
36
docs/PIL.rst
36
docs/PIL.rst
|
@ -4,6 +4,14 @@ PIL Package (autodoc of remaining modules)
|
|||
Reference for modules whose documentation has not yet been ported or written
|
||||
can be found here.
|
||||
|
||||
:mod:`PIL` Module
|
||||
-----------------
|
||||
|
||||
.. py:module:: PIL
|
||||
|
||||
.. autoexception:: UnidentifiedImageError
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`BdfFontFile` Module
|
||||
-------------------------
|
||||
|
||||
|
@ -52,21 +60,12 @@ can be found here.
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. intentionally skipped documenting this because it's not documented anywhere
|
||||
|
||||
:mod:`ImageDraw2` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: PIL.ImageDraw2
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`ImageShow` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: PIL.ImageShow
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
@ -78,14 +77,6 @@ can be found here.
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`JpegPresets` Module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: PIL.JpegPresets
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`PaletteFile` Module
|
||||
-------------------------
|
||||
|
||||
|
@ -140,12 +131,3 @@ can be found here.
|
|||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`_binary` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: PIL._binary
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import sphinx_rtd_theme
|
|||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
needs_sphinx = "2.4"
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
|
|
|
@ -12,6 +12,23 @@ Deprecated features
|
|||
Below are features which are considered deprecated. Where appropriate,
|
||||
a ``DeprecationWarning`` is issued.
|
||||
|
||||
Image.show command parameter
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.2.0
|
||||
|
||||
The ``command`` parameter was deprecated and will be removed in a future release.
|
||||
Use a subclass of ``ImageShow.Viewer`` instead.
|
||||
|
||||
Image._showxv
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.2.0
|
||||
|
||||
``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show`
|
||||
instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add
|
||||
a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||
|
||||
ImageFile.raise_ioerror
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -249,8 +249,8 @@ class DXT1Decoder(ImageFile.PyDecoder):
|
|||
def decode(self, buffer):
|
||||
try:
|
||||
self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize))
|
||||
except struct.error:
|
||||
raise OSError("Truncated DDS file")
|
||||
except struct.error as e:
|
||||
raise OSError("Truncated DDS file") from e
|
||||
return 0, 0
|
||||
|
||||
|
||||
|
@ -260,8 +260,8 @@ class DXT5Decoder(ImageFile.PyDecoder):
|
|||
def decode(self, buffer):
|
||||
try:
|
||||
self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize))
|
||||
except struct.error:
|
||||
raise OSError("Truncated DDS file")
|
||||
except struct.error as e:
|
||||
raise OSError("Truncated DDS file") from e
|
||||
return 0, 0
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ Over 30 different file formats can be identified and read by the library.
|
|||
Write support is less extensive, but most common interchange and presentation
|
||||
formats are supported.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` function identifies files from their
|
||||
The :py:meth:`~PIL.Image.open` function identifies files from their
|
||||
contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method
|
||||
looks at the name to determine which format to use, unless the format is given
|
||||
explicitly.
|
||||
|
@ -25,7 +25,7 @@ Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P`
|
|||
or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
|
||||
is not supported.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**compression**
|
||||
|
@ -74,7 +74,7 @@ are used or GIF89a is already in use.
|
|||
Note that GIF files are always read as grayscale (``L``)
|
||||
or palette mode (``P``) images.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**background**
|
||||
|
@ -203,7 +203,7 @@ ICNS
|
|||
Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the
|
||||
largest available icon is read, though you can override this by setting the
|
||||
:py:attr:`~PIL.Image.Image.size` property before calling
|
||||
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method
|
||||
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.open` method
|
||||
sets the following :py:attr:`~PIL.Image.Image.info` property:
|
||||
|
||||
**sizes**
|
||||
|
@ -257,7 +257,7 @@ Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
|
|||
converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
|
||||
their original size while loading them.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method may set the following
|
||||
The :py:meth:`~PIL.Image.open` method may set the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties if available:
|
||||
|
||||
**jfif**
|
||||
|
@ -697,7 +697,7 @@ Pillow also reads SPIDER stack files containing sequences of SPIDER images. The
|
|||
:py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell` methods are supported, and
|
||||
random access is allowed.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following attributes:
|
||||
The :py:meth:`~PIL.Image.open` method sets the following attributes:
|
||||
|
||||
**format**
|
||||
Set to ``SPIDER``
|
||||
|
@ -750,7 +750,7 @@ uncompressed files.
|
|||
support for reading Packbits, LZW and JPEG compressed TIFFs
|
||||
without using libtiff.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**compression**
|
||||
|
@ -1021,7 +1021,7 @@ FLI, FLC
|
|||
|
||||
Pillow reads Autodesk FLI and FLC animations.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**duration**
|
||||
|
@ -1054,7 +1054,7 @@ GBR
|
|||
|
||||
The GBR decoder reads GIMP brush files, version 1 and 2.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**comment**
|
||||
|
@ -1069,7 +1069,7 @@ GD
|
|||
Pillow reads uncompressed GD2 files. Note that you must use
|
||||
:py:func:`PIL.GdImageFile.open` to read such a file.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**transparency**
|
||||
|
@ -1185,7 +1185,7 @@ XPM
|
|||
|
||||
Pillow reads X pixmap files (mode ``P``) with 256 colors or less.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
The :py:meth:`~PIL.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**transparency**
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
The :py:mod:`ExifTags` module exposes two dictionaries which
|
||||
provide constants and clear-text names for various well-known EXIF tags.
|
||||
|
||||
.. py:class:: PIL.ExifTags.TAGS
|
||||
.. py:data:: TAGS
|
||||
:type: dict
|
||||
|
||||
The TAG dictionary maps 16-bit integer EXIF tag enumerations to
|
||||
descriptive string names. For instance:
|
||||
|
@ -16,7 +17,8 @@ provide constants and clear-text names for various well-known EXIF tags.
|
|||
>>> TAGS[0x010e]
|
||||
'ImageDescription'
|
||||
|
||||
.. py:class:: PIL.ExifTags.GPSTAGS
|
||||
.. py:data:: GPSTAGS
|
||||
:type: dict
|
||||
|
||||
The GPSTAGS dictionary maps 8-bit integer EXIF gps enumerations to
|
||||
descriptive string names. For instance:
|
||||
|
|
|
@ -76,9 +76,16 @@ Constructing images
|
|||
.. autofunction:: new
|
||||
.. autofunction:: fromarray
|
||||
.. autofunction:: frombytes
|
||||
.. autofunction:: fromstring
|
||||
.. autofunction:: frombuffer
|
||||
|
||||
Generating images
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autofunction:: effect_mandelbrot
|
||||
.. autofunction:: effect_noise
|
||||
.. autofunction:: linear_gradient
|
||||
.. autofunction:: radial_gradient
|
||||
|
||||
Registering plugins
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -88,12 +95,14 @@ Registering plugins
|
|||
ignore them.
|
||||
|
||||
.. autofunction:: register_open
|
||||
.. autofunction:: register_decoder
|
||||
.. autofunction:: register_mime
|
||||
.. autofunction:: register_save
|
||||
.. autofunction:: register_encoder
|
||||
.. autofunction:: register_save_all
|
||||
.. autofunction:: register_extension
|
||||
|
||||
.. autofunction:: register_extensions
|
||||
.. autofunction:: registered_extensions
|
||||
.. autofunction:: register_decoder
|
||||
.. autofunction:: register_encoder
|
||||
|
||||
The Image Class
|
||||
---------------
|
||||
|
@ -140,6 +149,8 @@ This crops the input image with the provided coordinates:
|
|||
|
||||
|
||||
.. automethod:: PIL.Image.Image.draft
|
||||
.. automethod:: PIL.Image.Image.effect_spread
|
||||
.. automethod:: PIL.Image.Image.entropy
|
||||
.. automethod:: PIL.Image.Image.filter
|
||||
|
||||
This blurs the input image using a filter from the ``ImageFilter`` module:
|
||||
|
@ -176,12 +187,14 @@ This helps to get the bounding box coordinates of the input image:
|
|||
print(im.getbbox())
|
||||
# Returns four coordinates in the format (left, upper, right, lower)
|
||||
|
||||
.. automethod:: PIL.Image.Image.getchannel
|
||||
.. automethod:: PIL.Image.Image.getcolors
|
||||
.. automethod:: PIL.Image.Image.getdata
|
||||
.. automethod:: PIL.Image.Image.getextrema
|
||||
.. automethod:: PIL.Image.Image.getexif
|
||||
.. automethod:: PIL.Image.Image.getextrema
|
||||
.. automethod:: PIL.Image.Image.getpalette
|
||||
.. automethod:: PIL.Image.Image.getpixel
|
||||
.. automethod:: PIL.Image.Image.getprojection
|
||||
.. automethod:: PIL.Image.Image.histogram
|
||||
.. automethod:: PIL.Image.Image.offset
|
||||
.. automethod:: PIL.Image.Image.paste
|
||||
|
@ -191,6 +204,8 @@ This helps to get the bounding box coordinates of the input image:
|
|||
.. automethod:: PIL.Image.Image.putpalette
|
||||
.. automethod:: PIL.Image.Image.putpixel
|
||||
.. automethod:: PIL.Image.Image.quantize
|
||||
.. automethod:: PIL.Image.Image.reduce
|
||||
.. automethod:: PIL.Image.Image.remap_palette
|
||||
.. automethod:: PIL.Image.Image.resize
|
||||
|
||||
This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``:
|
||||
|
@ -205,7 +220,6 @@ This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``
|
|||
(width, height) = (im.width // 2, im.height // 2)
|
||||
im_resized = im.resize((width, height))
|
||||
|
||||
.. automethod:: PIL.Image.Image.remap_palette
|
||||
.. automethod:: PIL.Image.Image.rotate
|
||||
|
||||
This rotates the input image by ``theta`` degrees counter clockwise:
|
||||
|
@ -225,7 +239,6 @@ This rotates the input image by ``theta`` degrees counter clockwise:
|
|||
.. automethod:: PIL.Image.Image.seek
|
||||
.. automethod:: PIL.Image.Image.show
|
||||
.. automethod:: PIL.Image.Image.split
|
||||
.. automethod:: PIL.Image.Image.getchannel
|
||||
.. automethod:: PIL.Image.Image.tell
|
||||
.. automethod:: PIL.Image.Image.thumbnail
|
||||
.. automethod:: PIL.Image.Image.tobitmap
|
||||
|
|
|
@ -124,7 +124,7 @@ Example: Draw Multiline Text
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. py:class:: PIL.ImageDraw.Draw(im, mode=None)
|
||||
.. py:method:: Draw(im, mode=None)
|
||||
|
||||
Creates an object that can be used to draw in the given image.
|
||||
|
||||
|
@ -140,13 +140,13 @@ Functions
|
|||
Methods
|
||||
-------
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.getfont()
|
||||
.. py:method:: ImageDraw.getfont()
|
||||
|
||||
Get the current default font.
|
||||
|
||||
:returns: An image font.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.arc(xy, start, end, fill=None, width=0)
|
||||
.. py:method:: ImageDraw.arc(xy, start, end, fill=None, width=0)
|
||||
|
||||
Draws an arc (a portion of a circle outline) between the start and end
|
||||
angles, inside the given bounding box.
|
||||
|
@ -162,7 +162,7 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.bitmap(xy, bitmap, fill=None)
|
||||
.. py:method:: ImageDraw.bitmap(xy, bitmap, fill=None)
|
||||
|
||||
Draws a bitmap (mask) at the given position, using the current fill color
|
||||
for the non-zero portions. The bitmap should be a valid transparency mask
|
||||
|
@ -173,7 +173,7 @@ Methods
|
|||
To paste pixel data into an image, use the
|
||||
:py:meth:`~PIL.Image.Image.paste` method on the image itself.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1)
|
||||
.. py:method:: ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1)
|
||||
|
||||
Same as :py:meth:`~PIL.ImageDraw.ImageDraw.arc`, but connects the end points
|
||||
with a straight line.
|
||||
|
@ -187,7 +187,7 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.ellipse(xy, fill=None, outline=None, width=1)
|
||||
.. py:method:: ImageDraw.ellipse(xy, fill=None, outline=None, width=1)
|
||||
|
||||
Draws an ellipse inside the given bounding box.
|
||||
|
||||
|
@ -200,9 +200,9 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0, joint=None)
|
||||
.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
|
||||
|
||||
Draws a line between the coordinates in the **xy** list.
|
||||
Draws a line between the coordinates in the ``xy`` list.
|
||||
|
||||
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
|
||||
numeric values like ``[x, y, x, y, ...]``.
|
||||
|
@ -216,7 +216,7 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1)
|
||||
.. py:method:: ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1)
|
||||
|
||||
Same as arc, but also draws straight lines between the end points and the
|
||||
center of the bounding box.
|
||||
|
@ -233,7 +233,7 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.point(xy, fill=None)
|
||||
.. py:method:: ImageDraw.point(xy, fill=None)
|
||||
|
||||
Draws points (individual pixels) at the given coordinates.
|
||||
|
||||
|
@ -241,7 +241,7 @@ Methods
|
|||
numeric values like ``[x, y, x, y, ...]``.
|
||||
:param fill: Color to use for the point.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.polygon(xy, fill=None, outline=None)
|
||||
.. py:method:: ImageDraw.polygon(xy, fill=None, outline=None)
|
||||
|
||||
Draws a polygon.
|
||||
|
||||
|
@ -254,7 +254,7 @@ Methods
|
|||
:param outline: Color to use for the outline.
|
||||
:param fill: Color to use for the fill.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
||||
.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
||||
|
||||
Draws a rectangle.
|
||||
|
||||
|
@ -267,13 +267,13 @@ Methods
|
|||
|
||||
.. versionadded:: 5.3.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.shape(shape, fill=None, outline=None)
|
||||
.. py:method:: ImageDraw.shape(shape, fill=None, outline=None)
|
||||
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
Draw a shape.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None)
|
||||
.. py:method:: ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None)
|
||||
|
||||
Draws the string at the given position.
|
||||
|
||||
|
@ -325,7 +325,7 @@ Methods
|
|||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None)
|
||||
.. py:method:: ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None)
|
||||
|
||||
Draws the string at the given position.
|
||||
|
||||
|
@ -362,7 +362,7 @@ Methods
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
|
@ -401,7 +401,7 @@ Methods
|
|||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
|
@ -439,7 +439,7 @@ Methods
|
|||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None)
|
||||
.. py:method:: getdraw(im=None, hints=None)
|
||||
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
|
@ -450,7 +450,7 @@ Methods
|
|||
:param hints: An optional list of hints.
|
||||
:returns: A (drawing context, drawing resource factory) tuple.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.floodfill(image, xy, value, border=None, thresh=0)
|
||||
.. py:method:: floodfill(image, xy, value, border=None, thresh=0)
|
||||
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ Classes
|
|||
All enhancement classes implement a common interface, containing a single
|
||||
method:
|
||||
|
||||
.. py:class:: PIL.ImageEnhance._Enhance
|
||||
.. py:class:: _Enhance
|
||||
|
||||
.. py:method:: enhance(factor)
|
||||
|
||||
Returns an enhanced image.
|
||||
|
@ -40,7 +41,7 @@ method:
|
|||
etc), and higher values more. There are no restrictions
|
||||
on this value.
|
||||
|
||||
.. py:class:: PIL.ImageEnhance.Color(image)
|
||||
.. py:class:: Color(image)
|
||||
|
||||
Adjust image color balance.
|
||||
|
||||
|
@ -49,7 +50,7 @@ method:
|
|||
factor of 0.0 gives a black and white image. A factor of 1.0 gives
|
||||
the original image.
|
||||
|
||||
.. py:class:: PIL.ImageEnhance.Contrast(image)
|
||||
.. py:class:: Contrast(image)
|
||||
|
||||
Adjust image contrast.
|
||||
|
||||
|
@ -57,7 +58,7 @@ method:
|
|||
to the contrast control on a TV set. An enhancement factor of 0.0
|
||||
gives a solid grey image. A factor of 1.0 gives the original image.
|
||||
|
||||
.. py:class:: PIL.ImageEnhance.Brightness(image)
|
||||
.. py:class:: Brightness(image)
|
||||
|
||||
Adjust image brightness.
|
||||
|
||||
|
@ -65,7 +66,7 @@ method:
|
|||
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
|
||||
original image.
|
||||
|
||||
.. py:class:: PIL.ImageEnhance.Sharpness(image)
|
||||
.. py:class:: Sharpness(image)
|
||||
|
||||
Adjust image sharpness.
|
||||
|
||||
|
|
27
docs/reference/ImageShow.rst
Normal file
27
docs/reference/ImageShow.rst
Normal file
|
@ -0,0 +1,27 @@
|
|||
.. py:module:: PIL.ImageShow
|
||||
.. py:currentmodule:: PIL.ImageShow
|
||||
|
||||
:py:mod:`ImageShow` Module
|
||||
==========================
|
||||
|
||||
The :py:mod:`ImageShow` Module is used to display images.
|
||||
All default viewers convert the image to be shown to PNG format.
|
||||
|
||||
.. autofunction:: PIL.ImageShow.show
|
||||
|
||||
.. autoclass:: WindowsViewer
|
||||
.. autoclass:: MacViewer
|
||||
|
||||
.. class:: UnixViewer
|
||||
|
||||
The following viewers may be registered on Unix-based systems, if the given command is found:
|
||||
|
||||
.. autoclass:: PIL.ImageShow.DisplayViewer
|
||||
.. autoclass:: PIL.ImageShow.EogViewer
|
||||
.. autoclass:: PIL.ImageShow.XVViewer
|
||||
|
||||
.. autofunction:: PIL.ImageShow.register
|
||||
.. autoclass:: PIL.ImageShow.Viewer
|
||||
:member-order: bysource
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -7,7 +7,7 @@
|
|||
The :py:mod:`ImageStat` module calculates global statistics for an image, or
|
||||
for a region of an image.
|
||||
|
||||
.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None)
|
||||
.. py:class:: Stat(image_or_list, mask=None)
|
||||
|
||||
Calculate statistics for the given image. If a mask is included,
|
||||
only the regions covered by that mask are included in the
|
||||
|
@ -22,13 +22,13 @@ for a region of an image.
|
|||
|
||||
.. note::
|
||||
|
||||
This relies on the :py:meth:`~PIL.Image.histogram` method, and
|
||||
This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and
|
||||
simply returns the low and high bins used. This is correct for
|
||||
images with 8 bits per channel, but fails for other modes such as
|
||||
``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.getextrema` to
|
||||
``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to
|
||||
return per-band extrema for the image. This is more correct and
|
||||
efficient because, for non-8-bit modes, the histogram method uses
|
||||
:py:meth:`~PIL.Image.getextrema` to determine the bins used.
|
||||
:py:meth:`~PIL.Image.Image.getextrema` to determine the bins used.
|
||||
|
||||
.. py:attribute:: count
|
||||
|
||||
|
|
11
docs/reference/JpegPresets.rst
Normal file
11
docs/reference/JpegPresets.rst
Normal file
|
@ -0,0 +1,11 @@
|
|||
.. py:currentmodule:: PIL.JpegPresets
|
||||
|
||||
:py:mod:`JpegPresets` Module
|
||||
============================
|
||||
|
||||
.. automodule:: PIL.JpegPresets
|
||||
|
||||
.. data:: presets
|
||||
:type: dict
|
||||
|
||||
A dictionary of all supported presets.
|
|
@ -10,8 +10,8 @@ metadata tag numbers, names, and type information.
|
|||
.. method:: lookup(tag)
|
||||
|
||||
:param tag: Integer tag number
|
||||
:returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible,
|
||||
otherwise just populating the value and name from ``TAGS``.
|
||||
:returns: Taginfo namedtuple, From the :py:data:`~PIL.TiffTags.TAGS_V2` info if possible,
|
||||
otherwise just populating the value and name from :py:data:`~PIL.TiffTags.TAGS`.
|
||||
If the tag is not recognized, "unknown" is returned for the name
|
||||
|
||||
.. versionadded:: 3.1.0
|
||||
|
@ -22,7 +22,7 @@ metadata tag numbers, names, and type information.
|
|||
|
||||
:param value: Integer Tag Number
|
||||
:param name: Tag Name
|
||||
:param type: Integer type from :py:attr:`PIL.TiffTags.TYPES`
|
||||
:param type: Integer type from :py:data:`PIL.TiffTags.TYPES`
|
||||
:param length: Array length: 0 == variable, 1 == single value, n = fixed
|
||||
:param enum: Dict of name:integer value options for an enumeration
|
||||
|
||||
|
@ -33,15 +33,17 @@ metadata tag numbers, names, and type information.
|
|||
|
||||
.. versionadded:: 3.0.0
|
||||
|
||||
.. py:attribute:: PIL.TiffTags.TAGS_V2
|
||||
.. py:data:: PIL.TiffTags.TAGS_V2
|
||||
:type: dict
|
||||
|
||||
The ``TAGS_V2`` dictionary maps 16-bit integer tag numbers to
|
||||
:py:class:`PIL.TagTypes.TagInfo` tuples for metadata fields defined in the TIFF
|
||||
:py:class:`PIL.TiffTags.TagInfo` tuples for metadata fields defined in the TIFF
|
||||
spec.
|
||||
|
||||
.. versionadded:: 3.0.0
|
||||
|
||||
.. py:attribute:: PIL.TiffTags.TAGS
|
||||
.. py:data:: PIL.TiffTags.TAGS
|
||||
:type: dict
|
||||
|
||||
The ``TAGS`` dictionary maps 16-bit integer TIFF tag number to
|
||||
descriptive string names. For instance:
|
||||
|
@ -50,10 +52,11 @@ metadata tag numbers, names, and type information.
|
|||
>>> TAGS[0x010e]
|
||||
'ImageDescription'
|
||||
|
||||
This dictionary contains a superset of the tags in TAGS_V2, common
|
||||
This dictionary contains a superset of the tags in :py:data:`~PIL.TiffTags.TAGS_V2`, common
|
||||
EXIF tags, and other well known metadata tags.
|
||||
|
||||
.. py:attribute:: PIL.TiffTags.TYPES
|
||||
.. py:data:: PIL.TiffTags.TYPES
|
||||
:type: dict
|
||||
|
||||
The ``TYPES`` dictionary maps the TIFF type short integer to a
|
||||
human readable type name.
|
||||
|
|
|
@ -7,8 +7,8 @@ Reference
|
|||
|
||||
Image
|
||||
ImageChops
|
||||
ImageColor
|
||||
ImageCms
|
||||
ImageColor
|
||||
ImageDraw
|
||||
ImageEnhance
|
||||
ImageFile
|
||||
|
@ -22,11 +22,13 @@ Reference
|
|||
ImagePath
|
||||
ImageQt
|
||||
ImageSequence
|
||||
ImageShow
|
||||
ImageStat
|
||||
ImageTk
|
||||
ImageWin
|
||||
ExifTags
|
||||
TiffTags
|
||||
JpegPresets
|
||||
PSDraw
|
||||
PixelAccess
|
||||
PyAccess
|
||||
|
|
|
@ -7,4 +7,4 @@ Internal Reference Docs
|
|||
open_files
|
||||
limits
|
||||
block_allocator
|
||||
|
||||
internal_modules
|
||||
|
|
38
docs/reference/internal_modules.rst
Normal file
38
docs/reference/internal_modules.rst
Normal file
|
@ -0,0 +1,38 @@
|
|||
Internal Modules
|
||||
================
|
||||
|
||||
:mod:`_binary` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: PIL._binary
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`_tkinter_finder` Module
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: PIL._tkinter_finder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`_util` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: PIL._util
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`_version` Module
|
||||
----------------------
|
||||
|
||||
.. module:: PIL._version
|
||||
|
||||
.. data:: __version__
|
||||
:annotation:
|
||||
:type: str
|
||||
|
||||
This is the master version number for Pillow,
|
||||
all other uses reference this module.
|
|
@ -37,6 +37,19 @@ are now read as just a single bytestring.
|
|||
Deprecations
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Image.show command parameter
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``command`` parameter was deprecated and will be removed in a future release.
|
||||
Use a subclass of :py:class:`PIL.ImageShow.Viewer` instead.
|
||||
|
||||
Image._showxv
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show`
|
||||
instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add
|
||||
a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||
|
||||
ImageFile.raise_ioerror
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -9,4 +9,5 @@ pyflakes
|
|||
pyroma
|
||||
pytest
|
||||
pytest-cov
|
||||
sphinx>=2.4
|
||||
sphinx-rtd-theme
|
||||
|
|
|
@ -9,5 +9,5 @@ line_length = 88
|
|||
multi_line_output = 3
|
||||
|
||||
[tool:pytest]
|
||||
addopts = -rs --color=yes
|
||||
addopts = -ra --color=yes
|
||||
testpaths = Tests
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""
|
||||
Parse X Bitmap Distribution Format (BDF)
|
||||
"""
|
||||
|
||||
|
||||
from . import FontFile, Image
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# parse X Bitmap Distribution Format (BDF)
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
bdf_slant = {
|
||||
"R": "Roman",
|
||||
"I": "Italic",
|
||||
|
@ -78,11 +78,9 @@ def bdf_char(f):
|
|||
return id, int(props["ENCODING"]), bbox, im
|
||||
|
||||
|
||||
##
|
||||
# Font file plugin for the X11 BDF format.
|
||||
|
||||
|
||||
class BdfFontFile(FontFile.FontFile):
|
||||
"""Font file plugin for the X11 BDF format."""
|
||||
|
||||
def __init__(self, fp):
|
||||
super().__init__()
|
||||
|
||||
|
|
|
@ -282,8 +282,8 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
|||
self.magic = self.fd.read(4)
|
||||
self._read_blp_header()
|
||||
self._load()
|
||||
except struct.error:
|
||||
raise OSError("Truncated Blp file")
|
||||
except struct.error as e:
|
||||
raise OSError("Truncated Blp file") from e
|
||||
return 0, 0
|
||||
|
||||
def _read_palette(self):
|
||||
|
|
|
@ -263,7 +263,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||
head_data = self.fp.read(14)
|
||||
# choke if the file does not have the required magic bytes
|
||||
if head_data[0:2] != b"BM":
|
||||
if not _accept(head_data):
|
||||
raise SyntaxError("Not a BMP file")
|
||||
# read the start position of the BMP image data (u32)
|
||||
offset = i32(head_data[10:14])
|
||||
|
@ -304,8 +304,8 @@ def _dib_save(im, fp, filename):
|
|||
def _save(im, fp, filename, bitmap_header=True):
|
||||
try:
|
||||
rawmode, bits, colors = SAVE[im.mode]
|
||||
except KeyError:
|
||||
raise OSError("cannot write mode %s as BMP" % im.mode)
|
||||
except KeyError as e:
|
||||
raise OSError("cannot write mode %s as BMP" % im.mode) from e
|
||||
|
||||
info = im.encoderinfo
|
||||
|
||||
|
|
|
@ -14,14 +14,16 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
##
|
||||
# A file object that provides read access to a part of an existing
|
||||
# file (for example a TAR file).
|
||||
|
||||
import io
|
||||
|
||||
|
||||
class ContainerIO:
|
||||
"""
|
||||
A file object that provides read access to a part of an existing
|
||||
file (for example a TAR file).
|
||||
"""
|
||||
|
||||
def __init__(self, file, offset, length):
|
||||
"""
|
||||
Create file object.
|
||||
|
|
|
@ -46,7 +46,7 @@ class DcxImageFile(PcxImageFile):
|
|||
|
||||
# Header
|
||||
s = self.fp.read(4)
|
||||
if i32(s) != MAGIC:
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not a DCX file")
|
||||
|
||||
# Component directory
|
||||
|
|
|
@ -231,8 +231,8 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
|
||||
try:
|
||||
m = split.match(s)
|
||||
except re.error:
|
||||
raise SyntaxError("not an EPS file")
|
||||
except re.error as e:
|
||||
raise SyntaxError("not an EPS file") from e
|
||||
|
||||
if m:
|
||||
k, v = m.group(1, 2)
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
##
|
||||
# This module provides constants and clear-text names for various
|
||||
# well-known EXIF tags.
|
||||
##
|
||||
"""
|
||||
This module provides constants and clear-text names for various
|
||||
well-known EXIF tags.
|
||||
"""
|
||||
|
||||
##
|
||||
# Maps EXIF tags to tag names.
|
||||
|
||||
TAGS = {
|
||||
# possibly incomplete
|
||||
|
@ -280,9 +278,8 @@ TAGS = {
|
|||
0xC74E: "OpcodeList3",
|
||||
0xC761: "NoiseProfile",
|
||||
}
|
||||
"""Maps EXIF tags to tag names."""
|
||||
|
||||
##
|
||||
# Maps EXIF GPS tags to tag names.
|
||||
|
||||
GPSTAGS = {
|
||||
0: "GPSVersionID",
|
||||
|
@ -318,3 +315,4 @@ GPSTAGS = {
|
|||
30: "GPSDifferential",
|
||||
31: "GPSHPositioningError",
|
||||
}
|
||||
"""Maps EXIF GPS tags to tag names."""
|
||||
|
|
|
@ -42,9 +42,8 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
|
||||
# HEAD
|
||||
s = self.fp.read(128)
|
||||
magic = i16(s[4:6])
|
||||
if not (
|
||||
magic in [0xAF11, 0xAF12]
|
||||
_accept(s)
|
||||
and i16(s[14:16]) in [0, 3] # flags
|
||||
and s[20:22] == b"\x00\x00" # reserved
|
||||
):
|
||||
|
@ -60,6 +59,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
|
||||
# animation speed
|
||||
duration = i32(s[16:20])
|
||||
magic = i16(s[4:6])
|
||||
if magic == 0xAF11:
|
||||
duration = (duration * 1000) // 70
|
||||
self.info["duration"] = duration
|
||||
|
|
|
@ -23,18 +23,15 @@ WIDTH = 800
|
|||
|
||||
|
||||
def puti16(fp, values):
|
||||
# write network order (big-endian) 16-bit sequence
|
||||
"""Write network order (big-endian) 16-bit sequence"""
|
||||
for v in values:
|
||||
if v < 0:
|
||||
v += 65536
|
||||
fp.write(_binary.o16be(v))
|
||||
|
||||
|
||||
##
|
||||
# Base class for raster font file handlers.
|
||||
|
||||
|
||||
class FontFile:
|
||||
"""Base class for raster font file handlers."""
|
||||
|
||||
bitmap = None
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
|
||||
try:
|
||||
self.ole = olefile.OleFileIO(self.fp)
|
||||
except OSError:
|
||||
raise SyntaxError("not an FPX file; invalid OLE file")
|
||||
except OSError as e:
|
||||
raise SyntaxError("not an FPX file; invalid OLE file") from e
|
||||
|
||||
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
|
||||
raise SyntaxError("not an FPX file; bad root CLSID")
|
||||
|
|
|
@ -14,26 +14,30 @@
|
|||
#
|
||||
|
||||
|
||||
# NOTE: This format cannot be automatically recognized, so the
|
||||
# class is not registered for use with Image.open(). To open a
|
||||
# gd file, use the GdImageFile.open() function instead.
|
||||
"""
|
||||
.. note::
|
||||
This format cannot be automatically recognized, so the
|
||||
class is not registered for use with :py:func:`PIL.Image.open()`. To open a
|
||||
gd file, use the :py:func:`PIL.GdImageFile.open()` function instead.
|
||||
|
||||
# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
|
||||
# implementation is provided for convenience and demonstrational
|
||||
# purposes only.
|
||||
.. warning::
|
||||
THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
|
||||
implementation is provided for convenience and demonstrational
|
||||
purposes only.
|
||||
"""
|
||||
|
||||
|
||||
from . import ImageFile, ImagePalette, UnidentifiedImageError
|
||||
from ._binary import i8, i16be as i16, i32be as i32
|
||||
|
||||
##
|
||||
# Image plugin for the GD uncompressed format. Note that this format
|
||||
# is not supported by the standard <b>Image.open</b> function. To use
|
||||
# this plugin, you have to import the <b>GdImageFile</b> module and
|
||||
# use the <b>GdImageFile.open</b> function.
|
||||
|
||||
|
||||
class GdImageFile(ImageFile.ImageFile):
|
||||
"""
|
||||
Image plugin for the GD uncompressed format. Note that this format
|
||||
is not supported by the standard :py:func:`PIL.Image.open()` function. To use
|
||||
this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and
|
||||
use the :py:func:`PIL.GdImageFile.open()` function.
|
||||
"""
|
||||
|
||||
format = "GD"
|
||||
format_description = "GD uncompressed images"
|
||||
|
@ -81,5 +85,5 @@ def open(fp, mode="r"):
|
|||
|
||||
try:
|
||||
return GdImageFile(fp)
|
||||
except SyntaxError:
|
||||
raise UnidentifiedImageError("cannot identify this image file")
|
||||
except SyntaxError as e:
|
||||
raise UnidentifiedImageError("cannot identify this image file") from e
|
||||
|
|
|
@ -63,7 +63,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
|
||||
# Screen
|
||||
s = self.fp.read(13)
|
||||
if s[:6] not in [b"GIF87a", b"GIF89a"]:
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not a GIF file")
|
||||
|
||||
self.info["version"] = s[:6]
|
||||
|
@ -130,9 +130,9 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
for f in range(self.__frame + 1, frame + 1):
|
||||
try:
|
||||
self._seek(f)
|
||||
except EOFError:
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
raise EOFError("no more images in GIF file")
|
||||
raise EOFError("no more images in GIF file") from e
|
||||
|
||||
def _seek(self, frame):
|
||||
|
||||
|
|
|
@ -13,17 +13,19 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""
|
||||
Stuff to translate curve segments to palette values (derived from
|
||||
the corresponding code in GIMP, written by Federico Mena Quintero.
|
||||
See the GIMP distribution for more information.)
|
||||
"""
|
||||
|
||||
|
||||
from math import log, pi, sin, sqrt
|
||||
|
||||
from ._binary import o8
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Stuff to translate curve segments to palette values (derived from
|
||||
# the corresponding code in GIMP, written by Federico Mena Quintero.
|
||||
# See the GIMP distribution for more information.)
|
||||
#
|
||||
|
||||
EPSILON = 1e-10
|
||||
"""""" # Enable auto-doc for data member
|
||||
|
||||
|
||||
def linear(middle, pos):
|
||||
|
@ -58,6 +60,7 @@ def sphere_decreasing(middle, pos):
|
|||
|
||||
|
||||
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
|
||||
"""""" # Enable auto-doc for data member
|
||||
|
||||
|
||||
class GradientFile:
|
||||
|
@ -98,11 +101,9 @@ class GradientFile:
|
|||
return b"".join(palette), "RGBA"
|
||||
|
||||
|
||||
##
|
||||
# File handler for GIMP's gradient format.
|
||||
|
||||
|
||||
class GimpGradientFile(GradientFile):
|
||||
"""File handler for GIMP's gradient format."""
|
||||
|
||||
def __init__(self, fp):
|
||||
|
||||
if fp.readline()[:13] != b"GIMP Gradient":
|
||||
|
|
|
@ -18,11 +18,9 @@ import re
|
|||
|
||||
from ._binary import o8
|
||||
|
||||
##
|
||||
# File handler for GIMP's palette format.
|
||||
|
||||
|
||||
class GimpPaletteFile:
|
||||
"""File handler for GIMP's palette format."""
|
||||
|
||||
rawmode = "RGB"
|
||||
|
||||
|
|
|
@ -163,8 +163,8 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
|
||||
try:
|
||||
m = split.match(s)
|
||||
except re.error:
|
||||
raise SyntaxError("not an IM file")
|
||||
except re.error as e:
|
||||
raise SyntaxError("not an IM file") from e
|
||||
|
||||
if m:
|
||||
|
||||
|
@ -341,8 +341,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
image_type, rawmode = SAVE[im.mode]
|
||||
except KeyError:
|
||||
raise ValueError("Cannot save %s images as IM" % im.mode)
|
||||
except KeyError as e:
|
||||
raise ValueError("Cannot save %s images as IM" % im.mode) from e
|
||||
|
||||
frames = im.encoderinfo.get("frames", 1)
|
||||
|
||||
|
|
|
@ -434,8 +434,8 @@ def _getdecoder(mode, decoder_name, args, extra=()):
|
|||
try:
|
||||
# get decoder
|
||||
decoder = getattr(core, decoder_name + "_decoder")
|
||||
except AttributeError:
|
||||
raise OSError("decoder %s not available" % decoder_name)
|
||||
except AttributeError as e:
|
||||
raise OSError("decoder %s not available" % decoder_name) from e
|
||||
return decoder(mode, *args + extra)
|
||||
|
||||
|
||||
|
@ -457,8 +457,8 @@ def _getencoder(mode, encoder_name, args, extra=()):
|
|||
try:
|
||||
# get encoder
|
||||
encoder = getattr(core, encoder_name + "_encoder")
|
||||
except AttributeError:
|
||||
raise OSError("encoder %s not available" % encoder_name)
|
||||
except AttributeError as e:
|
||||
raise OSError("encoder %s not available" % encoder_name) from e
|
||||
return encoder(mode, *args + extra)
|
||||
|
||||
|
||||
|
@ -971,10 +971,10 @@ class Image:
|
|||
if isinstance(t, tuple):
|
||||
try:
|
||||
t = trns_im.palette.getcolor(t)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
"Couldn't allocate a palette color for transparency"
|
||||
)
|
||||
) from e
|
||||
trns_im.putpixel((0, 0), t)
|
||||
|
||||
if mode in ("L", "RGB"):
|
||||
|
@ -1027,8 +1027,8 @@ class Image:
|
|||
# normalize source image and try again
|
||||
im = self.im.convert(getmodebase(self.mode))
|
||||
im = im.convert(mode, dither)
|
||||
except KeyError:
|
||||
raise ValueError("illegal conversion")
|
||||
except KeyError as e:
|
||||
raise ValueError("illegal conversion") from e
|
||||
|
||||
new_im = self._new(im)
|
||||
if delete_trns:
|
||||
|
@ -1625,16 +1625,16 @@ class Image:
|
|||
mode = getmodebase(self.mode) + "A"
|
||||
try:
|
||||
self.im.setmode(mode)
|
||||
except (AttributeError, ValueError):
|
||||
except (AttributeError, ValueError) as e:
|
||||
# do things the hard way
|
||||
im = self.im.convert(mode)
|
||||
if im.mode not in ("LA", "PA", "RGBA"):
|
||||
raise ValueError # sanity check
|
||||
raise ValueError from e # sanity check
|
||||
self.im = im
|
||||
self.pyaccess = None
|
||||
self.mode = self.im.mode
|
||||
except (KeyError, ValueError):
|
||||
raise ValueError("illegal image mode")
|
||||
except (KeyError, ValueError) as e:
|
||||
raise ValueError("illegal image mode") from e
|
||||
|
||||
if self.mode in ("LA", "PA"):
|
||||
band = 1
|
||||
|
@ -2136,8 +2136,8 @@ class Image:
|
|||
init()
|
||||
try:
|
||||
format = EXTENSION[ext]
|
||||
except KeyError:
|
||||
raise ValueError("unknown file extension: {}".format(ext))
|
||||
except KeyError as e:
|
||||
raise ValueError("unknown file extension: {}".format(ext)) from e
|
||||
|
||||
if format.upper() not in SAVE:
|
||||
init()
|
||||
|
@ -2181,8 +2181,10 @@ class Image:
|
|||
|
||||
def show(self, title=None, command=None):
|
||||
"""
|
||||
Displays this image. This method is mainly intended for
|
||||
debugging purposes.
|
||||
Displays this image. This method is mainly intended for debugging purposes.
|
||||
|
||||
This method calls :py:func:`PIL.ImageShow.show` internally. You can use
|
||||
:py:func:`PIL.ImageShow.register` to override its default behaviour.
|
||||
|
||||
The image is first saved to a temporary file. By default, it will be in
|
||||
PNG format.
|
||||
|
@ -2194,11 +2196,16 @@ class Image:
|
|||
|
||||
On Windows, the image is opened with the standard PNG display utility.
|
||||
|
||||
:param title: Optional title to use for the image window,
|
||||
where possible.
|
||||
:param command: command used to show the image
|
||||
:param title: Optional title to use for the image window, where possible.
|
||||
"""
|
||||
|
||||
if command is not None:
|
||||
warnings.warn(
|
||||
"The command parameter is deprecated and will be removed in a future "
|
||||
"release. Use a subclass of ImageShow.Viewer instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
_show(self, title=title, command=command)
|
||||
|
||||
def split(self):
|
||||
|
@ -2238,8 +2245,8 @@ class Image:
|
|||
if isinstance(channel, str):
|
||||
try:
|
||||
channel = self.getbands().index(channel)
|
||||
except ValueError:
|
||||
raise ValueError('The image has no channel "{}"'.format(channel))
|
||||
except ValueError as e:
|
||||
raise ValueError('The image has no channel "{}"'.format(channel)) from e
|
||||
|
||||
return self._new(self.im.getband(channel))
|
||||
|
||||
|
@ -2736,12 +2743,12 @@ def fromarray(obj, mode=None):
|
|||
if mode is None:
|
||||
try:
|
||||
typekey = (1, 1) + shape[2:], arr["typestr"]
|
||||
except KeyError:
|
||||
raise TypeError("Cannot handle this data type")
|
||||
except KeyError as e:
|
||||
raise TypeError("Cannot handle this data type") from e
|
||||
try:
|
||||
mode, rawmode = _fromarray_typemap[typekey]
|
||||
except KeyError:
|
||||
raise TypeError("Cannot handle this data type: %s, %s" % typekey)
|
||||
except KeyError as e:
|
||||
raise TypeError("Cannot handle this data type: %s, %s" % typekey) from e
|
||||
else:
|
||||
rawmode = mode
|
||||
if mode in ["1", "L", "I", "P", "F"]:
|
||||
|
@ -3143,12 +3150,21 @@ def register_encoder(name, encoder):
|
|||
|
||||
|
||||
def _show(image, **options):
|
||||
options["_internal_pillow"] = True
|
||||
_showxv(image, **options)
|
||||
|
||||
|
||||
def _showxv(image, title=None, **options):
|
||||
from . import ImageShow
|
||||
|
||||
if "_internal_pillow" in options:
|
||||
del options["_internal_pillow"]
|
||||
else:
|
||||
warnings.warn(
|
||||
"_showxv is deprecated and will be removed in a future release. "
|
||||
"Use Image.show instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
ImageShow.show(image, title, **options)
|
||||
|
||||
|
||||
|
|
|
@ -369,7 +369,7 @@ def profileToProfile(
|
|||
else:
|
||||
imOut = transform.apply(im)
|
||||
except (OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
return imOut
|
||||
|
||||
|
@ -393,7 +393,7 @@ def getOpenProfile(profileFilename):
|
|||
try:
|
||||
return ImageCmsProfile(profileFilename)
|
||||
except (OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def buildTransform(
|
||||
|
@ -474,7 +474,7 @@ def buildTransform(
|
|||
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
|
||||
)
|
||||
except (OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def buildProofTransform(
|
||||
|
@ -585,7 +585,7 @@ def buildProofTransform(
|
|||
flags,
|
||||
)
|
||||
except (OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
buildTransformFromOpenProfiles = buildTransform
|
||||
|
@ -640,7 +640,7 @@ def applyTransform(im, transform, inPlace=False):
|
|||
else:
|
||||
imOut = transform.apply(im)
|
||||
except (TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
return imOut
|
||||
|
||||
|
@ -682,15 +682,15 @@ def createProfile(colorSpace, colorTemp=-1):
|
|||
if colorSpace == "LAB":
|
||||
try:
|
||||
colorTemp = float(colorTemp)
|
||||
except (TypeError, ValueError):
|
||||
except (TypeError, ValueError) as e:
|
||||
raise PyCMSError(
|
||||
'Color temperature must be numeric, "%s" not valid' % colorTemp
|
||||
)
|
||||
) from e
|
||||
|
||||
try:
|
||||
return core.createProfile(colorSpace, colorTemp)
|
||||
except (TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileName(profile):
|
||||
|
@ -732,7 +732,7 @@ def getProfileName(profile):
|
|||
return "{} - {}\n".format(model, manufacturer)
|
||||
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileInfo(profile):
|
||||
|
@ -772,7 +772,7 @@ def getProfileInfo(profile):
|
|||
return "\r\n\r\n".join(arr) + "\r\n\r\n"
|
||||
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileCopyright(profile):
|
||||
|
@ -800,7 +800,7 @@ def getProfileCopyright(profile):
|
|||
profile = ImageCmsProfile(profile)
|
||||
return (profile.profile.copyright or "") + "\n"
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileManufacturer(profile):
|
||||
|
@ -828,7 +828,7 @@ def getProfileManufacturer(profile):
|
|||
profile = ImageCmsProfile(profile)
|
||||
return (profile.profile.manufacturer or "") + "\n"
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileModel(profile):
|
||||
|
@ -857,7 +857,7 @@ def getProfileModel(profile):
|
|||
profile = ImageCmsProfile(profile)
|
||||
return (profile.profile.model or "") + "\n"
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getProfileDescription(profile):
|
||||
|
@ -886,7 +886,7 @@ def getProfileDescription(profile):
|
|||
profile = ImageCmsProfile(profile)
|
||||
return (profile.profile.profile_description or "") + "\n"
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def getDefaultIntent(profile):
|
||||
|
@ -925,7 +925,7 @@ def getDefaultIntent(profile):
|
|||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.rendering_intent
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def isIntentSupported(profile, intent, direction):
|
||||
|
@ -976,7 +976,7 @@ def isIntentSupported(profile, intent, direction):
|
|||
else:
|
||||
return -1
|
||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
raise PyCMSError(v) from v
|
||||
|
||||
|
||||
def versions():
|
||||
|
|
|
@ -16,21 +16,35 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
(Experimental) WCK-style drawing interface operations
|
||||
|
||||
.. seealso:: :py:mod:`PIL.ImageDraw`
|
||||
"""
|
||||
|
||||
|
||||
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
||||
|
||||
|
||||
class Pen:
|
||||
"""Stores an outline color and width."""
|
||||
|
||||
def __init__(self, color, width=1, opacity=255):
|
||||
self.color = ImageColor.getrgb(color)
|
||||
self.width = width
|
||||
|
||||
|
||||
class Brush:
|
||||
"""Stores a fill color"""
|
||||
|
||||
def __init__(self, color, opacity=255):
|
||||
self.color = ImageColor.getrgb(color)
|
||||
|
||||
|
||||
class Font:
|
||||
"""Stores a TrueType font and color"""
|
||||
|
||||
def __init__(self, color, file, size=12):
|
||||
# FIXME: add support for bitmap fonts
|
||||
self.color = ImageColor.getrgb(color)
|
||||
|
@ -38,6 +52,10 @@ class Font:
|
|||
|
||||
|
||||
class Draw:
|
||||
"""
|
||||
(Experimental) WCK-style drawing interface
|
||||
"""
|
||||
|
||||
def __init__(self, image, size=None, color=None):
|
||||
if not hasattr(image, "im"):
|
||||
image = Image.new(image, size, color)
|
||||
|
@ -73,35 +91,89 @@ class Draw:
|
|||
getattr(self.draw, op)(xy, fill=fill, outline=outline)
|
||||
|
||||
def settransform(self, offset):
|
||||
"""Sets a transformation offset."""
|
||||
(xoffset, yoffset) = offset
|
||||
self.transform = (1, 0, xoffset, 0, 1, yoffset)
|
||||
|
||||
def arc(self, xy, start, end, *options):
|
||||
"""
|
||||
Draws an arc (a portion of a circle outline) between the start and end
|
||||
angles, inside the given bounding box.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc`
|
||||
"""
|
||||
self.render("arc", xy, start, end, *options)
|
||||
|
||||
def chord(self, xy, start, end, *options):
|
||||
"""
|
||||
Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points
|
||||
with a straight line.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord`
|
||||
"""
|
||||
self.render("chord", xy, start, end, *options)
|
||||
|
||||
def ellipse(self, xy, *options):
|
||||
"""
|
||||
Draws an ellipse inside the given bounding box.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse`
|
||||
"""
|
||||
self.render("ellipse", xy, *options)
|
||||
|
||||
def line(self, xy, *options):
|
||||
"""
|
||||
Draws a line between the coordinates in the ``xy`` list.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line`
|
||||
"""
|
||||
self.render("line", xy, *options)
|
||||
|
||||
def pieslice(self, xy, start, end, *options):
|
||||
"""
|
||||
Same as arc, but also draws straight lines between the end points and the
|
||||
center of the bounding box.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice`
|
||||
"""
|
||||
self.render("pieslice", xy, start, end, *options)
|
||||
|
||||
def polygon(self, xy, *options):
|
||||
"""
|
||||
Draws a polygon.
|
||||
|
||||
The polygon outline consists of straight lines between the given
|
||||
coordinates, plus a straight line between the last and the first
|
||||
coordinate.
|
||||
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon`
|
||||
"""
|
||||
self.render("polygon", xy, *options)
|
||||
|
||||
def rectangle(self, xy, *options):
|
||||
"""
|
||||
Draws a rectangle.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle`
|
||||
"""
|
||||
self.render("rectangle", xy, *options)
|
||||
|
||||
def text(self, xy, text, font):
|
||||
"""
|
||||
Draws the string at the given position.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text`
|
||||
"""
|
||||
if self.transform:
|
||||
xy = ImagePath.Path(xy)
|
||||
xy.transform(self.transform)
|
||||
self.draw.text(xy, text, font=font.font, fill=font.color)
|
||||
|
||||
def textsize(self, text, font):
|
||||
"""
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize`
|
||||
"""
|
||||
return self.draw.textsize(text, font=font.font)
|
||||
|
|
|
@ -122,7 +122,7 @@ class ImageFile(Image.Image):
|
|||
EOFError, # got header but not the first frame
|
||||
struct.error,
|
||||
) as v:
|
||||
raise SyntaxError(v)
|
||||
raise SyntaxError(v) from v
|
||||
|
||||
if not self.mode or self.size[0] <= 0:
|
||||
raise SyntaxError("not identified by this driver")
|
||||
|
@ -241,12 +241,12 @@ class ImageFile(Image.Image):
|
|||
while True:
|
||||
try:
|
||||
s = read(self.decodermaxblock)
|
||||
except (IndexError, struct.error):
|
||||
except (IndexError, struct.error) as e:
|
||||
# truncated png/gif
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
break
|
||||
else:
|
||||
raise OSError("image file is truncated")
|
||||
raise OSError("image file is truncated") from e
|
||||
|
||||
if not s: # truncated jpeg
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
|
@ -505,7 +505,7 @@ def _save(im, fp, tile, bufsize=0):
|
|||
try:
|
||||
fh = fp.fileno()
|
||||
fp.flush()
|
||||
except (AttributeError, io.UnsupportedOperation):
|
||||
except (AttributeError, io.UnsupportedOperation) as e:
|
||||
# compress to Python file-compatible object
|
||||
for e, b, o, a in tile:
|
||||
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
||||
|
@ -522,7 +522,7 @@ def _save(im, fp, tile, bufsize=0):
|
|||
if s:
|
||||
break
|
||||
if s < 0:
|
||||
raise OSError("encoder error %d when writing image file" % s)
|
||||
raise OSError("encoder error %d when writing image file" % s) from e
|
||||
e.cleanup()
|
||||
else:
|
||||
# slight speedup: compress to real file object
|
||||
|
|
|
@ -411,10 +411,10 @@ class Color3DLUT(MultibandFilter):
|
|||
def _check_size(size):
|
||||
try:
|
||||
_, _, _ = size
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise ValueError(
|
||||
"Size should be either an integer or a tuple of three integers."
|
||||
)
|
||||
) from e
|
||||
except TypeError:
|
||||
size = (size, size, size)
|
||||
size = [int(x) for x in size]
|
||||
|
|
|
@ -505,8 +505,8 @@ class FreeTypeFont:
|
|||
"""
|
||||
try:
|
||||
names = self.font.getvarnames()
|
||||
except AttributeError:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
||||
except AttributeError as e:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||
return [name.replace(b"\x00", b"") for name in names]
|
||||
|
||||
def set_variation_by_name(self, name):
|
||||
|
@ -535,8 +535,8 @@ class FreeTypeFont:
|
|||
"""
|
||||
try:
|
||||
axes = self.font.getvaraxes()
|
||||
except AttributeError:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
||||
except AttributeError as e:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||
for axis in axes:
|
||||
axis["name"] = axis["name"].replace(b"\x00", b"")
|
||||
return axes
|
||||
|
@ -548,8 +548,8 @@ class FreeTypeFont:
|
|||
"""
|
||||
try:
|
||||
self.font.setvaraxes(axes)
|
||||
except AttributeError:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
||||
except AttributeError as e:
|
||||
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||
|
||||
|
||||
class TransposedFont:
|
||||
|
|
|
@ -57,8 +57,8 @@ class _Operand:
|
|||
im1.load()
|
||||
try:
|
||||
op = getattr(_imagingmath, op + "_" + im1.mode)
|
||||
except AttributeError:
|
||||
raise TypeError("bad operand type for '%s'" % op)
|
||||
except AttributeError as e:
|
||||
raise TypeError("bad operand type for '%s'" % op) from e
|
||||
_imagingmath.unop(op, out.im.id, im1.im.id)
|
||||
else:
|
||||
# binary operation
|
||||
|
@ -85,8 +85,8 @@ class _Operand:
|
|||
im2.load()
|
||||
try:
|
||||
op = getattr(_imagingmath, op + "_" + im1.mode)
|
||||
except AttributeError:
|
||||
raise TypeError("bad operand type for '%s'" % op)
|
||||
except AttributeError as e:
|
||||
raise TypeError("bad operand type for '%s'" % op) from e
|
||||
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
|
||||
return _Operand(out)
|
||||
|
||||
|
|
|
@ -97,13 +97,13 @@ class ImagePalette:
|
|||
if isinstance(color, tuple):
|
||||
try:
|
||||
return self.colors[color]
|
||||
except KeyError:
|
||||
except KeyError as e:
|
||||
# allocate new color slot
|
||||
if isinstance(self.palette, bytes):
|
||||
self.palette = bytearray(self.palette)
|
||||
index = len(self.colors)
|
||||
if index >= 256:
|
||||
raise ValueError("cannot allocate more than 256 colors")
|
||||
raise ValueError("cannot allocate more than 256 colors") from e
|
||||
self.colors[color] = index
|
||||
self.palette[index] = color[0]
|
||||
self.palette[index + 256] = color[1]
|
||||
|
|
|
@ -38,8 +38,8 @@ class Iterator:
|
|||
try:
|
||||
self.im.seek(ix)
|
||||
return self.im
|
||||
except EOFError:
|
||||
raise IndexError # end of sequence
|
||||
except EOFError as e:
|
||||
raise IndexError from e # end of sequence
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
@ -49,8 +49,8 @@ class Iterator:
|
|||
self.im.seek(self.position)
|
||||
self.position += 1
|
||||
return self.im
|
||||
except EOFError:
|
||||
raise StopIteration
|
||||
except EOFError as e:
|
||||
raise StopIteration from e
|
||||
|
||||
|
||||
def all_frames(im, func=None):
|
||||
|
|
|
@ -24,6 +24,14 @@ _viewers = []
|
|||
|
||||
|
||||
def register(viewer, order=1):
|
||||
"""
|
||||
The :py:func:`register` function is used to register additional viewers.
|
||||
|
||||
:param viewer: The viewer to be registered.
|
||||
:param order:
|
||||
Zero or a negative integer to prepend this viewer to the list,
|
||||
a positive integer to append it.
|
||||
"""
|
||||
try:
|
||||
if issubclass(viewer, Viewer):
|
||||
viewer = viewer()
|
||||
|
@ -40,9 +48,9 @@ def show(image, title=None, **options):
|
|||
Display a given image.
|
||||
|
||||
:param image: An image object.
|
||||
:param title: Optional title. Not all viewers can display the title.
|
||||
:param title: Optional title. Not all viewers can display the title.
|
||||
:param \**options: Additional viewer options.
|
||||
:returns: True if a suitable viewer was found, false otherwise.
|
||||
:returns: ``True`` if a suitable viewer was found, ``False`` otherwise.
|
||||
"""
|
||||
for viewer in _viewers:
|
||||
if viewer.show(image, title=title, **options):
|
||||
|
@ -56,6 +64,10 @@ class Viewer:
|
|||
# main api
|
||||
|
||||
def show(self, image, **options):
|
||||
"""
|
||||
The main function for displaying an image.
|
||||
Converts the given image to the target format and displays it.
|
||||
"""
|
||||
|
||||
# save temporary image to disk
|
||||
if not (
|
||||
|
@ -70,25 +82,31 @@ class Viewer:
|
|||
# hook methods
|
||||
|
||||
format = None
|
||||
"""The format to convert the image into."""
|
||||
options = {}
|
||||
"""Additional options used to convert the image."""
|
||||
|
||||
def get_format(self, image):
|
||||
"""Return format name, or None to save as PGM/PPM"""
|
||||
"""Return format name, or ``None`` to save as PGM/PPM."""
|
||||
return self.format
|
||||
|
||||
def get_command(self, file, **options):
|
||||
"""
|
||||
Returns the command used to display the file.
|
||||
Not implemented in the base class.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def save_image(self, image):
|
||||
"""Save to temporary file, and return filename"""
|
||||
"""Save to temporary file and return filename."""
|
||||
return image._dump(format=self.get_format(image), **self.options)
|
||||
|
||||
def show_image(self, image, **options):
|
||||
"""Display given image"""
|
||||
"""Display the given image."""
|
||||
return self.show_file(self.save_image(image), **options)
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
"""Display the given file."""
|
||||
os.system(self.get_command(file, **options))
|
||||
return 1
|
||||
|
||||
|
@ -96,104 +114,116 @@ class Viewer:
|
|||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
class WindowsViewer(Viewer):
|
||||
"""The default viewer on Windows is the default system application for PNG files."""
|
||||
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
|
||||
def get_command(self, file, **options):
|
||||
return (
|
||||
'start "Pillow" /WAIT "%s" '
|
||||
"&& ping -n 2 127.0.0.1 >NUL "
|
||||
'&& del /f "%s"' % (file, file)
|
||||
)
|
||||
|
||||
|
||||
if sys.platform == "win32":
|
||||
|
||||
class WindowsViewer(Viewer):
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
|
||||
def get_command(self, file, **options):
|
||||
return (
|
||||
'start "Pillow" /WAIT "%s" '
|
||||
"&& ping -n 2 127.0.0.1 >NUL "
|
||||
'&& del /f "%s"' % (file, file)
|
||||
)
|
||||
|
||||
register(WindowsViewer)
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
|
||||
class MacViewer(Viewer):
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
class MacViewer(Viewer):
|
||||
"""The default viewer on MacOS using ``Preview.app``."""
|
||||
|
||||
def get_command(self, file, **options):
|
||||
# on darwin open returns immediately resulting in the temp
|
||||
# file removal while app is opening
|
||||
command = "open -a Preview.app"
|
||||
command = "({} {}; sleep 20; rm -f {})&".format(
|
||||
command, quote(file), quote(file)
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
|
||||
def get_command(self, file, **options):
|
||||
# on darwin open returns immediately resulting in the temp
|
||||
# file removal while app is opening
|
||||
command = "open -a Preview.app"
|
||||
command = "({} {}; sleep 20; rm -f {})&".format(
|
||||
command, quote(file), quote(file)
|
||||
)
|
||||
return command
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path, "r") as f:
|
||||
subprocess.Popen(
|
||||
["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
|
||||
shell=True,
|
||||
stdin=f,
|
||||
)
|
||||
return command
|
||||
os.remove(path)
|
||||
return 1
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path, "r") as f:
|
||||
subprocess.Popen(
|
||||
["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
|
||||
shell=True,
|
||||
stdin=f,
|
||||
)
|
||||
os.remove(path)
|
||||
return 1
|
||||
|
||||
if sys.platform == "darwin":
|
||||
register(MacViewer)
|
||||
|
||||
else:
|
||||
|
||||
# unixoids
|
||||
class UnixViewer(Viewer):
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
|
||||
class UnixViewer(Viewer):
|
||||
format = "PNG"
|
||||
options = {"compress_level": 1}
|
||||
def get_command(self, file, **options):
|
||||
command = self.get_command_ex(file, **options)[0]
|
||||
return "({} {}; rm -f {})&".format(command, quote(file), quote(file))
|
||||
|
||||
def get_command(self, file, **options):
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path, "r") as f:
|
||||
command = self.get_command_ex(file, **options)[0]
|
||||
return "({} {}; rm -f {})&".format(command, quote(file), quote(file))
|
||||
subprocess.Popen(
|
||||
["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
|
||||
)
|
||||
os.remove(path)
|
||||
return 1
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path, "r") as f:
|
||||
command = self.get_command_ex(file, **options)[0]
|
||||
subprocess.Popen(
|
||||
["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
|
||||
)
|
||||
os.remove(path)
|
||||
return 1
|
||||
|
||||
# implementations
|
||||
class DisplayViewer(UnixViewer):
|
||||
"""The ImageMagick ``display`` command."""
|
||||
|
||||
class DisplayViewer(UnixViewer):
|
||||
def get_command_ex(self, file, **options):
|
||||
command = executable = "display"
|
||||
return command, executable
|
||||
def get_command_ex(self, file, **options):
|
||||
command = executable = "display"
|
||||
return command, executable
|
||||
|
||||
|
||||
class EogViewer(UnixViewer):
|
||||
"""The GNOME Image Viewer ``eog`` command."""
|
||||
|
||||
def get_command_ex(self, file, **options):
|
||||
command = executable = "eog"
|
||||
return command, executable
|
||||
|
||||
|
||||
class XVViewer(UnixViewer):
|
||||
"""
|
||||
The X Viewer ``xv`` command.
|
||||
This viewer supports the ``title`` parameter.
|
||||
"""
|
||||
|
||||
def get_command_ex(self, file, title=None, **options):
|
||||
# note: xv is pretty outdated. most modern systems have
|
||||
# imagemagick's display command instead.
|
||||
command = executable = "xv"
|
||||
if title:
|
||||
command += " -name %s" % quote(title)
|
||||
return command, executable
|
||||
|
||||
|
||||
if sys.platform not in ("win32", "darwin"): # unixoids
|
||||
if shutil.which("display"):
|
||||
register(DisplayViewer)
|
||||
|
||||
class EogViewer(UnixViewer):
|
||||
def get_command_ex(self, file, **options):
|
||||
command = executable = "eog"
|
||||
return command, executable
|
||||
|
||||
if shutil.which("eog"):
|
||||
register(EogViewer)
|
||||
|
||||
class XVViewer(UnixViewer):
|
||||
def get_command_ex(self, file, title=None, **options):
|
||||
# note: xv is pretty outdated. most modern systems have
|
||||
# imagemagick's display command instead.
|
||||
command = executable = "xv"
|
||||
if title:
|
||||
command += " -name %s" % quote(title)
|
||||
return command, executable
|
||||
|
||||
if shutil.which("xv"):
|
||||
register(XVViewer)
|
||||
|
||||
|
|
|
@ -118,8 +118,8 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
# compression
|
||||
try:
|
||||
compression = COMPRESSION[self.getint((3, 120))]
|
||||
except KeyError:
|
||||
raise OSError("Unknown IPTC image compression")
|
||||
except KeyError as e:
|
||||
raise OSError("Unknown IPTC image compression") from e
|
||||
|
||||
# tile
|
||||
if tag == (8, 10):
|
||||
|
|
|
@ -323,7 +323,8 @@ MARKER = {
|
|||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[0:1] == b"\377"
|
||||
# Magic number was taken from https://en.wikipedia.org/wiki/JPEG
|
||||
return prefix[0:3] == b"\xFF\xD8\xFF"
|
||||
|
||||
|
||||
##
|
||||
|
@ -337,10 +338,11 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self):
|
||||
|
||||
s = self.fp.read(1)
|
||||
s = self.fp.read(3)
|
||||
|
||||
if i8(s) != 255:
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not a JPEG file")
|
||||
s = b"\xFF"
|
||||
|
||||
# Create attributes
|
||||
self.bits = self.layers = 0
|
||||
|
@ -503,13 +505,13 @@ def _getmp(self):
|
|||
file_contents.seek(info.next)
|
||||
info.load(file_contents)
|
||||
mp = dict(info)
|
||||
except Exception:
|
||||
raise SyntaxError("malformed MP Index (unreadable directory)")
|
||||
except Exception as e:
|
||||
raise SyntaxError("malformed MP Index (unreadable directory)") from e
|
||||
# it's an error not to have a number of images
|
||||
try:
|
||||
quant = mp[0xB001]
|
||||
except KeyError:
|
||||
raise SyntaxError("malformed MP Index (no number of images)")
|
||||
except KeyError as e:
|
||||
raise SyntaxError("malformed MP Index (no number of images)") from e
|
||||
# get MP entries
|
||||
mpentries = []
|
||||
try:
|
||||
|
@ -545,8 +547,8 @@ def _getmp(self):
|
|||
mpentry["Attribute"] = mpentryattr
|
||||
mpentries.append(mpentry)
|
||||
mp[0xB002] = mpentries
|
||||
except KeyError:
|
||||
raise SyntaxError("malformed MP Index (bad MP Entry)")
|
||||
except KeyError as e:
|
||||
raise SyntaxError("malformed MP Index (bad MP Entry)") from e
|
||||
# Next we should try and parse the individual image unique ID list;
|
||||
# we don't because I've never seen this actually used in a real MPO
|
||||
# file and so can't test it.
|
||||
|
@ -610,8 +612,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
rawmode = RAWMODE[im.mode]
|
||||
except KeyError:
|
||||
raise OSError("cannot write mode %s as JPEG" % im.mode)
|
||||
except KeyError as e:
|
||||
raise OSError("cannot write mode %s as JPEG" % im.mode) from e
|
||||
|
||||
info = im.encoderinfo
|
||||
|
||||
|
@ -663,8 +665,8 @@ def _save(im, fp, filename):
|
|||
for line in qtables.splitlines()
|
||||
for num in line.split("#", 1)[0].split()
|
||||
]
|
||||
except ValueError:
|
||||
raise ValueError("Invalid quantization table")
|
||||
except ValueError as e:
|
||||
raise ValueError("Invalid quantization table") from e
|
||||
else:
|
||||
qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
|
||||
if isinstance(qtables, (tuple, list, dict)):
|
||||
|
@ -679,8 +681,8 @@ def _save(im, fp, filename):
|
|||
if len(table) != 64:
|
||||
raise TypeError
|
||||
table = array.array("B", table)
|
||||
except TypeError:
|
||||
raise ValueError("Invalid quantization table")
|
||||
except TypeError as e:
|
||||
raise ValueError("Invalid quantization table") from e
|
||||
else:
|
||||
qtables[idx] = list(table)
|
||||
return qtables
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
"""
|
||||
JPEG quality settings equivalent to the Photoshop settings.
|
||||
Can be used when saving JPEG files.
|
||||
|
||||
More presets can be added to the presets dict if needed.
|
||||
|
||||
Can be use when saving JPEG file.
|
||||
The following presets are available by default:
|
||||
``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``,
|
||||
``low``, ``medium``, ``high``, ``maximum``.
|
||||
More presets can be added to the :py:data:`presets` dict if needed.
|
||||
|
||||
To apply the preset, specify::
|
||||
|
||||
|
@ -21,7 +23,6 @@ Example::
|
|||
|
||||
im.save("image_name.jpg", quality="web_high")
|
||||
|
||||
|
||||
Subsampling
|
||||
-----------
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
|||
|
||||
try:
|
||||
self.ole = olefile.OleFileIO(self.fp)
|
||||
except OSError:
|
||||
raise SyntaxError("not an MIC file; invalid OLE file")
|
||||
except OSError as e:
|
||||
raise SyntaxError("not an MIC file; invalid OLE file") from e
|
||||
|
||||
# find ACI subfiles with Image members (maybe not the
|
||||
# best way to identify MIC files, but what the... ;-)
|
||||
|
@ -77,8 +77,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
|||
return
|
||||
try:
|
||||
filename = self.images[frame]
|
||||
except IndexError:
|
||||
raise EOFError("no such frame")
|
||||
except IndexError as e:
|
||||
raise EOFError("no such frame") from e
|
||||
|
||||
self.fp = self.ole.openstream(filename)
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class MspImageFile(ImageFile.ImageFile):
|
|||
|
||||
# Header
|
||||
s = self.fp.read(32)
|
||||
if s[:4] not in [b"DanM", b"LinS"]:
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not an MSP file")
|
||||
|
||||
# Header checksum
|
||||
|
@ -116,8 +116,8 @@ class MspDecoder(ImageFile.PyDecoder):
|
|||
rowmap = struct.unpack_from(
|
||||
"<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2)
|
||||
)
|
||||
except struct.error:
|
||||
raise OSError("Truncated MSP file in row map")
|
||||
except struct.error as e:
|
||||
raise OSError("Truncated MSP file in row map") from e
|
||||
|
||||
for x, rowlen in enumerate(rowmap):
|
||||
try:
|
||||
|
@ -142,8 +142,8 @@ class MspDecoder(ImageFile.PyDecoder):
|
|||
img.write(row[idx : idx + runcount])
|
||||
idx += runcount
|
||||
|
||||
except struct.error:
|
||||
raise OSError("Corrupted MSP file in row %d" % x)
|
||||
except struct.error as e:
|
||||
raise OSError("Corrupted MSP file in row %d" % x) from e
|
||||
|
||||
self.set_as_raw(img.getvalue(), ("1", 0, 1))
|
||||
|
||||
|
|
|
@ -15,11 +15,9 @@
|
|||
|
||||
from ._binary import o8
|
||||
|
||||
##
|
||||
# File handler for Teragon-style palette files.
|
||||
|
||||
|
||||
class PaletteFile:
|
||||
"""File handler for Teragon-style palette files."""
|
||||
|
||||
rawmode = "RGB"
|
||||
|
||||
|
|
|
@ -48,11 +48,8 @@ def sz(s, o):
|
|||
return s[o : s.index(b"\0", o)]
|
||||
|
||||
|
||||
##
|
||||
# Font file plugin for the X11 PCF format.
|
||||
|
||||
|
||||
class PcfFontFile(FontFile.FontFile):
|
||||
"""Font file plugin for the X11 PCF format."""
|
||||
|
||||
name = "name"
|
||||
|
||||
|
|
|
@ -131,8 +131,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
version, bits, planes, rawmode = SAVE[im.mode]
|
||||
except KeyError:
|
||||
raise ValueError("Cannot save %s images as PCX" % im.mode)
|
||||
except KeyError as e:
|
||||
raise ValueError("Cannot save %s images as PCX" % im.mode) from e
|
||||
|
||||
# bytes per plane
|
||||
stride = (im.size[0] * bits + 7) // 8
|
||||
|
|
|
@ -43,7 +43,7 @@ class PixarImageFile(ImageFile.ImageFile):
|
|||
|
||||
# assuming a 4-byte magic label
|
||||
s = self.fp.read(4)
|
||||
if s != b"\200\350\000\000":
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not a PIXAR file")
|
||||
|
||||
# read rest of header
|
||||
|
|
|
@ -168,8 +168,10 @@ class ChunkStream:
|
|||
crc2 = i32(self.fp.read(4))
|
||||
if crc1 != crc2:
|
||||
raise SyntaxError("broken PNG file (bad header checksum in %r)" % cid)
|
||||
except struct.error:
|
||||
raise SyntaxError("broken PNG file (incomplete checksum in %r)" % cid)
|
||||
except struct.error as e:
|
||||
raise SyntaxError(
|
||||
"broken PNG file (incomplete checksum in %r)" % cid
|
||||
) from e
|
||||
|
||||
def crc_skip(self, cid, data):
|
||||
"""Read checksum. Used if the C module is not present"""
|
||||
|
@ -186,8 +188,8 @@ class ChunkStream:
|
|||
while True:
|
||||
try:
|
||||
cid, pos, length = self.read()
|
||||
except struct.error:
|
||||
raise OSError("truncated PNG file")
|
||||
except struct.error as e:
|
||||
raise OSError("truncated PNG file") from e
|
||||
|
||||
if cid == endchunk:
|
||||
break
|
||||
|
@ -633,7 +635,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self):
|
||||
|
||||
if self.fp.read(8) != _MAGIC:
|
||||
if not _accept(self.fp.read(8)):
|
||||
raise SyntaxError("not a PNG file")
|
||||
self.__fp = self.fp
|
||||
self.__frame = 0
|
||||
|
@ -737,9 +739,9 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
for f in range(self.__frame + 1, frame + 1):
|
||||
try:
|
||||
self._seek(f)
|
||||
except EOFError:
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
raise EOFError("no more images in APNG file")
|
||||
raise EOFError("no more images in APNG file") from e
|
||||
|
||||
def _seek(self, frame, rewind=False):
|
||||
if frame == 0:
|
||||
|
@ -1168,8 +1170,8 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
|||
# get the corresponding PNG mode
|
||||
try:
|
||||
rawmode, mode = _OUTMODES[mode]
|
||||
except KeyError:
|
||||
raise OSError("cannot write mode %s as PNG" % mode)
|
||||
except KeyError as e:
|
||||
raise OSError("cannot write mode %s as PNG" % mode) from e
|
||||
|
||||
#
|
||||
# write minimal PNG file
|
||||
|
|
|
@ -61,7 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
# header
|
||||
|
||||
s = read(26)
|
||||
if s[:4] != b"8BPS" or i16(s[4:]) != 1:
|
||||
if not _accept(s) or i16(s[4:]) != 1:
|
||||
raise SyntaxError("not a PSD file")
|
||||
|
||||
psd_bits = i16(s[22:])
|
||||
|
@ -144,8 +144,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
self.frame = layer
|
||||
self.fp = self.__fp
|
||||
return name, bbox
|
||||
except IndexError:
|
||||
raise EOFError("no such layer")
|
||||
except IndexError as e:
|
||||
raise EOFError("no such layer") from e
|
||||
|
||||
def tell(self):
|
||||
# return layer number (0=image, 1..max=layers)
|
||||
|
|
|
@ -58,8 +58,7 @@ class SgiImageFile(ImageFile.ImageFile):
|
|||
headlen = 512
|
||||
s = self.fp.read(headlen)
|
||||
|
||||
# magic number : 474
|
||||
if i16(s) != 474:
|
||||
if not _accept(s):
|
||||
raise ValueError("Not an SGI image file")
|
||||
|
||||
# compression : verbatim or RLE
|
||||
|
|
|
@ -111,8 +111,8 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
hdrlen = isSpiderHeader(t)
|
||||
if hdrlen == 0:
|
||||
raise SyntaxError("not a valid Spider file")
|
||||
except struct.error:
|
||||
raise SyntaxError("not a valid Spider file")
|
||||
except struct.error as e:
|
||||
raise SyntaxError("not a valid Spider file") from e
|
||||
|
||||
h = (99,) + t # add 1 value : spider header index starts at 1
|
||||
iform = int(h[5])
|
||||
|
|
|
@ -53,7 +53,7 @@ class SunImageFile(ImageFile.ImageFile):
|
|||
|
||||
# HEAD
|
||||
s = self.fp.read(32)
|
||||
if i32(s) != 0x59A66A95:
|
||||
if not _accept(s):
|
||||
raise SyntaxError("not an SUN raster file")
|
||||
|
||||
offset = 32
|
||||
|
|
|
@ -18,12 +18,10 @@ import io
|
|||
|
||||
from . import ContainerIO
|
||||
|
||||
##
|
||||
# A file object that provides read access to a given member of a TAR
|
||||
# file.
|
||||
|
||||
|
||||
class TarIO(ContainerIO.ContainerIO):
|
||||
"""A file object that provides read access to a given member of a TAR file."""
|
||||
|
||||
def __init__(self, tarfile, file):
|
||||
"""
|
||||
Create file object.
|
||||
|
|
|
@ -167,8 +167,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
|
||||
except KeyError:
|
||||
raise OSError("cannot write mode %s as TGA" % im.mode)
|
||||
except KeyError as e:
|
||||
raise OSError("cannot write mode %s as TGA" % im.mode) from e
|
||||
|
||||
if "rle" in im.encoderinfo:
|
||||
rle = im.encoderinfo["rle"]
|
||||
|
|
|
@ -1117,8 +1117,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
)
|
||||
try:
|
||||
decoder.setimage(self.im, extents)
|
||||
except ValueError:
|
||||
raise OSError("Couldn't set the image")
|
||||
except ValueError as e:
|
||||
raise OSError("Couldn't set the image") from e
|
||||
|
||||
close_self_fp = self._exclusive_fp and not self.is_animated
|
||||
if hasattr(self.fp, "getvalue"):
|
||||
|
@ -1231,9 +1231,9 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
logger.debug("format key: {}".format(key))
|
||||
try:
|
||||
self.mode, rawmode = OPEN_INFO[key]
|
||||
except KeyError:
|
||||
except KeyError as e:
|
||||
logger.debug("- unsupported format")
|
||||
raise SyntaxError("unknown pixel mode")
|
||||
raise SyntaxError("unknown pixel mode") from e
|
||||
|
||||
logger.debug("- raw mode: {}".format(rawmode))
|
||||
logger.debug("- pil mode: {}".format(self.mode))
|
||||
|
@ -1400,8 +1400,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
|
||||
except KeyError:
|
||||
raise OSError("cannot write mode %s as TIFF" % im.mode)
|
||||
except KeyError as e:
|
||||
raise OSError("cannot write mode %s as TIFF" % im.mode) from e
|
||||
|
||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||
|
||||
|
@ -1530,7 +1530,6 @@ def _save(im, fp, filename):
|
|||
# BITSPERSAMPLE, etc), passing arrays with a different length will result in
|
||||
# segfaults. Block these tags until we add extra validation.
|
||||
blocklist = [
|
||||
COLORMAP,
|
||||
REFERENCEBLACKWHITE,
|
||||
SAMPLEFORMAT,
|
||||
STRIPBYTECOUNTS,
|
||||
|
|
|
@ -483,7 +483,6 @@ LIBTIFF_CORE = {
|
|||
65537,
|
||||
}
|
||||
|
||||
LIBTIFF_CORE.remove(320) # Array of short, crashes
|
||||
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
||||
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
||||
|
||||
|
|
|
@ -12,13 +12,16 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
# NOTE: This format cannot be automatically recognized, so the reader
|
||||
# is not registered for use with Image.open(). To open a WAL file, use
|
||||
# the WalImageFile.open() function instead.
|
||||
"""
|
||||
This reader is based on the specification available from:
|
||||
https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
|
||||
and has been tested with a few sample files found using google.
|
||||
|
||||
# This reader is based on the specification available from:
|
||||
# https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
|
||||
# and has been tested with a few sample files found using google.
|
||||
.. note::
|
||||
This format cannot be automatically recognized, so the reader
|
||||
is not registered for use with :py:func:`PIL.Image.open()`.
|
||||
To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead.
|
||||
"""
|
||||
|
||||
import builtins
|
||||
|
||||
|
@ -31,7 +34,7 @@ def open(filename):
|
|||
Load texture from a Quake2 WAL texture file.
|
||||
|
||||
By default, a Quake2 standard palette is attached to the texture.
|
||||
To override the palette, use the <b>putpalette</b> method.
|
||||
To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method.
|
||||
|
||||
:param filename: WAL file name, or an opened file handle.
|
||||
:returns: An image instance.
|
||||
|
|
|
@ -132,4 +132,8 @@ _plugins = [
|
|||
|
||||
|
||||
class UnidentifiedImageError(OSError):
|
||||
"""
|
||||
Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
|
||||
"""Binary input/output support routines."""
|
||||
|
||||
|
||||
from struct import pack, unpack_from
|
||||
|
||||
|
||||
|
|
23
src/encode.c
23
src/encode.c
|
@ -671,7 +671,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
// This list also exists in TiffTags.py
|
||||
const int core_tags[] = {
|
||||
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
|
||||
341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996,
|
||||
341, 282, 283, 284, 286, 287, 296, 297, 320, 321, 338, 32995, 32998, 32996,
|
||||
339, 32997, 330, 531, 530, 65537
|
||||
};
|
||||
|
||||
|
@ -801,7 +801,26 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
TRACE(("Setting from Tuple: %d \n", key_int));
|
||||
len = PyTuple_Size(value);
|
||||
|
||||
if (type == TIFF_SHORT) {
|
||||
if (key_int == TIFFTAG_COLORMAP) {
|
||||
int stride = 256;
|
||||
if (len != 768) {
|
||||
PyErr_SetString(PyExc_ValueError, "Requiring 768 items for for Colormap");
|
||||
return NULL;
|
||||
}
|
||||
UINT16 *av;
|
||||
/* malloc check ok, calloc checks for overflow */
|
||||
av = calloc(len, sizeof(UINT16));
|
||||
if (av) {
|
||||
for (i=0;i<len;i++) {
|
||||
av[i] = (UINT16)PyLong_AsLong(PyTuple_GetItem(value,i));
|
||||
}
|
||||
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int,
|
||||
av,
|
||||
av + stride,
|
||||
av + stride * 2);
|
||||
free(av);
|
||||
}
|
||||
} else if (type == TIFF_SHORT) {
|
||||
UINT16 *av;
|
||||
/* malloc check ok, calloc checks for overflow */
|
||||
av = calloc(len, sizeof(UINT16));
|
||||
|
|
|
@ -683,7 +683,7 @@ ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
|
|||
return 0;
|
||||
}
|
||||
|
||||
big_hypotenuse = sqrt((double) (dx*dx + dy*dy));
|
||||
big_hypotenuse = hypot(dx, dy);
|
||||
small_hypotenuse = (width - 1) / 2.0;
|
||||
ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse;
|
||||
ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse;
|
||||
|
|
|
@ -251,9 +251,9 @@ deps = {
|
|||
"libs": [r"*.lib"],
|
||||
},
|
||||
"harfbuzz": {
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.7.zip",
|
||||
"filename": "harfbuzz-2.6.7.zip",
|
||||
"dir": "harfbuzz-2.6.7",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.8.zip",
|
||||
"filename": "harfbuzz-2.6.8.zip",
|
||||
"dir": "harfbuzz-2.6.8",
|
||||
"build": [
|
||||
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
|
||||
cmd_nmake(target="clean"),
|
||||
|
|
Loading…
Reference in New Issue
Block a user