Skip to content

Commit 06b0e64

Browse files
authored
Merge pull request #6300 from raygard/comment_correct_placement
Only write GIF comments at the beginning of the file
2 parents 68e39cb + 4cdbeaf commit 06b0e64

File tree

3 files changed

+64
-12
lines changed

3 files changed

+64
-12
lines changed

Tests/images/second_frame_comment.gif

2.34 KB
Loading

Tests/test_file_gif.py

+44
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,50 @@ def test_read_multiple_comment_blocks():
837837
assert im.info["comment"] == b"Test comment 1\nTest comment 2"
838838

839839

840+
def test_empty_string_comment(tmp_path):
841+
out = str(tmp_path / "temp.gif")
842+
with Image.open("Tests/images/chi.gif") as im:
843+
assert "comment" in im.info
844+
845+
# Empty string comment should suppress existing comment
846+
im.save(out, save_all=True, comment="")
847+
848+
with Image.open(out) as reread:
849+
for frame in ImageSequence.Iterator(reread):
850+
assert "comment" not in frame.info
851+
852+
853+
def test_retain_comment_in_subsequent_frames(tmp_path):
854+
# Test that a comment block at the beginning is kept
855+
with Image.open("Tests/images/chi.gif") as im:
856+
for frame in ImageSequence.Iterator(im):
857+
assert frame.info["comment"] == b"Created with GIMP"
858+
859+
with Image.open("Tests/images/second_frame_comment.gif") as im:
860+
assert "comment" not in im.info
861+
862+
# Test that a comment in the middle is read
863+
im.seek(1)
864+
assert im.info["comment"] == b"Comment in the second frame"
865+
866+
# Test that it is still present in a later frame
867+
im.seek(2)
868+
assert im.info["comment"] == b"Comment in the second frame"
869+
870+
# Test that rewinding removes the comment
871+
im.seek(0)
872+
assert "comment" not in im.info
873+
874+
# Test that a saved image keeps the comment
875+
out = str(tmp_path / "temp.gif")
876+
with Image.open("Tests/images/dispose_prev.gif") as im:
877+
im.save(out, save_all=True, comment="Test")
878+
879+
with Image.open(out) as reread:
880+
for frame in ImageSequence.Iterator(reread):
881+
assert frame.info["comment"] == b"Test"
882+
883+
840884
def test_version(tmp_path):
841885
out = str(tmp_path / "temp.gif")
842886

src/PIL/GifImagePlugin.py

+20-12
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ def _seek(self, frame, update_image=True):
163163
self.__frame = -1
164164
self._fp.seek(self.__rewind)
165165
self.disposal_method = 0
166+
if "comment" in self.info:
167+
del self.info["comment"]
166168
else:
167169
# ensure that the previous frame was loaded
168170
if self.tile and update_image:
@@ -230,7 +232,7 @@ def _seek(self, frame, update_image=True):
230232
#
231233
comment = b""
232234

233-
# Collect one comment block
235+
# Read this comment block
234236
while block:
235237
comment += block
236238
block = self.data()
@@ -395,7 +397,9 @@ def _rgb(color):
395397
)
396398
]
397399

398-
for k in ["duration", "comment", "extension", "loop"]:
400+
if info.get("comment"):
401+
self.info["comment"] = info["comment"]
402+
for k in ["duration", "extension", "loop"]:
399403
if k in info:
400404
self.info[k] = info[k]
401405
elif k in self.info:
@@ -712,15 +716,6 @@ def _write_local_header(fp, im, offset, flags):
712716
+ o8(0)
713717
)
714718

715-
if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]):
716-
fp.write(b"!" + o8(254)) # extension intro
717-
comment = im.encoderinfo["comment"]
718-
if isinstance(comment, str):
719-
comment = comment.encode()
720-
for i in range(0, len(comment), 255):
721-
subblock = comment[i : i + 255]
722-
fp.write(o8(len(subblock)) + subblock)
723-
fp.write(o8(0))
724719
if "loop" in im.encoderinfo:
725720
number_of_loops = im.encoderinfo["loop"]
726721
fp.write(
@@ -925,7 +920,7 @@ def _get_global_header(im, info):
925920
palette_bytes = _get_palette_bytes(im)
926921
color_table_size = _get_color_table_size(palette_bytes)
927922

928-
return [
923+
header = [
929924
b"GIF" # signature
930925
+ version # version
931926
+ o16(im.size[0]) # canvas width
@@ -938,6 +933,19 @@ def _get_global_header(im, info):
938933
# Global Color Table
939934
_get_header_palette(palette_bytes),
940935
]
936+
if info.get("comment"):
937+
comment_block = b"!" + o8(254) # extension intro
938+
939+
comment = info["comment"]
940+
if isinstance(comment, str):
941+
comment = comment.encode()
942+
for i in range(0, len(comment), 255):
943+
subblock = comment[i : i + 255]
944+
comment_block += o8(len(subblock)) + subblock
945+
946+
comment_block += o8(0)
947+
header.append(comment_block)
948+
return header
941949

942950

943951
def _write_frame_data(fp, im_frame, offset, params):

0 commit comments

Comments
 (0)