diff --git a/Tests/images/custom_gimp_palette.gpl b/Tests/images/custom_gimp_palette.gpl index 08ea7002849..ced2f243176 100644 --- a/Tests/images/custom_gimp_palette.gpl +++ b/Tests/images/custom_gimp_palette.gpl @@ -1,12 +1,12 @@ GIMP Palette Name: custompalette Columns: 4 -# - 0 0 0 Index 3 - 65 38 30 Index 4 -103 62 49 Index 6 - 79 73 72 Index 7 -114 101 97 Index 8 -208 127 100 Index 9 -151 144 142 Index 10 -221 207 199 Index 11 +# Original written by David Wetz in https://stackoverflow.com/questions/815836/im-creating-a-program-that-generates-a-palette-from-a-true-color-image-need-hel/815855#815855 + 0 0 0 Index 0 + 65 38 30 Index 1 +103 62 49 Index 2 + 79 73 72 Index 3 +114 101 97 Index 4 +208 127 100 Index 5 +151 144 142 Index 6 +221 207 199 Index 7 diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py index ff9cc91c5c2..4867753bc50 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -22,6 +22,13 @@ def test_sanity() -> None: GimpPaletteFile(fp) +def test_large_file_is_truncated(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(GimpPaletteFile, "_max_file_size", 100) + with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: + with pytest.warns(UserWarning): + GimpPaletteFile(fp) + + def test_get_palette() -> None: # Arrange with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: @@ -31,5 +38,18 @@ def test_get_palette() -> None: palette, mode = palette_file.getpalette() # Assert + expected_palette: list[int] = [] + for color in ( + (0, 0, 0), + (65, 38, 30), + (103, 62, 49), + (79, 73, 72), + (114, 101, 97), + (208, 127, 100), + (151, 144, 142), + (221, 207, 199), + ): + expected_palette += color + assert palette == bytes(expected_palette) assert mode == "RGB" assert len(palette) / 3 == 8 diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index 0f079f45758..9b6fac83b1c 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -16,6 +16,7 @@ from __future__ import annotations import re +import warnings from typing import IO @@ -24,24 +25,40 @@ class GimpPaletteFile: rawmode = "RGB" + #: override if reading larger palettes is needed + max_colors = 256 + _max_line_size = 100 + _max_file_size = 2**20 # 1MB + def __init__(self, fp: IO[bytes]) -> None: if not fp.readline().startswith(b"GIMP Palette"): msg = "not a GIMP palette file" raise SyntaxError(msg) + read = 0 + palette: list[int] = [] - for _ in range(256): - s = fp.readline() + while len(palette) < 3 * self.max_colors: + + s = fp.readline(self._max_file_size) if not s: break + read += len(s) + if read >= self._max_file_size: + warnings.warn( + f"Palette file truncated at {self._max_file_size - len(s)} bytes" + ) + break + # skip fields and comment lines if re.match(rb"\w+:|#", s): continue - if len(s) > 100: + if len(s) > self._max_line_size: msg = "bad palette file" raise SyntaxError(msg) + # 4th column is color name and may contain spaces. v = s.split(maxsplit=3) if len(v) < 3: msg = "bad palette entry"