~ubuntu-branches/ubuntu/wily/python-imaging/wily

« back to all changes in this revision

Viewing changes to PIL/PngImagePlugin.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-01-31 20:49:20 UTC
  • mfrom: (1.1.4)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130131204920-7tnuhqhlsqdza4c2
Rewrite build dependencies to allow cross builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
# See the README file for information on usage and redistribution.
32
32
#
33
33
 
 
34
from __future__ import print_function
 
35
 
34
36
__version__ = "0.9"
35
37
 
36
 
import re, string
37
 
 
38
 
import Image, ImageFile, ImagePalette, zlib
39
 
 
40
 
 
41
 
def i16(c):
42
 
    return ord(c[1]) + (ord(c[0])<<8)
43
 
def i32(c):
44
 
    return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
45
 
 
46
 
is_cid = re.compile("\w\w\w\w").match
47
 
 
48
 
 
49
 
_MAGIC = "\211PNG\r\n\032\n"
 
38
import re
 
39
 
 
40
from . import Image, ImageFile, ImagePalette, _binary
 
41
import zlib
 
42
 
 
43
i8 = _binary.i8
 
44
i16 = _binary.i16be
 
45
i32 = _binary.i32be
 
46
 
 
47
is_cid = re.compile(b"\w\w\w\w").match
 
48
 
 
49
 
 
50
_MAGIC = b"\211PNG\r\n\032\n"
50
51
 
51
52
 
52
53
_MODES = {
96
97
            len = i32(s)
97
98
 
98
99
        if not is_cid(cid):
99
 
            raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
 
100
            raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
100
101
 
101
102
        return cid, pos, len
102
103
 
111
112
        "Call the appropriate chunk handler"
112
113
 
113
114
        if Image.DEBUG:
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)
116
117
 
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))
122
123
        if crc1 != crc2:
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)
125
126
 
126
127
    def crc_skip(self, cid, data):
127
128
        "Read checksum.  Used if the C module is not present"
128
129
 
129
130
        self.fp.read(4)
130
131
 
131
 
    def verify(self, endchunk = "IEND"):
 
132
    def verify(self, endchunk = b"IEND"):
132
133
 
133
134
        # Simple approach; just calculate checksum for all remaining
134
135
        # blocks.  Must be called directly after open.
135
136
 
136
137
        cids = []
137
138
 
138
 
        while 1:
 
139
        while True:
139
140
            cid, pos, len = self.read()
140
141
            if cid == endchunk:
141
142
                break
157
158
        self.chunks.append((cid, data))
158
159
 
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')
 
164
 
 
165
        if not isinstance(value, bytes):
 
166
            value = value.encode('latin-1', 'replace')
 
167
 
160
168
        if zip:
161
169
            import zlib
162
 
            self.add("zTXt", key + "\0\0" + zlib.compress(value))
 
170
            self.add(b"zTXt", key + b"\0\0" + zlib.compress(value))
163
171
        else:
164
 
            self.add("tEXt", key + "\0" + value)
 
172
            self.add(b"tEXt", key + b"\0" + value)
165
173
 
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))
 
200
        i = s.find(b"\0")
193
201
        if Image.DEBUG:
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)
199
207
        try:
209
217
        s = ImageFile._safe_read(self.fp, len)
210
218
        self.im_size = i32(s), i32(s[4:])
211
219
        try:
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]))]
213
221
        except:
214
222
            pass
215
 
        if ord(s[12]):
 
223
        if i8(s[12]):
216
224
            self.im_info["interlace"] = 1
217
 
        if ord(s[11]):
218
 
            raise SyntaxError, "unknown filter category"
 
225
        if i8(s[11]):
 
226
            raise SyntaxError("unknown filter category")
219
227
        return s
220
228
 
221
229
    def chunk_IDAT(self, pos, len):
243
251
        # transparency
244
252
        s = ImageFile._safe_read(self.fp, len)
245
253
        if self.im_mode == "P":
246
 
            i = string.find(s, chr(0))
 
254
            i = s.find(b"\0")
247
255
            if i >= 0:
248
256
                self.im_info["transparency"] = i
249
257
        elif self.im_mode == "L":
264
272
        # pixels per unit
265
273
        s = ImageFile._safe_read(self.fp, len)
266
274
        px, py = i32(s), i32(s[4:])
267
 
        unit = ord(s[8])
 
275
        unit = i8(s[8])
268
276
        if unit == 1: # meter
269
277
            dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
270
278
            self.im_info["dpi"] = dpi
277
285
        # text
278
286
        s = ImageFile._safe_read(self.fp, len)
279
287
        try:
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
283
291
        if k:
 
292
            if bytes is not str:
 
293
                k = k.decode('latin-1', 'strict')
 
294
                v = v.decode('latin-1', 'replace')
 
295
 
284
296
            self.im_info[k] = self.im_text[k] = v
285
297
        return s
286
298
 
288
300
 
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)
295
307
        import zlib
296
 
        self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:])
 
308
        v = zlib.decompress(v[1:])
 
309
 
 
310
        if bytes is not str:
 
311
            k = k.decode('latin-1', 'strict')
 
312
            v = v.decode('latin-1', 'replace')
 
313
 
 
314
        self.im_info[k] = self.im_text[k] = v
297
315
        return s
298
316
 
299
317
# --------------------------------------------------------------------
313
331
    def _open(self):
