Skip to content

Commit

Permalink
Merge branch 'main' into iptc
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Jan 1, 2024
2 parents 17911d6 + f2c6f11 commit 3ef7b93
Show file tree
Hide file tree
Showing 29 changed files with 289 additions and 119 deletions.
18 changes: 18 additions & 0 deletions .github/problem-matchers/gcc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"__comment": "Based on vscode-cpptools' Extension/package.json gcc rule",
"problemMatcher": [
{
"owner": "gcc-problem-matcher",
"pattern": [
{
"regexp": "^\\s*(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ jobs:
env:
GHA_PYTHON_VERSION: ${{ matrix.python-version }}

- name: Register gcc problem matcher
if: "matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'"
run: echo "::add-matcher::.github/problem-matchers/gcc.json"

- name: Build
run: |
.ci/build.sh
Expand Down
27 changes: 27 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,36 @@ Changelog (Pillow)
10.2.0 (unreleased)
-------------------

- Allow uncompressed TIFF images to be saved in chunks #7650
[radarhere]

- Concatenate multiple JPEG EXIF markers #7496
[radarhere]

- Changed IPTC tile tuple to match other plugins #7661
[radarhere]

- Do not assign new fp attribute when exiting context manager #7566
[radarhere]

- Support arbitrary masks for uncompressed RGB DDS images #7589
[radarhere, akx]

- Support setting ROWSPERSTRIP tag #7654
[radarhere]

- Apply ImageFont.MAX_STRING_LENGTH to ImageFont.getmask() #7662
[radarhere]

- Optimise ``ImageColor`` using ``functools.lru_cache`` #7657
[hugovk]

- Restricted environment keys for ImageMath.eval() #7655
[wiredfool, radarhere]

- Optimise ``ImageMode.getmode`` using ``functools.lru_cache`` #7641
[hugovk, radarhere]

- Fix incorrect color blending for overlapping glyphs #7497
[ZachNagengast, nulano, radarhere]

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is

Pillow is the friendly PIL fork. It is

Copyright © 2010-2023 by Jeffrey A. Clark (Alex) and contributors.
Copyright © 2010-2024 by Jeffrey A. Clark (Alex) and contributors.

Like PIL, Pillow is licensed under the open source HPND License:

Expand Down
Binary file added Tests/images/bgr15.dds
Binary file not shown.
Binary file added Tests/images/bgr15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/multiple_exif.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
Binary file removed Tests/images/unsupported_bitcount_rgb.dds
Binary file not shown.
13 changes: 4 additions & 9 deletions Tests/test_file_dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
TEST_FILE_UNCOMPRESSED_L = "Tests/images/uncompressed_l.dds"
TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA = "Tests/images/uncompressed_la.dds"
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/hopper.dds"
TEST_FILE_UNCOMPRESSED_BGR15 = "Tests/images/bgr15.dds"
TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"


Expand Down Expand Up @@ -249,6 +250,7 @@ def test_dx10_r8g8b8a8_unorm_srgb():
("L", (128, 128), TEST_FILE_UNCOMPRESSED_L),
("LA", (128, 128), TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA),
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_RGB),
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_BGR15),
("RGBA", (800, 600), TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA),
],
)
Expand Down Expand Up @@ -341,16 +343,9 @@ def test_palette():
assert_image_equal_tofile(im, "Tests/images/transparent.gif")


@pytest.mark.parametrize(
"test_file",
(
"Tests/images/unsupported_bitcount_rgb.dds",
"Tests/images/unsupported_bitcount_luminance.dds",
),
)
def test_unsupported_bitcount(test_file):
def test_unsupported_bitcount():
with pytest.raises(OSError):
with Image.open(test_file):
with Image.open("Tests/images/unsupported_bitcount.dds"):
pass


Expand Down
20 changes: 12 additions & 8 deletions Tests/test_file_iptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,28 @@ def test_i():
c = b"a"

