From d3923f71420c58f8545fc74fc5f41f0b415635f8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Mar 2023 17:53:35 +1100 Subject: [PATCH 1/2] Use reading of comments to test saving comments --- Tests/test_file_jpeg2k.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 8261c612ab5..a869d74f0ce 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -372,6 +372,20 @@ def test_comment(): pass +def test_save_comment(): + comment = "Created by Pillow" + out = BytesIO() + test_card.save(out, "JPEG2000", comment=comment) + out.seek(0) + + with Image.open(out) as im: + assert im.info["comment"] == b"Created by Pillow" + + too_long_comment = " " * 65532 + with pytest.raises(ValueError): + test_card.save(out, "JPEG2000", comment=too_long_comment) + + @pytest.mark.parametrize( "test_file", [ @@ -391,20 +405,6 @@ def test_crashes(test_file): pass -def test_custom_comment(): - output_stream = BytesIO() - unique_comment = "This is a unique comment, which should be found below" - test_card.save(output_stream, "JPEG2000", comment=unique_comment) - output_stream.seek(0) - data = output_stream.read() - # Lazy method to determine if the comment is in the image generated - assert bytes(unique_comment, "utf-8") in data - - too_long_comment = " " * 65532 - with pytest.raises(ValueError): - test_card.save(output_stream, "JPEG2000", comment=too_long_comment) - - @skip_unless_feature_version("jpg_2000", "2.4.0") def test_plt_marker(): # Search the start of the codesteam for the PLT box (id 0xFF58) From 7c3fd254330ebcbd51fa08ef0b709b52587f5b92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Mar 2023 09:45:51 +1100 Subject: [PATCH 2/2] Allow saving bytes as comments --- Tests/test_file_jpeg2k.py | 14 +++++++------- src/PIL/Jpeg2KImagePlugin.py | 4 +++- src/encode.c | 21 +++++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index a869d74f0ce..60be50e0747 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -373,15 +373,15 @@ def test_comment(): def test_save_comment(): - comment = "Created by Pillow" - out = BytesIO() - test_card.save(out, "JPEG2000", comment=comment) - out.seek(0) + for comment in ("Created by Pillow", b"Created by Pillow"): + out = BytesIO() + test_card.save(out, "JPEG2000", comment=comment) + out.seek(0) - with Image.open(out) as im: - assert im.info["comment"] == b"Created by Pillow" + with Image.open(out) as im: + assert im.info["comment"] == b"Created by Pillow" - too_long_comment = " " * 65532 + too_long_comment = " " * 65531 with pytest.raises(ValueError): test_card.save(out, "JPEG2000", comment=too_long_comment) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 4249fe7141c..980c299dbce 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -351,10 +351,12 @@ def _save(im, fp, filename): cinema_mode = info.get("cinema_mode", "no") mct = info.get("mct", 0) signed = info.get("signed", False) - fd = -1 comment = info.get("comment") + if isinstance(comment, str): + comment = comment.encode() add_plt = info.get("add_plt", False) + fd = -1 if hasattr(fp, "fileno"): try: fd = fp.fileno() diff --git a/src/encode.c b/src/encode.c index e8946dbaef1..7dcb7976632 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1215,11 +1215,12 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { int sgnd = 0; Py_ssize_t fd = -1; char *comment = NULL; + Py_ssize_t comment_size; int add_plt = 0; if (!PyArg_ParseTuple( args, - "ss|OOOsOnOOOssbbnzp", + "ss|OOOsOnOOOssbbnz#p", &mode, &format, &offset, @@ -1237,6 +1238,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { &sgnd, &fd, &comment, + &comment_size, &add_plt)) { return NULL; } @@ -1319,9 +1321,9 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { } } - if (comment != NULL && strlen(comment) > 0) { + if (comment && comment_size > 0) { /* Size is stored as as an uint16, subtract 4 bytes for the header */ - if (strlen(comment) >= 65531) { + if (comment_size >= 65531) { PyErr_SetString( PyExc_ValueError, "JPEG 2000 comment is too long"); @@ -1329,15 +1331,14 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { return NULL; } - context->comment = strdup(comment); - - if (context->comment == NULL) { - PyErr_SetString( - PyExc_MemoryError, - "Couldn't allocate memory for JPEG 2000 comment"); + char *p = malloc(comment_size + 1); + if (!p) { Py_DECREF(encoder); - return NULL; + return ImagingError_MemoryError(); } + memcpy(p, comment, comment_size); + p[comment_size] = '\0'; + context->comment = p; } if (quality_layers && PySequence_Check(quality_layers)) {