Skip to content

Commit d263e3b

Browse files
authored
Fix saving MPO with more than one appended image (#8979)
2 parents a370209 + 5732a86 commit d263e3b

File tree

2 files changed

+21
-14
lines changed

2 files changed

+21
-14
lines changed

Tests/test_file_mpo.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -296,16 +296,18 @@ def test_save_all() -> None:
296296
assert_image_similar(im, im_reloaded, 30)
297297

298298
im = Image.new("RGB", (1, 1))
299-
im2 = Image.new("RGB", (1, 1), "#f00")
300-
im_reloaded = roundtrip(im, save_all=True, append_images=[im2])
301-
302-
assert_image_equal(im, im_reloaded)
303-
assert isinstance(im_reloaded, MpoImagePlugin.MpoImageFile)
304-
assert im_reloaded.mpinfo is not None
305-
assert im_reloaded.mpinfo[45056] == b"0100"
306-
307-
im_reloaded.seek(1)
308-
assert_image_similar(im2, im_reloaded, 1)
299+
for colors in (("#f00",), ("#f00", "#0f0")):
300+
append_images = [Image.new("RGB", (1, 1), color) for color in colors]
301+
im_reloaded = roundtrip(im, save_all=True, append_images=append_images)
302+
303+
assert_image_equal(im, im_reloaded)
304+
assert isinstance(im_reloaded, MpoImagePlugin.MpoImageFile)
305+
assert im_reloaded.mpinfo is not None
306+
assert im_reloaded.mpinfo[45056] == b"0100"
307+
308+
for im_expected in append_images:
309+
im_reloaded.seek(im_reloaded.tell() + 1)
310+
assert_image_similar(im_reloaded, im_expected, 1)
309311

310312
# Test that a single frame image will not be saved as an MPO
311313
jpg = roundtrip(im, save_all=True)

src/PIL/MpoImagePlugin.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#
2020
from __future__ import annotations
2121

22-
import itertools
2322
import os
2423
import struct
2524
from typing import IO, Any, cast
@@ -47,12 +46,18 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
4746

4847
mpf_offset = 28
4948
offsets: list[int] = []
50-
for imSequence in itertools.chain([im], append_images):
51-
for im_frame in ImageSequence.Iterator(imSequence):
49+
im_sequences = [im, *append_images]
50+
total = sum(getattr(seq, "n_frames", 1) for seq in im_sequences)
51+
for im_sequence in im_sequences:
52+
for im_frame in ImageSequence.Iterator(im_sequence):
5253
if not offsets:
5354
# APP2 marker
55+
ifd_length = 66 + 16 * total
5456
im_frame.encoderinfo["extra"] = (
55-
b"\xff\xe2" + struct.pack(">H", 6 + 82) + b"MPF\0" + b" " * 82
57+
b"\xff\xe2"
58+
+ struct.pack(">H", 6 + ifd_length)
59+
+ b"MPF\0"
60+
+ b" " * ifd_length
5661
)
5762
exif = im_frame.encoderinfo.get("exif")
5863
if isinstance(exif, Image.Exif):

0 commit comments

Comments
 (0)