diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index df589e24a..8feae2edd 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -991,6 +991,13 @@ class TestFileJpeg: else: assert im.getxmp() == {"xmpmeta": None} + def test_save_xmp(self, tmp_path: Path) -> None: + f = str(tmp_path / "temp.jpg") + hopper().save(f, xmp=b"XMP test") + + with Image.open(f) as reloaded: + assert reloaded.info["xmp"] == b"XMP test" + @pytest.mark.timeout(timeout=1) def test_eof(self) -> None: # Even though this decoder never says that it is finished diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index fc897e2b9..f15b62f0f 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -746,6 +746,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: extra = info.get("extra", b"") + xmp = info.get("xmp") + if xmp: + size = o16(31 + len(xmp)) + extra += b"\xFF\xE1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp + MAX_BYTES_IN_MARKER = 65533 icc_profile = info.get("icc_profile") if icc_profile: