diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c1ccf3fe2..e98e55aca 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -660,6 +660,18 @@ class TestFileTiff: assert isinstance(im, TiffImagePlugin.TiffImageFile) assert im.tag_v2[278] == 256 + im = hopper() + im2 = Image.new("L", (128, 128)) + im2.encoderinfo = {"tiffinfo": {278: 256}} + im.save(outfile, save_all=True, append_images=[im2]) + + with Image.open(outfile) as im: + assert isinstance(im, TiffImagePlugin.TiffImageFile) + assert im.tag_v2[278] == 128 + + im.seek(1) + assert im.tag_v2[278] == 256 + def test_strip_raw(self) -> None: infile = "Tests/images/tiff_strip_raw.tif" with Image.open(infile) as im: diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 991cadaa2..a1d93821f 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1163,9 +1163,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum **append_images** A list of images to append as additional frames. Each of the - images in the list can be single or multiframe images. Note however, that for - correct results, all the appended images should have the same - ``encoderinfo`` and ``encoderconfig`` properties. + images in the list can be single or multiframe images. .. versionadded:: 4.2.0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 684c87c4d..399a38bc2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2475,7 +2475,21 @@ class Image: format to use is determined from the filename extension. If a file object was used instead of a filename, this parameter should always be used. - :param params: Extra parameters to the image writer. + :param params: Extra parameters to the image writer. These can also be + set on the image itself through ``encoderinfo``. This is useful when + saving multiple images:: + + # Saving XMP data to a single image + from PIL import Image + red = Image.new("RGB", (1, 1), "#f00") + red.save("out.mpo", xmp=b"test") + + # Saving XMP data to the second frame of an image + from PIL import Image + black = Image.new("RGB", (1, 1)) + red = Image.new("RGB", (1, 1), "#f00") + red.encoderinfo = {"xmp": b"test"} + black.save("out.mpo", save_all=True, append_images=[red]) :returns: None :exception ValueError: If the output format could not be determined from the file name. Use the format option to solve this. diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index b8ff47a12..2b471abac 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -2295,9 +2295,7 @@ class AppendingTiffWriter(io.BytesIO): def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - encoderinfo = im.encoderinfo.copy() - encoderconfig = im.encoderconfig - append_images = list(encoderinfo.get("append_images", [])) + append_images = list(im.encoderinfo.get("append_images", [])) if not hasattr(im, "n_frames") and not append_images: return _save(im, fp, filename) @@ -2305,12 +2303,11 @@ 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: - ims.encoderinfo = encoderinfo - ims.encoderconfig = encoderconfig - if not hasattr(ims, "n_frames"): - nfr = 1 - else: - nfr = ims.n_frames + if not hasattr(ims, "encoderinfo"): + ims.encoderinfo = {} + if not hasattr(ims, "encoderconfig"): + ims.encoderconfig = () + nfr = getattr(ims, "n_frames", 1) for idx in range(nfr): ims.seek(idx)