Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .ci/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ packages=(
libtiff-dev
meson
nasm
netpbm # netpbm provides ppmquant and ppmtogif for GifImagePlugin._save_netpbm
python3-tk
sway
wl-clipboard
Expand Down
4 changes: 0 additions & 4 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,6 @@ def djpeg_available() -> bool:
return False


def netpbm_available() -> bool:
return bool(shutil.which("ppmquant") and shutil.which("ppmtogif"))


def magick_command() -> list[str] | None:
if sys.platform == "win32":
magickhome = os.environ.get("MAGICK_HOME")
Expand Down
25 changes: 0 additions & 25 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
assert_image_similar,
hopper,
is_pypy,
netpbm_available,
)

TYPE_CHECKING = False
Expand Down Expand Up @@ -397,30 +396,6 @@ def roundtrip(im: Image.Image, **kwargs: bool) -> Image.Image:
assert_image_equal(im_rgb, reloaded)


@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
with Image.open(TEST_GIF) as img:
img_rgb = img.convert("RGB")

tempfile = str(tmp_path / "temp.gif")
b = BytesIO()
GifImagePlugin._save_netpbm(img_rgb, b, tempfile)
with Image.open(tempfile) as reloaded:
assert_image_equal(img_rgb, reloaded.convert("RGB"))


@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
with Image.open(TEST_GIF) as img:
img_l = img.convert("L")

tempfile = str(tmp_path / "temp.gif")
b = BytesIO()
GifImagePlugin._save_netpbm(img_l, b, tempfile)
with Image.open(tempfile) as reloaded:
assert_image_equal(img_l, reloaded.convert("L"))


def test_seek() -> None:
with Image.open("Tests/images/dispose_none.gif") as img:
assert isinstance(img, GifImagePlugin.GifImageFile)
Expand Down
19 changes: 2 additions & 17 deletions Tests/test_shell_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import pytest

from PIL import GifImagePlugin, Image, JpegImagePlugin
from PIL import Image, JpegImagePlugin

from .helper import djpeg_available, is_win32, netpbm_available
from .helper import djpeg_available, is_win32

TYPE_CHECKING = False
if TYPE_CHECKING:
Expand All @@ -16,7 +16,6 @@
from typing import IO

TEST_JPG = "Tests/images/hopper.jpg"
TEST_GIF = "Tests/images/hopper.gif"

test_filenames = ("temp_';", 'temp_";', "temp_'\"|", "temp_'\"||", "temp_'\"&&")

Expand Down Expand Up @@ -46,17 +45,3 @@ def test_load_djpeg_filename(self, tmp_path: Path) -> None:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
with pytest.warns(DeprecationWarning, match="load_djpeg"):
im.load_djpeg()

@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
with Image.open(TEST_GIF) as im:
im_rgb = im.convert("RGB")
self.assert_save_filename_check(
tmp_path, im_rgb, GifImagePlugin._save_netpbm
)

@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
with Image.open(TEST_GIF) as im:
im_l = im.convert("L")
self.assert_save_filename_check(tmp_path, im_l, GifImagePlugin._save_netpbm)
56 changes: 0 additions & 56 deletions src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@

import itertools
import math
import os
import subprocess
from enum import IntEnum
from functools import cached_property
from typing import NamedTuple, cast
Expand Down Expand Up @@ -881,54 +879,6 @@ def _write_local_header(
fp.write(o8(8)) # bits


def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# Unused by default.
# To use, uncomment the register_save call at the end of the file.
#
# If you need real GIF compression and/or RGB quantization, you
# can use the external NETPBM/PBMPLUS utilities. See comments
# below for information on how to enable this.
tempfile = im._dump()

try:
with open(filename, "wb") as f:
if im.mode != "RGB":
subprocess.check_call(
["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL
)
else:
# Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
quant_cmd = ["ppmquant", "256", tempfile]
togif_cmd = ["ppmtogif"]
quant_proc = subprocess.Popen(
quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
)
togif_proc = subprocess.Popen(
togif_cmd,
stdin=quant_proc.stdout,
stdout=f,
stderr=subprocess.DEVNULL,
)

# Allow ppmquant to receive SIGPIPE if ppmtogif exits
assert quant_proc.stdout is not None
quant_proc.stdout.close()

retcode = quant_proc.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, quant_cmd)

retcode = togif_proc.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, togif_cmd)
finally:
try:
os.unlink(tempfile)
except OSError:
pass


# Force optimization so that we can test performance against
# cases where it took lots of memory and time previously.
_FORCE_OPTIMIZE = False
Expand Down Expand Up @@ -1223,9 +1173,3 @@ def write(self, data: Buffer) -> int:
Image.register_save_all(GifImageFile.format, _save_all)
Image.register_extension(GifImageFile.format, ".gif")
Image.register_mime(GifImageFile.format, "image/gif")

#
# Uncomment the following line if you wish to use NETPBM/PBMPLUS
# instead of the built-in "uncompressed" GIF encoder

# Image.register_save(GifImageFile.format, _save_netpbm)