mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-03-23 11:34:14 +03:00
Merge branch 'main' into jpeg2000_cmyk_save
This commit is contained in:
commit
973cd6481a
|
@ -436,8 +436,9 @@ def test_pclr() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_comment() -> None:
|
def test_comment() -> None:
|
||||||
with Image.open("Tests/images/comment.jp2") as im:
|
for path in ("Tests/images/9bit.j2k", "Tests/images/comment.jp2"):
|
||||||
assert im.info["comment"] == b"Created by OpenJPEG version 2.5.0"
|
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
|
# Test an image that is truncated partway through a codestream
|
||||||
with open("Tests/images/comment.jp2", "rb") as fp:
|
with open("Tests/images/comment.jp2", "rb") as fp:
|
||||||
|
|
|
@ -772,22 +772,18 @@ class TestFilePng:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
|
||||||
@pytest.mark.parametrize("buffer", (True, False))
|
@pytest.mark.parametrize("buffer", (True, False))
|
||||||
def test_save_stdout(self, buffer: bool) -> None:
|
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
old_stdout = sys.stdout
|
|
||||||
|
|
||||||
class MyStdOut:
|
class MyStdOut:
|
||||||
buffer = BytesIO()
|
buffer = BytesIO()
|
||||||
|
|
||||||
mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else 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:
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
im.save(sys.stdout, "PNG")
|
im.save(sys.stdout, "PNG")
|
||||||
|
|
||||||
# Reset stdout
|
|
||||||
sys.stdout = old_stdout
|
|
||||||
|
|
||||||
if isinstance(mystdout, MyStdOut):
|
if isinstance(mystdout, MyStdOut):
|
||||||
mystdout = mystdout.buffer
|
mystdout = mystdout.buffer
|
||||||
with Image.open(mystdout) as reloaded:
|
with Image.open(mystdout) as reloaded:
|
||||||
|
|
|
@ -367,22 +367,18 @@ def test_mimetypes(tmp_path: Path) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("buffer", (True, False))
|
@pytest.mark.parametrize("buffer", (True, False))
|
||||||
def test_save_stdout(buffer: bool) -> None:
|
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
old_stdout = sys.stdout
|
|
||||||
|
|
||||||
class MyStdOut:
|
class MyStdOut:
|
||||||
buffer = BytesIO()
|
buffer = BytesIO()
|
||||||
|
|
||||||
mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
|
mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
|
||||||
|
|
||||||
sys.stdout = mystdout
|
monkeypatch.setattr(sys, "stdout", mystdout)
|
||||||
|
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.save(sys.stdout, "PPM")
|
im.save(sys.stdout, "PPM")
|
||||||
|
|
||||||
# Reset stdout
|
|
||||||
sys.stdout = old_stdout
|
|
||||||
|
|
||||||
if isinstance(mystdout, MyStdOut):
|
if isinstance(mystdout, MyStdOut):
|
||||||
mystdout = mystdout.buffer
|
mystdout = mystdout.buffer
|
||||||
with Image.open(mystdout) as reloaded:
|
with Image.open(mystdout) as reloaded:
|
||||||
|
|
|
@ -52,6 +52,12 @@ zlib library, and what version of zlib-ng is being used::
|
||||||
Other Changes
|
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
|
Saving JPEG 2000 CMYK images
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -104,28 +104,17 @@ def grab(
|
||||||
|
|
||||||
def grabclipboard() -> Image.Image | list[str] | None:
|
def grabclipboard() -> Image.Image | list[str] | None:
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
fh, filepath = tempfile.mkstemp(".png")
|
p = subprocess.run(
|
||||||
os.close(fh)
|
["osascript", "-e", "get the clipboard as «class PNGf»"],
|
||||||
commands = [
|
capture_output=True,
|
||||||
'set theFile to (open for access POSIX file "'
|
)
|
||||||
+ filepath
|
if p.returncode != 0:
|
||||||
+ '" with write permission)',
|
return None
|
||||||
"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)
|
|
||||||
|
|
||||||
im = None
|
import binascii
|
||||||
if os.stat(filepath).st_size != 0:
|
|
||||||
im = Image.open(filepath)
|
data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
|
||||||
im.load()
|
return Image.open(data)
|
||||||
os.unlink(filepath)
|
|
||||||
return im
|
|
||||||
elif sys.platform == "win32":
|
elif sys.platform == "win32":
|
||||||
fmt, data = Image.core.grabclipboard_win32()
|
fmt, data = Image.core.grabclipboard_win32()
|
||||||
if fmt == "file": # CF_HDROP
|
if fmt == "file": # CF_HDROP
|
||||||
|
|
|
@ -252,6 +252,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
if sig == b"\xff\x4f\xff\x51":
|
if sig == b"\xff\x4f\xff\x51":
|
||||||
self.codec = "j2k"
|
self.codec = "j2k"
|
||||||
self._size, self._mode = _parse_codestream(self.fp)
|
self._size, self._mode = _parse_codestream(self.fp)
|
||||||
|
self._parse_comment()
|
||||||
else:
|
else:
|
||||||
sig = sig + self.fp.read(8)
|
sig = sig + self.fp.read(8)
|
||||||
|
|
||||||
|
@ -262,6 +263,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
if dpi is not None:
|
if dpi is not None:
|
||||||
self.info["dpi"] = dpi
|
self.info["dpi"] = dpi
|
||||||
if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"):
|
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()
|
self._parse_comment()
|
||||||
else:
|
else:
|
||||||
msg = "not a JPEG 2000 file"
|
msg = "not a JPEG 2000 file"
|
||||||
|
@ -296,10 +300,6 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
]
|
]
|
||||||
|
|
||||||
def _parse_comment(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:
|
while True:
|
||||||
marker = self.fp.read(2)
|
marker = self.fp.read(2)
|
||||||
if not marker:
|
if not marker:
|
||||||
|
|
|
@ -640,7 +640,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
|
||||||
opj_dparameters_t params;
|
opj_dparameters_t params;
|
||||||
OPJ_COLOR_SPACE color_space;
|
OPJ_COLOR_SPACE color_space;
|
||||||
j2k_unpacker_t unpack = NULL;
|
j2k_unpacker_t unpack = NULL;
|
||||||
size_t buffer_size = 0, tile_bytes = 0;
|
size_t tile_bytes = 0;
|
||||||
unsigned n, tile_height, tile_width;
|
unsigned n, tile_height, tile_width;
|
||||||
int subsampling;
|
int subsampling;
|
||||||
int total_component_width = 0;
|
int total_component_width = 0;
|
||||||
|
@ -870,7 +870,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
|
||||||
tile_info.data_size = tile_bytes;
|
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 */
|
/* malloc check ok, overflow and tile size sanity check above */
|
||||||
UINT8 *new = realloc(state->buffer, tile_info.data_size);
|
UINT8 *new = realloc(state->buffer, tile_info.data_size);
|
||||||
if (!new) {
|
if (!new) {
|
||||||
|
@ -883,7 +883,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
|
||||||
to valgrind errors. */
|
to valgrind errors. */
|
||||||
memset(new, 0, tile_info.data_size);
|
memset(new, 0, tile_info.data_size);
|
||||||
state->buffer = new;
|
state->buffer = new;
|
||||||
buffer_size = tile_info.data_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opj_decode_tile_data(
|
if (!opj_decode_tile_data(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user