Repository: python-pillow/Pillow

Issue:
TypeError: exif.startswith(b"Exif\x00\x00"): arg must be str
https://github.com/python-pillow/Pillow/blob/893489c57227f76cff8816de167ad329e5862756/src/PIL/PngImagePlugin.py#L1306
This line hits a TypeError when trying to save a png file (while trying to save a thumbnail of a png image)

```python
if exif.startswith(b"Exif\x00\x00"):
```
```
TypeError: startswith first arg must be str or a tuple of str, not bytes
```

OS: Ubuntu18.04 x86_64
Python 3.6
PIL: 7.2.0



Discussion:
Could you upload the image that causes the problem?

I'm able to run the following code over one of our sample images without a problem.
```python
from PIL import Image
with Image.open("Tests/images/exif.png") as im:
    exif = im.getexif()
    im.thumbnail((20, 20))
    im.save("out.png", exif=exif)
```
I'll have to check with legal and actually find the image that caused it.
I'm using BytesIO() to read/write the image data in case that matters.
Yes, it could matter. Are you able to post a simple version of your code?
The code looks something like this, image_data comes over network.

```python
from PIL import Image
from io import BytesIO

resolutions=[(138, 108), (256, 256)]
with BytesIO(bytes(image_data)) as buf, Image.open(buf) as img:
    if img.mode != "RGB":
        img = img.convert("RGBA")
    for resolution in resolutions:
        thumbnail = img.copy()
        thumbnail.thumbnail(resolution)
        with BytesIO() as mem:
            thumbnail.save(mem, format="PNG", optimize=True)
            yield mem.getvalue(), resolution
```
Hmm. Reading a file as `image_data`, the code runs fine for me.

```python
from PIL import Image
from io import BytesIO

resolutions=[(138, 108), (256, 256)]
with open("Tests/images/hopper.png", "rb") as f:
    image_data = f.read()
with BytesIO(bytes(image_data)) as buf, Image.open(buf) as img:
    if img.mode != "RGB":
        img = img.convert("RGBA")
    for resolution in resolutions:
        thumbnail = img.copy()
        thumbnail.thumbnail(resolution)
        with BytesIO() as mem:
            thumbnail.save(mem, format="PNG", optimize=True)
```

Are you able to test if https://github.com/python-pillow/Pillow/raw/master/Tests/images/exif.png works in your program?
If it works, then there is something different about your image and posting it is the next step.
If it doesn't work, then there might be something missing from your code example.
Above exif.png works fine in my program without triggering the TypeError.
I'll let you know when I have a repro that I can share.
I've found the image that caused the error and compared its meta data to yours. It turns out the file that triggers the issue has a tEXt chunk: "exif":"Exif <some more meta data>". I modified your file accordingly and it triggers the issue as well.

[PIL_repro.zip](https://github.com/python-pillow/Pillow/files/5000472/PIL_repro.zip)


Resolving patch:
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -509,10 +509,11 @@ def chunk_tEXt(self, pos, length):
             v = b""
         if k:
             k = k.decode("latin-1", "strict")
-            v = v.decode("latin-1", "replace")
+            v_str = v.decode("latin-1", "replace")
 
-            self.im_info[k] = self.im_text[k] = v
-            self.check_text_memory(len(v))
+            self.im_info[k] = v if k == "exif" else v_str
+            self.im_text[k] = v_str
+            self.check_text_memory(len(v_str))
 
         return s
 


Test patch:
diff --git a/Tests/images/exif_text.png b/Tests/images/exif_text.png
new file mode 100644
Binary files /dev/null and b/Tests/images/exif_text.png differ
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -606,6 +606,11 @@ def test_exif(self):
             exif = im.copy().getexif()
             assert exif[274] == 1
 
+        # With a tEXt chunk
+        with Image.open("Tests/images/exif_text.png") as im:
+            exif = im._getexif()
+        assert exif[274] == 1
+
         # With XMP tags
         with Image.open("Tests/images/xmp_tags_orientation.png") as im:
             exif = im.getexif()