Repository: python-pillow/Pillow

Issue:
Saving TIFF as JPEG fails with OSError: encoder error -2
<!--
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?

Tried to convert TIFF to JPEG.

### What did you expect to happen?
Converted image saved successfully.

### What actually happened?
```pytb
Bogus marker length
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/gverm/Desktop/venv/lib/python3.10/site-packages/PIL/Image.py", line 2430, in save
    save_handler(self, fp, filename)
  File "/home/gverm/Desktop/venv/lib/python3.10/site-packages/PIL/JpegImagePlugin.py", line 803, in _save
    ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize)
  File "/home/gverm/Desktop/venv/lib/python3.10/site-packages/PIL/ImageFile.py", line 517, in _save
    _encode_tile(im, fp, tile, bufsize, fh)
  File "/home/gverm/Desktop/venv/lib/python3.10/site-packages/PIL/ImageFile.py", line 547, in _encode_tile
    raise OSError(msg) from exc
OSError: encoder error -2 when writing image file
```

### What are your OS, Python and Pillow versions?

* OS:  EndeavourOS
* Python: 3.10 & 3.11
* Pillow: 9.4.0 & current git

<!--
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, ImageOps, TiffImagePlugin
src_img = Image.open("/home/gverm/ae213421281f00a1cbe6fa2a9eb9cea5-A65_02_048$02.tif")
exif = src_img.getexif()
del exif[TiffImagePlugin.STRIPOFFSETS]
dst_img = ImageOps.exif_transpose(src_img).convert("RGB")
dst_img.save("test.jpg", quality=95, exif=exif)
```
[Link to image](https://api.collectie.gent/storage/v1/download/ae213421281f00a1cbe6fa2a9eb9cea5-A65_02_048$02.tif)


Discussion:
I find that if you add in
```python
del exif[TiffImagePlugin.ICCPROFILE]
```
the error disappears.
@radarhere Is this something that is to be expected or is there actually a bug here?
Without that workaround, the EXIF data you're trying to save is 417170 bytes long.

https://en.wikipedia.org/wiki/Exif
> Exif metadata are restricted in size to 64 kB in JPEG images because according to the specification this information must be contained within a single JPEG APP1 segment.

So it is a restriction of the JPEG format.
Thanks for the explanation. Would it be possible to catch this case and display an error stating that this limitation has been hit?

Resolving patch:
diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py
--- a/src/PIL/JpegImagePlugin.py
+++ b/src/PIL/JpegImagePlugin.py
@@ -730,10 +730,10 @@ def validate_qtables(qtables):
 
     extra = info.get("extra", b"")
 
+    MAX_BYTES_IN_MARKER = 65533
     icc_profile = info.get("icc_profile")
     if icc_profile:
         ICC_OVERHEAD_LEN = 14
-        MAX_BYTES_IN_MARKER = 65533
         MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN
         markers = []
         while icc_profile:
@@ -764,6 +764,9 @@ def validate_qtables(qtables):
     exif = info.get("exif", b"")
     if isinstance(exif, Image.Exif):
         exif = exif.tobytes()
+    if len(exif) > MAX_BYTES_IN_MARKER:
+        msg = "EXIF data is too long"
+        raise ValueError(msg)
 
     # get keyword arguments
     im.encoderconfig = (


Test patch:
diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py
--- a/Tests/test_file_jpeg.py
+++ b/Tests/test_file_jpeg.py
@@ -270,7 +270,10 @@ def test_large_exif(self, tmp_path):
         # https://github.com/python-pillow/Pillow/issues/148
         f = str(tmp_path / "temp.jpg")
         im = hopper()
-        im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
+        im.save(f, "JPEG", quality=90, exif=b"1" * 65533)
+
+        with pytest.raises(ValueError):
+            im.save(f, "JPEG", quality=90, exif=b"1" * 65534)
 
     def test_exif_typeerror(self):
         with Image.open("Tests/images/exif_typeerror.jpg") as im: