mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 18:06:18 +03:00
Added reading and writing of private PNG chunks
This commit is contained in:
parent
e2437c9b48
commit
f7144c1216
|
@ -564,6 +564,28 @@ class TestFilePng:
|
||||||
chunks = PngImagePlugin.getchunks(im)
|
chunks = PngImagePlugin.getchunks(im)
|
||||||
assert len(chunks) == 3
|
assert len(chunks) == 3
|
||||||
|
|
||||||
|
def test_read_private_chunks(self):
|
||||||
|
im = Image.open("Tests/images/exif.png")
|
||||||
|
assert im.private_chunks == [(b"orNT", b"\x01")]
|
||||||
|
|
||||||
|
def test_roundtrip_private_chunk(self):
|
||||||
|
# Check private chunk roundtripping
|
||||||
|
|
||||||
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
info = PngImagePlugin.PngInfo()
|
||||||
|
info.add(b"prIV", b"VALUE")
|
||||||
|
info.add(b"atEC", b"VALUE2")
|
||||||
|
info.add(b"prIV", b"VALUE3", True)
|
||||||
|
|
||||||
|
im = roundtrip(im, pnginfo=info)
|
||||||
|
assert im.private_chunks == [(b"prIV", b"VALUE"), (b"atEC", b"VALUE2")]
|
||||||
|
im.load()
|
||||||
|
assert im.private_chunks == [
|
||||||
|
(b"prIV", b"VALUE"),
|
||||||
|
(b"atEC", b"VALUE2"),
|
||||||
|
(b"prIV", b"VALUE3", True),
|
||||||
|
]
|
||||||
|
|
||||||
def test_textual_chunks_after_idat(self):
|
def test_textual_chunks_after_idat(self):
|
||||||
with Image.open("Tests/images/hopper.png") as im:
|
with Image.open("Tests/images/hopper.png") as im:
|
||||||
assert "comment" in im.text.keys()
|
assert "comment" in im.text.keys()
|
||||||
|
|
|
@ -228,15 +228,20 @@ class PngInfo:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.chunks = []
|
self.chunks = []
|
||||||
|
|
||||||
def add(self, cid, data):
|
def add(self, cid, data, after_idat=False):
|
||||||
"""Appends an arbitrary chunk. Use with caution.
|
"""Appends an arbitrary chunk. Use with caution.
|
||||||
|
|
||||||
:param cid: a byte string, 4 bytes long.
|
:param cid: a byte string, 4 bytes long.
|
||||||
:param data: a byte string of the encoded data
|
:param data: a byte string of the encoded data
|
||||||
|
:param after_idat: for use with private chunks. Whether the chunk
|
||||||
|
should be written after IDAT
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.chunks.append((cid, data))
|
chunk = [cid, data]
|
||||||
|
if after_idat:
|
||||||
|
chunk.append(True)
|
||||||
|
self.chunks.append(tuple(chunk))
|
||||||
|
|
||||||
def add_itxt(self, key, value, lang="", tkey="", zip=False):
|
def add_itxt(self, key, value, lang="", tkey="", zip=False):
|
||||||
"""Appends an iTXt chunk.
|
"""Appends an iTXt chunk.
|
||||||
|
@ -641,6 +646,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
#
|
#
|
||||||
# Parse headers up to the first IDAT or fDAT chunk
|
# Parse headers up to the first IDAT or fDAT chunk
|
||||||
|
|
||||||
|
self.private_chunks = []
|
||||||
self.png = PngStream(self.fp)
|
self.png = PngStream(self.fp)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -657,6 +663,8 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.debug("%r %s %s (unknown)", cid, pos, length)
|
logger.debug("%r %s %s (unknown)", cid, pos, length)
|
||||||
s = ImageFile._safe_read(self.fp, length)
|
s = ImageFile._safe_read(self.fp, length)
|
||||||
|
if cid[1:2].islower():
|
||||||
|
self.private_chunks.append((cid, s))
|
||||||
|
|
||||||
self.png.crc(cid, s)
|
self.png.crc(cid, s)
|
||||||
|
|
||||||
|
@ -888,7 +896,9 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
ImageFile._safe_read(self.fp, length)
|
ImageFile._safe_read(self.fp, length)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.debug("%r %s %s (unknown)", cid, pos, length)
|
logger.debug("%r %s %s (unknown)", cid, pos, length)
|
||||||
ImageFile._safe_read(self.fp, length)
|
s = ImageFile._safe_read(self.fp, length)
|
||||||
|
if cid[1:2].islower():
|
||||||
|
self.private_chunks.append((cid, s, True))
|
||||||
self._text = self.png.im_text
|
self._text = self.png.im_text
|
||||||
if not self.is_animated:
|
if not self.is_animated:
|
||||||
self.png.close()
|
self.png.close()
|
||||||
|
@ -1217,12 +1227,18 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
||||||
info = im.encoderinfo.get("pnginfo")
|
info = im.encoderinfo.get("pnginfo")
|
||||||
if info:
|
if info:
|
||||||
chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"]
|
chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"]
|
||||||
for cid, data in info.chunks:
|
for info_chunk in info.chunks:
|
||||||
|
cid, data = info_chunk[:2]
|
||||||
if cid in chunks:
|
if cid in chunks:
|
||||||
chunks.remove(cid)
|
chunks.remove(cid)
|
||||||
chunk(fp, cid, data)
|
chunk(fp, cid, data)
|
||||||
elif cid in chunks_multiple_allowed:
|
elif cid in chunks_multiple_allowed:
|
||||||
chunk(fp, cid, data)
|
chunk(fp, cid, data)
|
||||||
|
elif cid[1:2].islower():
|
||||||
|
# Private chunk
|
||||||
|
after_idat = info_chunk[2:3]
|
||||||
|
if not after_idat:
|
||||||
|
chunk(fp, cid, data)
|
||||||
|
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
palette_byte_number = (2 ** bits) * 3
|
palette_byte_number = (2 ** bits) * 3
|
||||||
|
@ -1272,7 +1288,8 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
||||||
|
|
||||||
if info:
|
if info:
|
||||||
chunks = [b"bKGD", b"hIST"]
|
chunks = [b"bKGD", b"hIST"]
|
||||||
for cid, data in info.chunks:
|
for info_chunk in info.chunks:
|
||||||
|
cid, data = info_chunk[:2]
|
||||||
if cid in chunks:
|
if cid in chunks:
|
||||||
chunks.remove(cid)
|
chunks.remove(cid)
|
||||||
chunk(fp, cid, data)
|
chunk(fp, cid, data)
|
||||||
|
@ -1290,6 +1307,15 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
||||||
else:
|
else:
|
||||||
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
|
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
|
||||||
|
|
||||||
|
if info:
|
||||||
|
for info_chunk in info.chunks:
|
||||||
|
cid, data = info_chunk[:2]
|
||||||
|
if cid[1:2].islower():
|
||||||
|
# Private chunk
|
||||||
|
after_idat = info_chunk[2:3]
|
||||||
|
if after_idat:
|
||||||
|
chunk(fp, cid, data)
|
||||||
|
|
||||||
chunk(fp, b"IEND", b"")
|
chunk(fp, b"IEND", b"")
|
||||||
|
|
||||||
if hasattr(fp, "flush"):
|
if hasattr(fp, "flush"):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user