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 caec9cf2115..e6587ff7c0b 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -20,6 +20,17 @@ def test_sanity(): GimpPaletteFile(fp) +def test_large_file_is_truncated(): + original_max_file_size = GimpPaletteFile._max_file_size + try: + GimpPaletteFile._max_file_size = 100 + with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: + with pytest.warns(UserWarning): + GimpPaletteFile(fp) + finally: + GimpPaletteFile._max_file_size = original_max_file_size + + def test_get_palette(): # Arrange with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: @@ -29,4 +40,17 @@ def test_get_palette(): palette, mode = palette_file.getpalette() # Assert + expected_palette = [] + 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" diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index 2e9cbe58d20..1f7605866ef 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -15,8 +15,7 @@ # import re - -from ._binary import o8 +import warnings class GimpPaletteFile: @@ -24,35 +23,49 @@ class GimpPaletteFile: rawmode = "RGB" - def __init__(self, fp): + #: override if reading larger palettes is needed + max_colors = 256 + _max_line_size = 100 + _max_file_size = 2**20 # 1MB - self.palette = [o8(i) * 3 for i in range(256)] + def __init__(self, fp): if fp.readline()[:12] != b"GIMP Palette": msg = "not a GIMP palette file" raise SyntaxError(msg) - for i in range(256): + read = 0 - s = fp.readline() + self.palette = [] + 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: msg = "bad palette file" raise SyntaxError(msg) - v = tuple(map(int, s.split()[:3])) - if len(v) != 3: + # 4th column is color name and may contain spaces. + v = s.split(maxsplit=3) + if len(v) < 3: msg = "bad palette entry" raise ValueError(msg) - self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + self.palette += (int(v[0]), int(v[1]), int(v[2])) - self.palette = b"".join(self.palette) + self.palette = bytes(self.palette) def getpalette(self):