Repository: python-pillow/Pillow

Issue:
Saving as GIF throws exception when source image has "comment" metadata
### What did you do?
Attempted to open a png image file, and save it as a gif file.

### What did you expect to happen?
The file should have been successfully saved/converted

### What actually happened?
This exception was thrown:
```
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    image.save("out.gif", "gif")
  File "/usr/local/lib/python3.6/site-packages/PIL/Image.py", line 2084, in save
    save_handler(self, fp, filename)
  File "/usr/local/lib/python3.6/site-packages/PIL/GifImagePlugin.py", line 513, in _save
    _write_single_frame(im, fp, palette)
  File "/usr/local/lib/python3.6/site-packages/PIL/GifImagePlugin.py", line 415, in _write_single_frame
    _write_local_header(fp, im, (0, 0), flags)
  File "/usr/local/lib/python3.6/site-packages/PIL/GifImagePlugin.py", line 577, in _write_local_header
    fp.write(o8(len(subblock)) + subblock)
TypeError: can't concat str to bytes
```

### What are your OS, Python and Pillow versions?
* OS: Ubuntu 14.04.5
* Python: 3.6.6
* Pillow: 6.2.1

## Code to Reproduce
```python
from PIL import Image
image = Image.open("example.png")
image.save("out.gif", "gif")
```
Have this image in the directory while running the code:
![example.png](https://user-images.githubusercontent.com/3231343/71637322-bc562c00-2bf4-11ea-8198-7712d87eda23.png)

## My diagnosis
It appears that it is erroring because when it tries to write the "comment" field from the encoderinfo of the image, the "comment" field is a string instead of bytes. I'm guessing either that should be checked here, or it should be read in differently when `Image.open` is called. I checked this by checking the metadata of the image via `identify -verbose example.png`. When I stripped the metadata from the image using `mogrify -strip example.png`, the issue went away and the conversion was successful.

Heres the stripped version of the image that has no issues when converted using the code above:
![example_stripped.png](https://user-images.githubusercontent.com/3231343/71637362-42bf3d80-2bf6-11ea-8931-45482fc05116.png)

I'd create a pull request for this myself, except I'm not sure if the fix needs to be on reading it in, or attempting to write it when writing the gif.



Discussion:

Resolving patch:
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -569,8 +569,11 @@ def _write_local_header(fp, im, offset, flags):
 
     if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]):
         fp.write(b"!" + o8(254))  # extension intro
-        for i in range(0, len(im.encoderinfo["comment"]), 255):
-            subblock = im.encoderinfo["comment"][i : i + 255]
+        comment = im.encoderinfo["comment"]
+        if isinstance(comment, str):
+            comment = comment.encode()
+        for i in range(0, len(comment), 255):
+            subblock = comment[i : i + 255]
             fp.write(o8(len(subblock)) + subblock)
         fp.write(o8(0))
     if "loop" in im.encoderinfo:


Test patch:
diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py
--- a/Tests/test_file_gif.py
+++ b/Tests/test_file_gif.py
@@ -565,14 +565,18 @@ def test_comment(self):
                 im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0"
             )
 
-            out = self.tempfile("temp.gif")
-            im = Image.new("L", (100, 100), "#000")
-            im.info["comment"] = b"Test comment text"
-            im.save(out)
+        out = self.tempfile("temp.gif")
+        im = Image.new("L", (100, 100), "#000")
+        im.info["comment"] = b"Test comment text"
+        im.save(out)
         with Image.open(out) as reread:
-
             self.assertEqual(reread.info["comment"], im.info["comment"])
 
+        im.info["comment"] = "Test comment text"
+        im.save(out)
+        with Image.open(out) as reread:
+            self.assertEqual(reread.info["comment"], im.info["comment"].encode())
+
     def test_comment_over_255(self):
         out = self.tempfile("temp.gif")
         im = Image.new("L", (100, 100), "#000")