314
332
 
315
333
        if self.fp.read(8) != _MAGIC:
316
 
            raise SyntaxError, "not a PNG file"
 
334
            raise SyntaxError("not a PNG file")
317
335
 
318
336
        #
319
337
        # Parse headers up to the first IDAT chunk
320
338
 
321
339
        self.png = PngStream(self.fp)
322
340
 
323
 
        while 1:
 
341
        while True:
324
342
 
325
343
            #
326
344
            # get next chunk
333
351
                break
334
352
            except AttributeError:
335
353
                if Image.DEBUG:
336
 
                    print cid, pos, len, "(unknown)"
 
354
                    print(cid, pos, len, "(unknown)")
337
355
                s = ImageFile._safe_read(self.fp, len)
338
356
 
339
357
            self.png.crc(cid, s)
390
408
 
391
409
            cid, pos, len = self.png.read()
392
410
 
393
 
            if cid not in ["IDAT", "DDAT"]:
 
411
            if cid not in [b"IDAT", b"DDAT"]:
394
412
                self.png.push(cid, pos, len)
395
 
                return ""
 
413
                return b""
396
414
 
397
415
            self.__idat = len # empty chunks are allowed
398
416
 
417
435
# --------------------------------------------------------------------
418
436
# PNG writer
419
437
 
420
 
def o16(i):
421
 
    return chr(i>>8&255) + chr(i&255)
422
 
 
423
 
def o32(i):
424
 
    return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
 
438
o8 = _binary.o8
 
439
o16 = _binary.o16be
 
440
o32 = _binary.o32be
425
441
 
426
442
_OUTMODES = {
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'),
441
457
}
442
458
 
443
459
def putchunk(fp, cid, *data):
444
460
    "Write a PNG chunk (including CRC field)"
445
461
 
446
 
    data = string.join(data, "")
 
462
    data = b"".join(data)
447
463
 
448
464
    fp.write(o32(len(data)) + cid)
449
465
    fp.write(data)
457
473
        self.fp = fp
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)
461
477
 
462
478
def _save(im, fp, filename, chunk=putchunk, check=0):
463
479
    # save an image to disk (called by the save method)
469
485
        #
470
486
        # attempt to minimize storage requirements for palette images
471
487
 
472
 
        if im.encoderinfo.has_key("bits"):
 
488
        if "bits" in im.encoderinfo:
473
489
 
474
490
            # number of bits specified by user
475
491
            n = 1 << im.encoderinfo["bits"]
492
508
            mode = "%s;%d" % (mode, bits)
493
509
 
494
510
    # encoder options
495
 
    if im.encoderinfo.has_key("dictionary"):
 
511
    if "dictionary" in im.encoderinfo:
496
512
        dictionary = im.encoderinfo["dictionary"]
497
513
    else:
498
 
        dictionary = ""
 
514
        dictionary = b""
499
515
 
500
 
    im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
 
516
    im.encoderconfig = ("optimize" in im.encoderinfo, dictionary)
501
517
 
502
518
    # get the corresponding PNG mode
503
519
    try:
504
520
        rawmode, mode = _OUTMODES[mode]
505
521
    except KeyError:
506
 
        raise IOError, "cannot write mode %s as PNG" % mode
 
522
        raise IOError("cannot write mode %s as PNG" % mode)
507
523
 
508
524
    if check:
509
525
        return check
513
529
 
514
530
    fp.write(_MAGIC)
515
531
 
516
 
    chunk(fp, "IHDR",
 
532
    chunk(fp, b"IHDR",
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
522
538
 
523
539
    if im.mode == "P":
524
 
        chunk(fp, "PLTE", im.im.getpalette("RGB"))
 
540
        chunk(fp, b"PLTE", im.im.getpalette("RGB"))
525
541
 
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))
536
552
        else:
537
553
            raise IOError("cannot use transparency for this mode")
538
554
 
539
555
    if 0:
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)))
542
558
 
543
559
    dpi = im.encoderinfo.get("dpi")
544
560
    if dpi:
545
 
        chunk(fp, "pHYs",
 
561
        chunk(fp, b"pHYs",
546
562
              o32(int(dpi[0] / 0.0254 + 0.5)),
547
563
              o32(int(dpi[1] / 0.0254 + 0.5)),
548
 
              chr(1))
 
564
              b'\x01')
549
565
 
550
566
    info = im.encoderinfo.get("pnginfo")
551
567
    if info:
553
569
            chunk(fp, cid, data)
554
570
 
555
571
    # ICC profile writing support -- 2008-06-06 Florian Hoech
556
 
    if im.info.has_key("icc_profile"):
 
572
    if "icc_profile" in im.info:
557
573
        # ICC profile
558
574
        # according to PNG spec, the iCCP chunk contains:
559
575
        # Profile name  1-79 bytes (character string)
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:
568
 
            name = "ICC Profile"
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)
571
587
 
572
588
    ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
573
589
 
574
 
    chunk(fp, "IEND", "")
 
590
    chunk(fp, b"IEND", b"")
575
591
 
576
592
    try:
577
593
        fp.flush()
593
609
            self.data.append(chunk)
594
610
 
595
611
    def append(fp, cid, *data):
596
 
        data = string.join(data, "")
 
612
        data = b"".join(data)
597
613
        hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
598
614
        crc = o16(hi) + o16(lo)
599
615
        fp.append((cid, data, crc))