Skip to content

Commit 095f599

Browse files
authored
Merge pull request #8826 from radarhere/gimp
Added GimpPaletteFile frombytes() to allow for unlimited parsing
2 parents 3c185d1 + 21ff960 commit 095f599

File tree

3 files changed

+322
-8
lines changed

3 files changed

+322
-8
lines changed

Tests/images/full_gimp_palette.gpl

+260
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
GIMP Palette
2+
Name: fullpalette
3+
Columns: 4
4+
#
5+
0 0 0 Index 0
6+
1 1 1 Index 1
7+
2 2 2 Index 2
8+
3 3 3 Index 3
9+
4 4 4 Index 4
10+
5 5 5 Index 5
11+
6 6 6 Index 6
12+
7 7 7 Index 7
13+
8 8 8 Index 8
14+
9 9 9 Index 9
15+
10 10 10 Index 10
16+
11 11 11 Index 11
17+
12 12 12 Index 12
18+
13 13 13 Index 13
19+
14 14 14 Index 14
20+
15 15 15 Index 15
21+
16 16 16 Index 16
22+
17 17 17 Index 17
23+
18 18 18 Index 18
24+
19 19 19 Index 19
25+
20 20 20 Index 20
26+
21 21 21 Index 21
27+
22 22 22 Index 22
28+
23 23 23 Index 23
29+
24 24 24 Index 24
30+
25 25 25 Index 25
31+
26 26 26 Index 26
32+
27 27 27 Index 27
33+
28 28 28 Index 28
34+
29 29 29 Index 29
35+
30 30 30 Index 30
36+
31 31 31 Index 31
37+
32 32 32 Index 32
38+
33 33 33 Index 33
39+
34 34 34 Index 34
40+
35 35 35 Index 35
41+
36 36 36 Index 36
42+
37 37 37 Index 37
43+
38 38 38 Index 38
44+
39 39 39 Index 39
45+
40 40 40 Index 40
46+
41 41 41 Index 41
47+
42 42 42 Index 42
48+
43 43 43 Index 43
49+
44 44 44 Index 44
50+
45 45 45 Index 45
51+
46 46 46 Index 46
52+
47 47 47 Index 47
53+
48 48 48 Index 48
54+
49 49 49 Index 49
55+
50 50 50 Index 50
56+
51 51 51 Index 51
57+
52 52 52 Index 52
58+
53 53 53 Index 53
59+
54 54 54 Index 54
60+
55 55 55 Index 55
61+
56 56 56 Index 56
62+
57 57 57 Index 57
63+
58 58 58 Index 58
64+
59 59 59 Index 59
65+
60 60 60 Index 60
66+
61 61 61 Index 61
67+
62 62 62 Index 62
68+
63 63 63 Index 63
69+
64 64 64 Index 64
70+
65 65 65 Index 65
71+
66 66 66 Index 66
72+
67 67 67 Index 67
73+
68 68 68 Index 68
74+
69 69 69 Index 69
75+
70 70 70 Index 70
76+
71 71 71 Index 71
77+
72 72 72 Index 72
78+
73 73 73 Index 73
79+
74 74 74 Index 74
80+
75 75 75 Index 75
81+
76 76 76 Index 76
82+
77 77 77 Index 77
83+
78 78 78 Index 78
84+
79 79 79 Index 79
85+
80 80 80 Index 80
86+
81 81 81 Index 81
87+
82 82 82 Index 82
88+
83 83 83 Index 83
89+
84 84 84 Index 84
90+
85 85 85 Index 85
91+
86 86 86 Index 86
92+
87 87 87 Index 87
93+
88 88 88 Index 88
94+
89 89 89 Index 89
95+
90 90 90 Index 90
96+
91 91 91 Index 91
97+
92 92 92 Index 92
98+
93 93 93 Index 93
99+
94 94 94 Index 94
100+
95 95 95 Index 95
101+
96 96 96 Index 96
102+
97 97 97 Index 97
103+
98 98 98 Index 98
104+
99 99 99 Index 99
105+
100 100 100 Index 100
106+
101 101 101 Index 101
107+
102 102 102 Index 102
108+
103 103 103 Index 103
109+
104 104 104 Index 104
110+
105 105 105 Index 105
111+
106 106 106 Index 106
112+
107 107 107 Index 107
113+
108 108 108 Index 108
114+
109 109 109 Index 109
115+
110 110 110 Index 110
116+
111 111 111 Index 111
117+
112 112 112 Index 112
118+
113 113 113 Index 113
119+
114 114 114 Index 114
120+
115 115 115 Index 115
121+
116 116 116 Index 116
122+
117 117 117 Index 117
123+
118 118 118 Index 118
124+
119 119 119 Index 119
125+
120 120 120 Index 120
126+
121 121 121 Index 121
127+
122 122 122 Index 122
128+
123 123 123 Index 123
129+
124 124 124 Index 124
130+
125 125 125 Index 125
131+
126 126 126 Index 126
132+
127 127 127 Index 127
133+
128 128 128 Index 128
134+
129 129 129 Index 129
135+
130 130 130 Index 130
136+
131 131 131 Index 131
137+
132 132 132 Index 132
138+
133 133 133 Index 133
139+
134 134 134 Index 134
140+
135 135 135 Index 135
141+
136 136 136 Index 136
142+
137 137 137 Index 137
143+
138 138 138 Index 138
144+
139 139 139 Index 139
145+
140 140 140 Index 140
146+
141 141 141 Index 141
147+
142 142 142 Index 142
148+
143 143 143 Index 143
149+
144 144 144 Index 144
150+
145 145 145 Index 145
151+
146 146 146 Index 146
152+
147 147 147 Index 147
153+
148 148 148 Index 148
154+
149 149 149 Index 149
155+
150 150 150 Index 150
156+
151 151 151 Index 151
157+
152 152 152 Index 152
158+
153 153 153 Index 153
159+
154 154 154 Index 154
160+
155 155 155 Index 155
161+
156 156 156 Index 156
162+
157 157 157 Index 157
163+
158 158 158 Index 158
164+
159 159 159 Index 159
165+
160 160 160 Index 160
166+
161 161 161 Index 161
167+
162 162 162 Index 162
168+
163 163 163 Index 163
169+
164 164 164 Index 164
170+
165 165 165 Index 165
171+
166 166 166 Index 166
172+
167 167 167 Index 167
173+
168 168 168 Index 168
174+
169 169 169 Index 169
175+
170 170 170 Index 170
176+
171 171 171 Index 171
177+
172 172 172 Index 172
178+
173 173 173 Index 173
179+
174 174 174 Index 174
180+
175 175 175 Index 175
181+
176 176 176 Index 176
182+
177 177 177 Index 177
183+
178 178 178 Index 178
184+
179 179 179 Index 179
185+
180 180 180 Index 180
186+
181 181 181 Index 181
187+
182 182 182 Index 182
188+
183 183 183 Index 183
189+
184 184 184 Index 184
190+
185 185 185 Index 185
191+
186 186 186 Index 186
192+
187 187 187 Index 187
193+
188 188 188 Index 188
194+
189 189 189 Index 189
195+
190 190 190 Index 190
196+
191 191 191 Index 191
197+
192 192 192 Index 192
198+
193 193 193 Index 193
199+
194 194 194 Index 194
200+
195 195 195 Index 195
201+
196 196 196 Index 196
202+
197 197 197 Index 197
203+
198 198 198 Index 198
204+
199 199 199 Index 199
205+
200 200 200 Index 200
206+
201 201 201 Index 201
207+
202 202 202 Index 202
208+
203 203 203 Index 203
209+
204 204 204 Index 204
210+
205 205 205 Index 205
211+
206 206 206 Index 206
212+
207 207 207 Index 207
213+
208 208 208 Index 208
214+
209 209 209 Index 209
215+
210 210 210 Index 210
216+
211 211 211 Index 211
217+
212 212 212 Index 212
218+
213 213 213 Index 213
219+
214 214 214 Index 214
220+
215 215 215 Index 215
221+
216 216 216 Index 216
222+
217 217 217 Index 217
223+
218 218 218 Index 218
224+
219 219 219 Index 219
225+
220 220 220 Index 220
226+
221 221 221 Index 221
227+
222 222 222 Index 222
228+
223 223 223 Index 223
229+
224 224 224 Index 224
230+
225 225 225 Index 225
231+
226 226 226 Index 226
232+
227 227 227 Index 227
233+
228 228 228 Index 228
234+
229 229 229 Index 229
235+
230 230 230 Index 230
236+
231 231 231 Index 231
237+
232 232 232 Index 232
238+
233 233 233 Index 233
239+
234 234 234 Index 234
240+
235 235 235 Index 235
241+
236 236 236 Index 236
242+
237 237 237 Index 237
243+
238 238 238 Index 238
244+
239 239 239 Index 239
245+
240 240 240 Index 240
246+
241 241 241 Index 241
247+
242 242 242 Index 242
248+
243 243 243 Index 243
249+
244 244 244 Index 244
250+
245 245 245 Index 245
251+
246 246 246 Index 246
252+
247 247 247 Index 247
253+
248 248 248 Index 248
254+
249 249 249 Index 249
255+
250 250 250 Index 250
256+
251 251 251 Index 251
257+
252 252 252 Index 252
258+
253 253 253 Index 253
259+
254 254 254 Index 254
260+
255 255 255 Index 255