# Act
ret = IptcImagePlugin.i(c)
with pytest.warns(DeprecationWarning):
ret = IptcImagePlugin.i(c)

# Assert
assert ret == 97


def test_dump():
def test_dump(monkeypatch):
# Arrange
c = b"abc"
# Temporarily redirect stdout
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
mystdout = StringIO()
monkeypatch.setattr(sys, "stdout", mystdout)

# Act
IptcImagePlugin.dump(c)

# Reset stdout
sys.stdout = old_stdout
with pytest.warns(DeprecationWarning):
IptcImagePlugin.dump(c)

# Assert
assert mystdout.getvalue() == "61 62 63 \n"


def test_pad_deprecation():
with pytest.warns(DeprecationWarning):
assert IptcImagePlugin.PAD == b"\0\0\0\0"
4 changes: 4 additions & 0 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,10 @@ def test_ifd_offset_exif(self):
# Act / Assert
assert im._getexif()[306] == "2017:03:13 23:03:09"

def test_multiple_exif(self):
with Image.open("Tests/images/multiple_exif.jpg") as im:
assert im.info["exif"] == b"Exif\x00\x00firstsecond"

@mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
)
Expand Down
33 changes: 31 additions & 2 deletions Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,13 @@ def test_modify_exif(self, tmp_path):
outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
exif = im.getexif()
exif[256] = 100
exif[264] = 100

im.save(outfile, exif=exif)

with Image.open(outfile) as im:
exif = im.getexif()
assert exif[256] == 100
assert exif[264] == 100

def test_reload_exif_after_seek(self):
with Image.open("Tests/images/multipage.tiff") as im:
Expand Down Expand Up @@ -612,6 +612,14 @@ def test_roundtrip_tiff_uint16(self, tmp_path):

assert_image_equal_tofile(im, tmpfile)

def test_rowsperstrip(self, tmp_path):
outfile = str(tmp_path / "temp.tif")
im = hopper()
im.save(outfile, tiffinfo={278: 256})

with Image.open(outfile) as im:
assert im.tag_v2[278] == 256

def test_strip_raw(self):
infile = "Tests/images/tiff_strip_raw.tif"
with Image.open(infile) as im:
Expand Down Expand Up @@ -773,6 +781,27 @@ def test_get_photoshop_blocks(self):
4001,
]

def test_tiff_chunks(self, tmp_path):
tmpfile = str(tmp_path / "temp.tif")

im = hopper()
with open(tmpfile, "wb") as fp:
for y in range(0, 128, 32):
chunk = im.crop((0, y, 128, y + 32))
if y == 0:
chunk.save(
fp,
"TIFF",
tiffinfo={
TiffImagePlugin.IMAGEWIDTH: 128,
TiffImagePlugin.IMAGELENGTH: 128,
},
)
else:
fp.write(chunk.tobytes())

assert_image_equal_tofile(im, tmpfile)

def test_close_on_load_exclusive(self, tmp_path):
# similar to test_fd_leak, but runs on unixlike os
tmpfile = str(tmp_path / "temp.tif")
Expand Down
3 changes: 3 additions & 0 deletions Tests/test_file_tiff_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def test_write_metadata(tmp_path):
"""Test metadata writing through the python code"""
with Image.open("Tests/images/hopper.tif") as img:
f = str(tmp_path / "temp.tiff")
del img.tag[278]
img.save(f, tiffinfo=img.tag)

original = img.tag_v2.named()
Expand Down Expand Up @@ -159,9 +160,11 @@ def test_change_stripbytecounts_tag_type(tmp_path):
out = str(tmp_path / "temp.tiff")
with Image.open("Tests/images/hopper.tif") as im:
info = im.tag_v2
del info[278]

# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
im = im.resize((500, 500))
info[TiffImagePlugin.IMAGEWIDTH] = im.width

