[Image-SIG] PNG enhancements
Fred L. Drake
Fred L. Drake, Jr." <fdrake@acm.org
Mon, 20 Jul 1998 10:44:33 -0400 (EDT)
--5ydeQFjoGe
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
I've attached a patch to PngImagePlugin.py which adds support for
the zTXt, bKGD, and gIFg chunks. The tEXt and zTXt chunks contain
text information; this is now stored in the "text" element of the info
dictionary; the value is another dictionary of key/value pairs. Each
value is a list of (string, <compressed>) pairs, where <compressed> is
a boolean indicating whether the string was compressed (a zTXt chunk)
or uncompressed (a tEXt chunk). The bKGD chunk is used to add a
"background" field to the info dictionary, and the gIFg chunk is used
to determine a "duration" field.
-Fred
--
Fred L. Drake, Jr. <fdrake@acm.org>
Corporation for National Research Initiatives
1895 Preston White Dr. Reston, VA 20191
--5ydeQFjoGe
Content-Type: text/plain
Content-Description: improve PNG support for PIL
Content-Disposition: inline;
filename="PngImagePlugin.patch"
Content-Transfer-Encoding: 7bit
*** PIL/PngImagePlugin.py Sun Jul 19 10:25:14 1998
--- /home/fdrake/lib/python/PngImagePlugin.py Mon Jul 20 10:40:38 1998
***************
*** 88,93 ****
--- 88,96 ----
def close(self):
del self.queue
self.fp = None
+ if self.__dict__.has_key("crc"):
+ # using crc_skip(); remove circular reference
+ del self.__dict__["crc"]
def push(self, cid, pos, len):
***************
*** 167,172 ****
--- 170,178 ----
# image data
self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
self.im_idat = len
+
+ # This error is to separate the "header" chunks from the remaining
+ # chunks; see PngImageFile.__init__().
raise EOFError
def chunk_IEND(self, pos, len):
***************
*** 193,202 ****
# text
s = self.fp.read(len)
! [k, v] = string.split(s, "\0")
! self.im_info[k] = v
return s
# --------------------------------------------------------------------
# PNG reader
--- 199,266 ----
# text
s = self.fp.read(len)
! [k, v] = string.split(s, "\0", 1)
! self.add_text(k, v)
return s
+ def chunk_zTXt(self, pos, len):
+ s = self.fp.read(len)
+ [k, d] = string.split(s, "\0", 1)
+ if not d:
+ raise SyntaxError, "zTXt chunk missing compression method"
+ m, z = d[0], d[1:]
+ v = _inflate(z)
+ self.add_text(k, v, 1)
+ return s
+
+ def add_text(self, keyword, value, compressed=0):
+ # If there is already a chunk with this keyword, the value of
+ # info[k] will become a list containing all values, otherwise (if
+ # there is no value already), only a single string will be used.
+ value = (value, compressed)
+ try:
+ text = self.im_info["text"]
+ except KeyError:
+ text = self.im_info["text"] = {}
+ if text.has_key(keyword):
+ text[keyword].append(value)
+ else:
+ text[keyword] = [value]
+
+ def chunk_bKGD(self, pos, len):
+ s = self.fp.read(len)
+ if len == 1:
+ self.im_info["background"] = ord(s)
+ elif len == 2:
+ self.im_info["background"] = ord(s[0]) << 8 | ord(s[1])
+ elif len == 6:
+ # tuple (R, G, B)
+ self.im_info["background"] = ((ord(s[0]) << 8 | ord(s[1])),
+ (ord(s[2]) << 8 | ord(s[3])),
+ (ord(s[4]) << 8 | ord(s[5])))
+ elif Image.DEBUG:
+ print "unsupported background chunk in mode", self.im_mode
+ return s
+
+ def chunk_gIFg(self, pos, len):
+ s = self.fp.read(len)
+ if len != 4:
+ raise SyntaxError, "invalid gIFg chunk length"
+ import struct
+ disp, input, delay = struct.unpack(">BBH", s)
+ self.im_info["duration"] = int(delay)
+ return s
+
+
+ def _deflate(data):
+ import zlib
+ co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED,
+ -zlib.MAX_WBITS)
+ return co.compress(data) + co.flush()
+
+ def _inflate(data):
+ import zlib
+ return zlib.decompress(data, -zlib.MAX_WBITS)
# --------------------------------------------------------------------
# PNG reader
***************
*** 232,237 ****
--- 296,302 ----
break
except AttributeError:
+ # presumably an unknown chunk type
if Image.DEBUG:
print cid, pos, len, "(unknown)"
s = self.fp.read(len)
***************
*** 311,317 ****
"L;2": ("L;2", chr(2)+chr(0)),
"L;4": ("L;4", chr(4)+chr(0)),
"L": ("L", chr(8)+chr(0)),
- "I": ("I;16B", chr(16)+chr(0)),
"P;1": ("P;1", chr(1)+chr(3)),
"P;2": ("P;2", chr(2)+chr(3)),
"P;4": ("P;4", chr(4)+chr(3)),
--- 376,381 ----
***************
*** 386,392 ****
raise IOError, "cannot write mode %s as PNG" % mode
if check:
! return check
#
# write minimal PNG file
--- 450,456 ----
raise IOError, "cannot write mode %s as PNG" % mode
if check:
! return check
#
# write minimal PNG file
***************
*** 400,413 ****
chr(0), # 11: filter category
chr(0)) # 12: interlace flag
if im.mode == "P":
! chunk(fp, "PLTE", im.im.getpalette("RGB"))
if 0:
# FIXME: to be supported some day
chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
chunk(fp, "IEND", "")
--- 464,512 ----
chr(0), # 11: filter category
chr(0)) # 12: interlace flag
+ colortype = ord(mode[-1]) # "color type" from the spec.
+ info = im.info.copy()
if im.mode == "P":
! pal = im.im.getpalette("RGB")
! chunk(fp, "PLTE", pal)
! if im.info.has_key("transparency"):
! tRNS_data = None
! if colortype == 3:
! v = [0] * (len(pal) / 3)
! v[im.info["transparency"]] = 1
! tRNS_data = string.join(map(chr, v), '')
! elif colortype in (4, 6):
! # prohibited
! pass
! elif colortype in (0, 2):
! # not yet implemented
! pass
! else:
! raise RuntimeError, "bad colortype value -- internal error"
! if tRNS_data:
! chunk(fp, "tRNS", tRNS_data)
! if im.info.has_key("background") and colortype == 3:
! bKGD_data = chr(im.info["background"])
! chunk(fp, "bKGD", bKGD_data)
! if im.info.has_key("duration"):
! import struct
! disp, input, delay = 0, 0, im.info["duration"]
! gIFg_data = struct.pack(">BBH", disp, input, delay)
! chunk(fp, "gIFg", gIFg_data)
if 0:
# FIXME: to be supported some day
chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
+
+ if im.info.has_key("text"):
+ for k, v in info["text"].items():
+ if type(v) is type(()) and len(v) == 2:
+ v, c = v
+ if c:
+ v = _deflate(v)
+ chunk(fp, c and "zTXt" or "tEXt", v)
chunk(fp, "IEND", "")
--5ydeQFjoGe--