Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions Tests/test_file_mpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,24 @@ def test_save_all() -> None:
def test_save_xmp() -> None:
im = Image.new("RGB", (1, 1))
im2 = Image.new("RGB", (1, 1), "#f00")

def roundtrip_xmp() -> list[Any]:
im_reloaded = roundtrip(im, xmp=b"Default", save_all=True, append_images=[im2])
xmp = [im_reloaded.info["xmp"]]
im_reloaded.seek(1)
return xmp + [im_reloaded.info["xmp"]]

# Use the save parameters for all frames by default
assert roundtrip_xmp() == [b"Default", b"Default"]

# Specify a value for the first frame
im.encoderinfo = {"xmp": b"First frame"}
assert roundtrip_xmp() == [b"First frame", b"Default"]
del im.encoderinfo

# Specify value for the second frame
im2.encoderinfo = {"xmp": b"Second frame"}
im_reloaded = roundtrip(im, xmp=b"First frame", save_all=True, append_images=[im2])
assert roundtrip_xmp() == [b"Default", b"Second frame"]

# Test that encoderinfo is unchanged
assert im2.encoderinfo == {"xmp": b"Second frame"}

assert im_reloaded.info["xmp"] == b"First frame"

im_reloaded.seek(1)
assert im_reloaded.info["xmp"] == b"Second frame"
13 changes: 9 additions & 4 deletions Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,16 +681,21 @@ def test_rowsperstrip(self, tmp_path: Path) -> None:
assert im.tag_v2[278] == 256

im = hopper()
im.encoderinfo = {"tiffinfo": {278: 100}}
im2 = Image.new("L", (128, 128))
im2.encoderinfo = {"tiffinfo": {278: 256}}
im.save(outfile, save_all=True, append_images=[im2])
im3 = im2.copy()
im3.encoderinfo = {"tiffinfo": {278: 300}}
im.save(outfile, save_all=True, tiffinfo={278: 200}, append_images=[im2, im3])

with Image.open(outfile) as im:
assert isinstance(im, TiffImagePlugin.TiffImageFile)
assert im.tag_v2[278] == 128
assert im.tag_v2[278] == 100

im.seek(1)
assert im.tag_v2[278] == 256
assert im.tag_v2[278] == 200

im.seek(2)
assert im.tag_v2[278] == 300

def test_strip_raw(self) -> None:
infile = "Tests/images/tiff_strip_raw.tif"
Expand Down
8 changes: 7 additions & 1 deletion src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2556,8 +2556,9 @@ def save(
self.load()

save_all = params.pop("save_all", None)
self._default_encoderinfo = params
encoderinfo = getattr(self, "encoderinfo", {})
self.encoderinfo = {**encoderinfo, **params}
self._attach_default_encoderinfo(self)
self.encoderconfig: tuple[Any, ...] = ()

if format.upper() not in SAVE:
Expand Down Expand Up @@ -2599,6 +2600,11 @@ def save(
if open_fp:
fp.close()

def _attach_default_encoderinfo(self, im: Image) -> dict[str, Any]:
encoderinfo = getattr(self, "encoderinfo", {})
self.encoderinfo = {**im._default_encoderinfo, **encoderinfo}
return encoderinfo

def seek(self, frame: int) -> None:
"""
Seeks to the given frame in this sequence file. If you seek
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/MpoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
JpegImagePlugin._save(im_frame, fp, filename)
offsets.append(fp.tell())
else:
encoderinfo = im_frame._attach_default_encoderinfo(im)
im_frame.save(fp, "JPEG")
im_frame.encoderinfo = encoderinfo
offsets.append(fp.tell() - offsets[-1])

ifd = TiffImagePlugin.ImageFileDirectory_v2()
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2311,8 +2311,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
try:
with AppendingTiffWriter(fp) as tf:
for ims in [im] + append_images:
if not hasattr(ims, "encoderinfo"):
ims.encoderinfo = {}
encoderinfo = ims._attach_default_encoderinfo(im)
if not hasattr(ims, "encoderconfig"):
ims.encoderconfig = ()
nfr = getattr(ims, "n_frames", 1)
Expand All @@ -2322,6 +2321,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
ims.load()
_save(ims, tf, filename)
tf.newFrame()
ims.encoderinfo = encoderinfo
finally:
im.seek(cur_idx)

Expand Down
Loading