Skip to content

Commit

Permalink
Merge branch 'main' into jpeg2000_cmyk_save
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Dec 28, 2024
2 parents 0220b02 + 41a89ea commit 973cd64
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 42 deletions.
5 changes: 3 additions & 2 deletions Tests/test_file_jpeg2k.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,9 @@ def test_pclr() -> None:


def test_comment() -> None:
with Image.open("Tests/images/comment.jp2") as im:
assert im.info["comment"] == b"Created by OpenJPEG version 2.5.0"
for path in ("Tests/images/9bit.j2k", "Tests/images/comment.jp2"):
with Image.open(path) as im:
assert im.info["comment"] == b"Created by OpenJPEG version 2.5.0"

# Test an image that is truncated partway through a codestream
with open("Tests/images/comment.jp2", "rb") as fp:
Expand Down
8 changes: 2 additions & 6 deletions Tests/test_file_png.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,22 +772,18 @@ def test_seek(self) -> None:
im.seek(1)

@pytest.mark.parametrize("buffer", (True, False))
def test_save_stdout(self, buffer: bool) -> None:
old_stdout = sys.stdout
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:

class MyStdOut:
buffer = BytesIO()

mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()

sys.stdout = mystdout
monkeypatch.setattr(sys, "stdout", mystdout)

with Image.open(TEST_PNG_FILE) as im:
im.save(sys.stdout, "PNG")

# Reset stdout
sys.stdout = old_stdout

if isinstance(mystdout, MyStdOut):
mystdout = mystdout.buffer
with Image.open(mystdout) as reloaded:
Expand Down
8 changes: 2 additions & 6 deletions Tests/test_file_ppm.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,22 +367,18 @@ def test_mimetypes(tmp_path: Path) -> None:


@pytest.mark.parametrize("buffer", (True, False))
def test_save_stdout(buffer: bool) -> None:
old_stdout = sys.stdout
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:

class MyStdOut:
buffer = BytesIO()

mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()

sys.stdout = mystdout
monkeypatch.setattr(sys, "stdout", mystdout)

with Image.open(TEST_FILE) as im:
im.save(sys.stdout, "PPM")

# Reset stdout
sys.stdout = old_stdout

if isinstance(mystdout, MyStdOut):
mystdout = mystdout.buffer
with Image.open(mystdout) as reloaded:
Expand Down
6 changes: 6 additions & 0 deletions docs/releasenotes/11.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ zlib library, and what version of zlib-ng is being used::
Other Changes
=============

Reading JPEG 2000 comments
^^^^^^^^^^^^^^^^^^^^^^^^^^

When opening a JPEG 2000 image, the comment may now be read into
:py:attr:`~PIL.Image.Image.info` for J2K images, not just JP2 images.

Saving JPEG 2000 CMYK images
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
31 changes: 10 additions & 21 deletions src/PIL/ImageGrab.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,28 +104,17 @@ def grab(

def grabclipboard() -> Image.Image | list[str] | None:
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp(".png")
os.close(fh)
commands = [
'set theFile to (open for access POSIX file "'
+ filepath
+ '" with write permission)',
"try",
" write (the clipboard as «class PNGf») to theFile",
"end try",
"close access theFile",
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
p = subprocess.run(
["osascript", "-e", "get the clipboard as «class PNGf»"],
capture_output=True,
)
if p.returncode != 0:
return None

im = None
if os.stat(filepath).st_size != 0:
im = Image.open(filepath)
im.load()
os.unlink(filepath)
return im
import binascii

data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
return Image.open(data)
elif sys.platform == "win32":
fmt, data = Image.core.grabclipboard_win32()
if fmt == "file": # CF_HDROP
Expand Down
8 changes: 4 additions & 4 deletions src/PIL/Jpeg2KImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def _open(self) -> None:
if sig == b"\xff\x4f\xff\x51":
self.codec = "j2k"
self._size, self._mode = _parse_codestream(self.fp)
self._parse_comment()
else:
sig = sig + self.fp.read(8)

Expand All @@ -262,6 +263,9 @@ def _open(self) -> None:
if dpi is not None:
self.info["dpi"] = dpi
if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"):
hdr = self.fp.read(2)
length = _binary.i16be(hdr)
self.fp.seek(length - 2, os.SEEK_CUR)
self._parse_comment()
else:
msg = "not a JPEG 2000 file"
Expand Down Expand Up @@ -296,10 +300,6 @@ def _open(self) -> None:
]

def _parse_comment(self) -> None:
hdr = self.fp.read(2)
length = _binary.i16be(hdr)
self.fp.seek(length - 2, os.SEEK_CUR)

while True:
marker = self.fp.read(2)
if not marker:
Expand Down
5 changes: 2 additions & 3 deletions src/libImaging/Jpeg2KDecode.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
opj_dparameters_t params;
OPJ_COLOR_SPACE color_space;
j2k_unpacker_t unpack = NULL;
size_t buffer_size = 0, tile_bytes = 0;
size_t tile_bytes = 0;
unsigned n, tile_height, tile_width;
int subsampling;
int total_component_width = 0;
Expand Down Expand Up @@ -870,7 +870,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
tile_info.data_size = tile_bytes;
}

if (buffer_size < tile_info.data_size) {
if (tile_info.data_size > 0) {
/* malloc check ok, overflow and tile size sanity check above */
UINT8 *new = realloc(state->buffer, tile_info.data_size);
if (!new) {
Expand All @@ -883,7 +883,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
to valgrind errors. */
memset(new, 0, tile_info.data_size);
state->buffer = new;
buffer_size = tile_info.data_size;
}

if (!opj_decode_tile_data(
Expand Down

0 comments on commit 973cd64

Please sign in to comment.