31
31
# See the README file for information on usage and redistribution.
34
from __future__ import print_function
34
36
__version__ = "0.9"
38
import Image, ImageFile, ImagePalette, zlib
42
return ord(c[1]) + (ord(c[0])<<8)
44
return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
46
is_cid = re.compile("\w\w\w\w").match
49
_MAGIC = "\211PNG\r\n\032\n"
40
from . import Image, ImageFile, ImagePalette, _binary
47
is_cid = re.compile(b"\w\w\w\w").match
50
_MAGIC = b"\211PNG\r\n\032\n"
111
112
"Call the appropriate chunk handler"
114
print "STREAM", cid, pos, len
115
return getattr(self, "chunk_" + cid)(pos, len)
115
print("STREAM", cid, pos, len)
116
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len)
117
118
def crc(self, cid, data):
118
119
"Read and verify checksum"
120
121
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
121
122
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
123
raise SyntaxError, "broken PNG file"\
124
"(bad header checksum in %s)" % cid
124
raise SyntaxError("broken PNG file"\
125
"(bad header checksum in %s)" % cid)
126
127
def crc_skip(self, cid, data):
127
128
"Read checksum. Used if the C module is not present"
131
def verify(self, endchunk = "IEND"):
132
def verify(self, endchunk = b"IEND"):
133
134
# Simple approach; just calculate checksum for all remaining
134
135
# blocks. Must be called directly after open.
139
140
cid, pos, len = self.read()
140
141
if cid == endchunk:
157
158
self.chunks.append((cid, data))
159
160
def add_text(self, key, value, zip=0):
161
# The tEXt chunk stores latin-1 text
162
if not isinstance(key, bytes):
163
key = key.encode('latin-1', 'strict')
165
if not isinstance(value, bytes):
166
value = value.encode('latin-1', 'replace')
162
self.add("zTXt", key + "\0\0" + zlib.compress(value))
170
self.add(b"zTXt", key + b"\0\0" + zlib.compress(value))
164
self.add("tEXt", key + "\0" + value)
172
self.add(b"tEXt", key + b"\0" + value)
166
174
# --------------------------------------------------------------------
167
175
# PNG image stream (IHDR/IEND)
189
197
# Null separator 1 byte (null character)
190
198
# Compression method 1 byte (0)
191
199
# Compressed profile n bytes (zlib with deflate compression)
192
i = string.find(s, chr(0))
194
print "iCCP profile name", s[:i]
195
print "Compression method", ord(s[i])
196
comp_method = ord(s[i])
202
print("iCCP profile name", s[:i])
203
print("Compression method", i8(s[i]))
204
comp_method = i8(s[i])
197
205
if comp_method != 0:
198
206
raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
209
217
s = ImageFile._safe_read(self.fp, len)
210
218
self.im_size = i32(s), i32(s[4:])
212
self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))]
220
self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))]
216
224
self.im_info["interlace"] = 1
218
raise SyntaxError, "unknown filter category"
226
raise SyntaxError("unknown filter category")
221
229
def chunk_IDAT(self, pos, len):
278
286
s = ImageFile._safe_read(self.fp, len)
280
k, v = string.split(s, "\0", 1)
288
k, v = s.split(b"\0", 1)
281
289
except ValueError:
282
k = s; v = "" # fallback for broken tEXt tags
290
k = s; v = b"" # fallback for broken tEXt tags
293
k = k.decode('latin-1', 'strict')
294
v = v.decode('latin-1', 'replace')
284
296
self.im_info[k] = self.im_text[k] = v
289
301
# compressed text
290
302
s = ImageFile._safe_read(self.fp, len)
291
k, v = string.split(s, "\0", 1)
292
comp_method = ord(v[0])
303
k, v = s.split(b"\0", 1)
304
comp_method = i8(v[0])
293
305
if comp_method != 0:
294
306
raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method)
296
self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:])
308
v = zlib.decompress(v[1:])
311
k = k.decode('latin-1', 'strict')
312
v = v.decode('latin-1', 'replace')
314
self.im_info[k] = self.im_text[k] = v
299
317
# --------------------------------------------------------------------
391
409
cid, pos, len = self.png.read()
393
if cid not in ["IDAT", "DDAT"]:
411
if cid not in [b"IDAT", b"DDAT"]:
394
412
self.png.push(cid, pos, len)
397
415
self.__idat = len # empty chunks are allowed
417
435
# --------------------------------------------------------------------
421
return chr(i>>8&255) + chr(i&255)
424
return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
427
443
# supported PIL modes, and corresponding rawmodes/bits/color combinations
428
"1": ("1", chr(1)+chr(0)),
429
"L;1": ("L;1", chr(1)+chr(0)),
430
"L;2": ("L;2", chr(2)+chr(0)),
431
"L;4": ("L;4", chr(4)+chr(0)),
432
"L": ("L", chr(8)+chr(0)),
433
"LA": ("LA", chr(8)+chr(4)),
434
"I": ("I;16B", chr(16)+chr(0)),
435
"P;1": ("P;1", chr(1)+chr(3)),
436
"P;2": ("P;2", chr(2)+chr(3)),
437
"P;4": ("P;4", chr(4)+chr(3)),
438
"P": ("P", chr(8)+chr(3)),
439
"RGB": ("RGB", chr(8)+chr(2)),
440
"RGBA":("RGBA", chr(8)+chr(6)),
444
"1": ("1", b'\x01\x00'),
445
"L;1": ("L;1", b'\x01\x00'),
446
"L;2": ("L;2", b'\x02\x00'),
447
"L;4": ("L;4", b'\x04\x00'),
448
"L": ("L", b'\x08\x00'),
449
"LA": ("LA", b'\x08\x04'),
450
"I": ("I;16B", b'\x10\x00'),
451
"P;1": ("P;1", b'\x01\x03'),
452
"P;2": ("P;2", b'\x02\x03'),
453
"P;4": ("P;4", b'\x04\x03'),
454
"P": ("P", b'\x08\x03'),
455
"RGB": ("RGB", b'\x08\x02'),
456
"RGBA":("RGBA", b'\x08\x06'),
443
459
def putchunk(fp, cid, *data):
444
460
"Write a PNG chunk (including CRC field)"
446
data = string.join(data, "")
462
data = b"".join(data)
448
464
fp.write(o32(len(data)) + cid)
458
474
self.chunk = chunk
459
475
def write(self, data):
460
self.chunk(self.fp, "IDAT", data)
476
self.chunk(self.fp, b"IDAT", data)
462
478
def _save(im, fp, filename, chunk=putchunk, check=0):
463
479
# save an image to disk (called by the save method)
492
508
mode = "%s;%d" % (mode, bits)
494
510
# encoder options
495
if im.encoderinfo.has_key("dictionary"):
511
if "dictionary" in im.encoderinfo:
496
512
dictionary = im.encoderinfo["dictionary"]
500
im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
516
im.encoderconfig = ("optimize" in im.encoderinfo, dictionary)
502
518
# get the corresponding PNG mode
504
520
rawmode, mode = _OUTMODES[mode]
506
raise IOError, "cannot write mode %s as PNG" % mode
522
raise IOError("cannot write mode %s as PNG" % mode)
517
533
o32(im.size[0]), o32(im.size[1]), # 0: size
518
534
mode, # 8: depth/type
519
chr(0), # 10: compression
520
chr(0), # 11: filter category
521
chr(0)) # 12: interlace flag
535
b'\0', # 10: compression
536
b'\0', # 11: filter category
537
b'\0') # 12: interlace flag
523
539
if im.mode == "P":
524
chunk(fp, "PLTE", im.im.getpalette("RGB"))
540
chunk(fp, b"PLTE", im.im.getpalette("RGB"))
526
if im.encoderinfo.has_key("transparency"):
542
if "transparency" in im.encoderinfo:
527
543
if im.mode == "P":
528
544
transparency = max(0, min(255, im.encoderinfo["transparency"]))
529
chunk(fp, "tRNS", chr(255) * transparency + chr(0))
545
chunk(fp, b"tRNS", b'\xFF' * transparency + b'\0')
530
546
elif im.mode == "L":
531
547
transparency = max(0, min(65535, im.encoderinfo["transparency"]))
532
chunk(fp, "tRNS", o16(transparency))
548
chunk(fp, b"tRNS", o16(transparency))
533
549
elif im.mode == "RGB":
534
550
red, green, blue = im.encoderinfo["transparency"]
535
chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue))
551
chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
537
553
raise IOError("cannot use transparency for this mode")
540
556
# FIXME: to be supported some day
541
chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
557
chunk(fp, b"gAMA", o32(int(gamma * 100000.0)))
543
559
dpi = im.encoderinfo.get("dpi")
546
562
o32(int(dpi[0] / 0.0254 + 0.5)),
547
563
o32(int(dpi[1] / 0.0254 + 0.5)),
550
566
info = im.encoderinfo.get("pnginfo")
565
581
p = ICCProfile.ICCProfile(im.info["icc_profile"])
566
582
name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79]
567
583
except ImportError:
569
data = name + "\0\0" + zlib.compress(im.info["icc_profile"])
570
chunk(fp, "iCCP", data)
584
name = b"ICC Profile"
585
data = name + b"\0\0" + zlib.compress(im.info["icc_profile"])
586
chunk(fp, b"iCCP", data)
572
588
ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
574
chunk(fp, "IEND", "")
590
chunk(fp, b"IEND", b"")