Repository: python-pillow/Pillow

Problem statement:
Loss of Exif IFD attributes when saving new JPEG image
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?
create a new jpeg image with IFD0 and ExifIFD tags
### What did you expect to happen?
retreive all tags
### What actually happened?
i retreive only the IFD0 tags
### What are your OS, Python and Pillow versions?

* OS: windows 10
* Python: 3.12.1
* Pillow: 11.0.0

```text

--------------------------------------------------------------------
Pillow 11.0.0
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]
--------------------------------------------------------------------
Python executable is C:\Python\python.exe
System Python files loaded from C:\Python
--------------------------------------------------------------------
Python Pillow modules loaded from C:\Python\Lib\site-packages\PIL
Binary Pillow modules loaded from C:\Python\Lib\site-packages\PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 11.0.0
--- TKINTER support ok, loaded 8.6
--- FREETYPE2 support ok, loaded 2.13.3
--- LITTLECMS2 support ok, loaded 2.16
--- WEBP support ok, loaded 1.4.0
--- JPEG support ok, compiled for libjpeg-turbo 3.0.4
--- OPENJPEG (JPEG2000) support ok, loaded 2.5.2
--- ZLIB (PNG/ZIP) support ok, loaded 1.3.1
--- LIBTIFF support ok, loaded 4.6.0
*** RAQM (Bidirectional Text) support not installed
*** LIBIMAGEQUANT (Quantization method) support not installed
*** XCB (X protocol) support not installed
--------------------------------------------------------------------
```

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
from PIL import Image, ExifTags


if __name__ == '__main__':

    newim = Image.new('RGB', [400,500])
    exif = newim.getexif()
# IFD0    
    exif[0x010E] = 'my description' # ImageDescription
    exif[0x013B] = "the artist" # Artist
    exif[0x0132] = "2020:02:02 00:00:00" # DateTime 
# ExifIFD  
    exif.get_ifd(ExifTags.IFD.Exif)[0x9003] = "2020:02:02 00:00:01" # DateTimeOriginal
    exif.get_ifd(ExifTags.IFD.Exif)[0x9004] = "2020:02:02 00:00:02" # DateTimeDigitized 

    newim.save('newimage.jpeg', exif=exif)
    newim.close()

    im = Image.open('newimage.jpeg')
    exf = im.getexif()
    print('IFD0', exf) # IFD0 {306: '2020:02:02 00:00:00', 315: 'the artist', 270: 'my description'}
    print('ExifIFD', exf.get_ifd(ExifTags.IFD.Exif)) # ExifIFD {} 
```



Hints:
I've created #8635 to fix this.

Patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -4023,6 +4023,9 @@ def tobytes(self, offset: int = 8) -> bytes:
 
         head = self._get_head()
         ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
+        for tag, ifd_dict in self._ifds.items():
+            if tag not in self:
+                ifd[tag] = ifd_dict
         for tag, value in self.items():
             if tag in [
                 ExifTags.IFD.Exif,


Test patch:
diff --git a/Tests/test_image.py b/Tests/test_image.py
--- a/Tests/test_image.py
+++ b/Tests/test_image.py
@@ -793,6 +793,10 @@ def test_empty_get_ifd(self) -> None:
         ifd[36864] = b"0220"
         assert exif.get_ifd(0x8769) == {36864: b"0220"}
 
+        reloaded_exif = Image.Exif()
+        reloaded_exif.load(exif.tobytes())
+        assert reloaded_exif.get_ifd(0x8769) == {36864: b"0220"}
+
     @mark_if_feature_version(
         pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
     )