Tests/test_file_gimppalette.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from io import BytesIO
4+
35
import pytest
46

57
from PIL.GimpPaletteFile import GimpPaletteFile
@@ -14,22 +16,57 @@ def test_sanity() -> None:
1416
GimpPaletteFile(fp)
1517

1618
with open("Tests/images/bad_palette_file.gpl", "rb") as fp:
17-
with pytest.raises(SyntaxError):
19+
with pytest.raises(SyntaxError, match="bad palette file"):
1820
GimpPaletteFile(fp)
1921

2022
with open("Tests/images/bad_palette_entry.gpl", "rb") as fp:
21-
with pytest.raises(ValueError):
23+
with pytest.raises(ValueError, match="bad palette entry"):
2224
GimpPaletteFile(fp)
2325

2426

25-
def test_get_palette() -> None:
27+
@pytest.mark.parametrize(
28+
"filename, size", (("custom_gimp_palette.gpl", 8), ("full_gimp_palette.gpl", 256))
29+
)
30+
def test_get_palette(filename: str, size: int) -> None:
2631
# Arrange
27-
with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp:
32+
with open("Tests/images/" + filename, "rb") as fp:
2833
palette_file = GimpPaletteFile(fp)
2934

3035
# Act
3136
palette, mode = palette_file.getpalette()
3237

3338
# Assert
3439
assert mode == "RGB"
35-
assert len(palette) / 3 == 8
40+
assert len(palette) / 3 == size
41+
42+
43+
def test_frombytes() -> None:
44+
# Test that __init__ stops reading after 260 lines
45+
with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp:
46+
custom_data = fp.read()
47+
custom_data += b"#\n" * 300 + b" 0 0 0 Index 12"
48+
b = BytesIO(custom_data)
49+
palette = GimpPaletteFile(b)
50+
assert len(palette.palette) / 3 == 8
51+
52+
# Test that __init__ only reads 256 entries
53+
with open("Tests/images/full_gimp_palette.gpl", "rb") as fp:
54+
full_data = fp.read()
55+
data = full_data.replace(b"#\n", b"") + b" 0 0 0 Index 256"
56+
b = BytesIO(data)
57+
palette = GimpPaletteFile(b)
58+
assert len(palette.palette) / 3 == 256
59+
60+
# Test that frombytes() can read beyond that
61+
palette = GimpPaletteFile.frombytes(data)
62+
assert len(palette.palette) / 3 == 257
63+
64+
# Test that __init__ raises an error if a comment is too long
65+
data = full_data[:-1] + b"a" * 100
66+
b = BytesIO(data)
67+
with pytest.raises(SyntaxError, match="bad palette file"):
68+
palette = GimpPaletteFile(b)
69+
70+
# Test that frombytes() can read the data regardless
71+
palette = GimpPaletteFile.frombytes(data)
72+
assert len(palette.palette) / 3 == 256

src/PIL/GimpPaletteFile.py

+20-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from __future__ import annotations
1717

1818
import re
19+
from io import BytesIO
1920
from typing import IO
2021

2122

@@ -24,21 +25,26 @@ class GimpPaletteFile:
2425

2526
rawmode = "RGB"
2627

27-
def __init__(self, fp: IO[bytes]) -> None:
28+
def _read(self, fp: IO[bytes], limit: bool = True) -> None:
2829
if not fp.readline().startswith(b"GIMP Palette"):
2930
msg = "not a GIMP palette file"
3031
raise SyntaxError(msg)
3132

3233
palette: list[int] = []
33-
for _ in range(256):
34+
i = 0
35+
while True:
36+
if limit and i == 256 + 3:
37+
break
38+
39+
i += 1
3440
s = fp.readline()
3541
if not s:
3642
break
3743

3844
# skip fields and comment lines
3945
if re.match(rb"\w+:|#", s):
4046
continue
41-
if len(s) > 100:
47+
if limit and len(s) > 100:
4248
msg = "bad palette file"
4349
raise SyntaxError(msg)
4450

@@ -48,8 +54,19 @@ def __init__(self, fp: IO[bytes]) -> None:
4854
raise ValueError(msg)
4955

5056
palette += (int(v[i]) for i in range(3))
57+
if limit and len(palette) == 768:
58+
break
5159

5260
self.palette = bytes(palette)
5361

62+
def __init__(self, fp: IO[bytes]) -> None:
63+
self._read(fp)
64+
65+
@classmethod
66+
def frombytes(cls, data: bytes) -> GimpPaletteFile:
67+
self = cls.__new__(cls)
68+
self._read(BytesIO(data), False)
69+
return self
70+
5471
def getpalette(self) -> tuple[bytes, str]:
5572
return self.palette, self.rawmode

0 commit comments

Comments
 (0)