Repository: python-pillow/Pillow

Issue:
Support for a "blank" exif
<!--
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?
load a image and its exif is b'Exif\x00\x00'
```python
im=Image.open(/path/to/img)
im._exif is None
```
```
True
```
```python
im.info
```
```
{'jfif': 257,
 'jfif_version': (1, 1),
 'dpi': (1, 1),
 'jfif_unit': 1,
 'jfif_density': (1, 1),
 'exif': b'Exif\x00\x00'}
```
```python
im.getexif()
```
```
Traceback (most recent call last):

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3444, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "/tmp/ipykernel_15028/2749469980.py", line 1, in <module>
    im.getexif()

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/Image.py", line 1391, in getexif
    self._exif.load(exif_info)

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/Image.py", line 3422, in load
    self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/TiffImagePlugin.py", line 491, in __init__
    raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)")

  File "<string>", line unknown
SyntaxError: not a TIFF file (header b'' not valid)
```

### What did you expect to happen?
Nothing to be done. Since functions like ```ImageOps.exif_transpose``` calls ```im.getexif()```, so I will expect that no errors should be raised.
### What actually happened?
the ```getexif()``` function will call Exif() to initiate a blank Exif instance, since the im._exif is None. And then the Exif() instance will load the im.info.get('exif'), which is ```b'Exif\x00\x00'```. In the load function, the ```b'Exif\x00\x00'``` will be dropped by this [line](https://github.com/python-pillow/Pillow/blob/29960c6610ba542b9ec137f2f459a497c6365083/src/PIL/Image.py#L3486). So a `b''` is passed to `TiffImagePlugin.ImageFileDirectory_v2(self.head)`. And since `b''`  is not accepted by `TiffImagePlugin.ImageFileDirectory_v2`, an error will be raised. 

BTW, these three lines in Exif.load():
```python
if data == self._loaded_exif:
        return
self._loaded_exif = data
```
make the code work when calling im.getexif() the second time.
### What are your OS, Python and Pillow versions?

* OS: Linux
* Python: 3.9.7
* Pillow: 8.4.0 and 9.0.1

<!--
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
im=Image.open(/path/to/img)
im.getexif()
```
![testimage](https://user-images.githubusercontent.com/41351449/157874204-1f2e067b-46af-4d47-ab88-e57af6bb3849.jpeg)

I wonder if this repo is considering returning a blank Exif in this case, instead of raising an error. I have no idea whether this would lead to other unknown damages or not.


Discussion:

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -3478,12 +3478,12 @@ def load(self, data):
         self._loaded_exif = data
         self._data.clear()
         self._ifds.clear()
+        if data and data.startswith(b"Exif\x00\x00"):
+            data = data[6:]
         if not data:
             self._info = None
             return
 
-        if data.startswith(b"Exif\x00\x00"):
-            data = data[6:]
         self.fp = io.BytesIO(data)
         self.head = self.fp.read(8)
         # process dictionary


Test patch:
diff --git a/Tests/test_image.py b/Tests/test_image.py
--- a/Tests/test_image.py
+++ b/Tests/test_image.py
@@ -666,6 +666,19 @@ def act(fp):
 
             assert not fp.closed
 
+    def test_empty_exif(self):
+        with Image.open("Tests/images/exif.png") as im:
+            exif = im.getexif()
+        assert dict(exif) != {}
+
+        # Test that exif data is cleared after another load
+        exif.load(None)
+        assert dict(exif) == {}
+
+        # Test loading just the EXIF header
+        exif.load(b"Exif\x00\x00")
+        assert dict(exif) == {}
+
     @mark_if_feature_version(
         pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
     )