From a12389bf35c13446bdf06f70630c33b43f328833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20S=2E=20O=2E=20Bueno?= Date: Thu, 6 Oct 2022 11:35:41 -0300 Subject: [PATCH] Provide way to load big palettes; --- Tests/images/custom_gimp_palette.gpl | 2 +- Tests/test_file_gimppalette.py | 25 ++++++++++++++++++++++++ src/PIL/GimpPaletteFile.py | 29 +++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Tests/images/custom_gimp_palette.gpl b/Tests/images/custom_gimp_palette.gpl index 229c45d3e..ced2f2431 100644 --- a/Tests/images/custom_gimp_palette.gpl +++ b/Tests/images/custom_gimp_palette.gpl @@ -1,7 +1,7 @@ GIMP Palette Name: custompalette Columns: 4 -# +# 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 diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py index 241273f8c..23bb8aaab 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -20,6 +20,21 @@ def test_sanity(): GimpPaletteFile(fp) +def test_large_file_is_truncated(): + import warnings + from unittest.mock import patch + try: + original_value = GimpPaletteFile._max_file_size + GimpPaletteFile._max_file_size = 100 + with warnings.catch_warnings(): + warnings.simplefilter("error") + with pytest.raises(UserWarning): + with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: + palette_file = GimpPaletteFile(fp) + + finally: + GimpPaletteFile._max_file_size = original_value + def test_get_palette(): # Arrange with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: @@ -43,3 +58,13 @@ def test_get_palette(): expected_palette += bytes(color) assert palette == expected_palette assert mode == "RGB" + + +def test_n_colors(): + # Arrange + with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: + palette_file = GimpPaletteFile(fp) + + palette, _ = palette_file.getpalette() + assert len(palette) == 24 + assert palette_file.n_colors == 8 diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index cdc28f4e4..ff5d6e10f 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -15,6 +15,7 @@ # import re +import warnings from ._binary import o8 @@ -24,25 +25,39 @@ class GimpPaletteFile: rawmode = "RGB" + #: override if reading larger palettes is needed + max_colors = 256 + _max_line_size = 100 + _max_file_size = 2 ** 20 + def __init__(self, fp): if fp.readline()[:12] != b"GIMP Palette": raise SyntaxError("not a GIMP palette file") - self.palette = b"" - while len(self.palette) < 768: + read = 0 - s = fp.readline() + self.palette = b"" + while len(self.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: raise SyntaxError("bad palette file") - v = s.split() + # 4th column is color name and may contain spaces. + v = s.split(None, 4) if len(v) < 3: raise ValueError("bad palette entry") for i in range(3): @@ -51,3 +66,7 @@ class GimpPaletteFile: def getpalette(self): return self.palette, self.rawmode + + @property + def n_colors(self): + return len(self.palette) / 3