108 lines
2.6 KiB
Python
108 lines
2.6 KiB
Python
|
#!/usr/bin/python
|
||
|
#
|
||
|
# Copyright 2013 Google, Inc. All Rights Reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
#
|
||
|
# Google Author(s): Behdad Esfahbod
|
||
|
#
|
||
|
|
||
|
import struct, StringIO
|
||
|
|
||
|
|
||
|
class PNG:
|
||
|
|
||
|
signature = bytearray ((137,80,78,71,13,10,26,10))
|
||
|
|
||
|
def __init__ (self, f):
|
||
|
|
||
|
if isinstance(f, basestring):
|
||
|
f = open (f, 'rb')
|
||
|
|
||
|
self.f = f
|
||
|
self.IHDR = None
|
||
|
|
||
|
def tell (self):
|
||
|
return self.f.tell ()
|
||
|
|
||
|
def seek (self, pos):
|
||
|
self.f.seek (pos)
|
||
|
|
||
|
def stream (self):
|
||
|
return self.f
|
||
|
|
||
|
def data (self):
|
||
|
self.seek (0)
|
||
|
return bytearray (self.f.read ())
|
||
|
|
||
|
class BadSignature (Exception): pass
|
||
|
class BadChunk (Exception): pass
|
||
|
|
||
|
def read_signature (self):
|
||
|
header = bytearray (self.f.read (8))
|
||
|
if header != PNG.signature:
|
||
|
raise PNG.BadSignature
|
||
|
return PNG.signature
|
||
|
|
||
|
def read_chunk (self):
|
||
|
length = struct.unpack (">I", self.f.read (4))[0]
|
||
|
chunk_type = self.f.read (4)
|
||
|
chunk_data = self.f.read (length)
|
||
|
if len (chunk_data) != length:
|
||
|
raise PNG.BadChunk
|
||
|
crc = self.f.read (4)
|
||
|
if len (crc) != 4:
|
||
|
raise PNG.BadChunk
|
||
|
return (chunk_type, chunk_data, crc)
|
||
|
|
||
|
def read_IHDR (self):
|
||
|
(chunk_type, chunk_data, crc) = self.read_chunk ()
|
||
|
if chunk_type != "IHDR":
|
||
|
raise PNG.BadChunk
|
||
|
# Width: 4 bytes
|
||
|
# Height: 4 bytes
|
||
|
# Bit depth: 1 byte
|
||
|
# Color type: 1 byte
|
||
|
# Compression method: 1 byte
|
||
|
# Filter method: 1 byte
|
||
|
# Interlace method: 1 byte
|
||
|
return struct.unpack (">IIBBBBB", chunk_data)
|
||
|
|
||
|
def read_header (self):
|
||
|
self.read_signature ()
|
||
|
self.IHDR = self.read_IHDR ()
|
||
|
return self.IHDR
|
||
|
|
||
|
def get_size (self):
|
||
|
if not self.IHDR:
|
||
|
pos = self.tell ()
|
||
|
self.seek (0)
|
||
|
self.read_header ()
|
||
|
self.seek (pos)
|
||
|
return self.IHDR[0:2]
|
||
|
|
||
|
def filter_chunks (self, chunks):
|
||
|
self.seek (0);
|
||
|
out = StringIO.StringIO ()
|
||
|
out.write (self.read_signature ())
|
||
|
while True:
|
||
|
chunk_type, chunk_data, crc = self.read_chunk ()
|
||
|
if chunk_type in chunks:
|
||
|
out.write (struct.pack (">I", len (chunk_data)))
|
||
|
out.write (chunk_type)
|
||
|
out.write (chunk_data)
|
||
|
out.write (crc)
|
||
|
if chunk_type == "IEND":
|
||
|
break
|
||
|
return PNG (out)
|