# STRIPBYTECOUNTS can be a SHORT or a LONG
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
Expand Down
5 changes: 5 additions & 0 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,11 @@ def test_fli_overrun2(self):
except OSError as e:
assert str(e) == "buffer overrun when reading image file"

def test_exit_fp(self):
with Image.new("L", (1, 1)) as im:
pass
assert not hasattr(im, "fp")

def test_close_graceful(self, caplog):
with Image.open("Tests/images/hopper.jpg") as im:
copy = im.copy()
Expand Down
2 changes: 2 additions & 0 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,8 @@ def test_too_many_characters(font):
imagefont.getlength("A" * 1_000_001)
with pytest.raises(ValueError):
imagefont.getbbox("A" * 1_000_001)
with pytest.raises(ValueError):
imagefont.getmask("A" * 1_000_001)


@pytest.mark.parametrize(
Expand Down
2 changes: 1 addition & 1 deletion docs/COPYING
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is

Pillow is the friendly PIL fork. It is

Copyright © 2010-2023 by Jeffrey A. Clark (Alex) and contributors
Copyright © 2010-2024 by Jeffrey A. Clark (Alex) and contributors

Like PIL, Pillow is licensed under the open source PIL
Software License:
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
# General information about the project.
project = "Pillow (PIL Fork)"
copyright = (
"1995-2011 Fredrik Lundh, 2010-2023 Jeffrey A. Clark (Alex) and contributors"
"1995-2011 Fredrik Lundh, 2010-2024 Jeffrey A. Clark (Alex) and contributors"
)
author = "Fredrik Lundh, Jeffrey A. Clark (Alex), contributors"

Expand Down
11 changes: 11 additions & 0 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ ImageFile.raise_oserror
error codes returned by a codec's ``decode()`` method, which ImageFile already does
automatically.

IptcImageFile helper functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. deprecated:: 10.2.0

The functions ``IptcImageFile.dump`` and ``IptcImageFile.i``, and the constant
``IptcImageFile.PAD`` have been deprecated and will be removed in Pillow
12.0.0 (2025-10-15). These are undocumented helper functions intended
for internal use, so there is no replacement. They can each be replaced
by a single line of code using builtin functions in Python.

Removed features
----------------

Expand Down
25 changes: 20 additions & 5 deletions docs/releasenotes/10.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ ImageFile.raise_oserror
error codes returned by a codec's ``decode()`` method, which ImageFile already does
automatically.

TODO
^^^^
IptcImageFile helper functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

TODO
The functions ``IptcImageFile.dump`` and ``IptcImageFile.i``, and the constant
``IptcImageFile.PAD`` have been deprecated and will be removed in Pillow
12.0.0 (2025-10-15). These are undocumented helper functions intended
for internal use, so there is no replacement. They can each be replaced
by a single line of code using builtin functions in Python.

API Changes
===========
Expand Down Expand Up @@ -62,8 +66,19 @@ output only the quantization and Huffman tables for the image.
Security
========

Restricted environment keys for ImageMath.eval
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ImageFont.getmask: Applied ImageFont.MAX_STRING_LENGTH
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To protect against potential DOS attacks when using arbitrary strings as text input,
Pillow will now raise a :py:exc:`ValueError` if the number of characters passed into
:py:meth:`PIL.ImageFont.ImageFont.getmask` is over a certain limit,
:py:data:`PIL.ImageFont.MAX_STRING_LENGTH`.

This threshold can be changed by setting :py:data:`PIL.ImageFont.MAX_STRING_LENGTH`. It
can be disabled by setting ``ImageFont.MAX_STRING_LENGTH = None``.

ImageMath.eval: Restricted environment keys
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

:cve:`2023-50447`: If an attacker has control over the keys passed to the
``environment`` argument of :py:meth:`PIL.ImageMath.eval`, they may be able to execute
Expand Down
Loading

0 comments on commit 3ef7b93

Please sign in to comment.