Skip to content

Instantly share code, notes, and snippets.

@cessor
Last active January 17, 2020 17:48
Show Gist options
  • Save cessor/7585a315a26e5a5a44b580961a4078e8 to your computer and use it in GitHub Desktop.
Save cessor/7585a315a26e5a5a44b580961a4078e8 to your computer and use it in GitHub Desktop.
png_chunks.py
"""
Reads PNG Chunk Data.
http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html
"""
import sys
import datetime
import pathlib
import collections
import zlib
def big_endian_number(function):
def convert_bytes(*args, **kwargs):
return int.from_bytes(function(*args, **kwargs), 'big')
return convert_bytes
class _Chunk:
class WrongLength(Exception):
def __init__(self, actual_length, expected_length):
self._actual_length = actual_length
self._expected_length = expected_length
def __str__(self):
return (
f'Wrong amount of data read:\n'
f' Length of data read: {self._actual_length}\n'
f' Expected Length: {self._expected_length}\n'
)
class ChecksumMismatch(Exception):
def __init__(self, actual_crc, expected_crc):
self._actual_crc = actual_crc
self._expected_crc = expected_crc
def __str__(self):
return (
f'Data Corruption detected:\n'
f' Actual: {self._actual_crc}\n'
f' Expected: {self._expected_crc}\n'
)
def __init__(self, length, chunk_type, data, crc):
self._length = length
self._chunk_type = chunk_type
self._data = data
self._crc = crc
self._check_length()
self._check_sum()
def _check_length(self):
if self._length == len(self._data):
return
raise Chunk.WrongLength(
len(self._data), self._length
)
def _check_sum(self):
actual_crc = zlib.crc32(self._chunk_type + self._data)
if actual_crc == self._crc:
return
raise Chunk.ChecksumMismatch(
self._actual_crc, self._crc
)
def __repr__(self):
name = self.__class__.__name__
type_ = self._chunk_type.decode('ascii')
return f'<{name}: {type_} ({self._length})>'
class ImageHeader(_Chunk):
def __init__(self, *args):
super().__init__(*args)
@big_endian_number
def width(self):
return self._data[:4]
@big_endian_number
def height(self):
return self._data[4:8]
@big_endian_number
def bit_depth(self):
return self._data[8:9]
@big_endian_number
def color_type(self):
# IF 6, Pixels are RGB Triple and Bit Depth must be 8 or 16
return self._data[9:10]
@big_endian_number
def compression_method(self):
# PNG 1.1: @0
return self._data[10:11]
@big_endian_number
def filter_method(self):
# PNG 1.1: @0
return self._data[11:12]
@big_endian_number
def interlace_method(self):
# 0 == no interlace
# 1 == interlace
return self._data[12:13]
def __repr__(self):
w, h = self.width(), self.height()
return f'<ImageHeader: {w}x{h}>'
class ImageData(_Chunk):
def deflate(self):
# Data is in zlib Format, thus comes with a header
# (first couple of bytes)
# and an ADLER32 CRC (last 4 bytes)
# This could word off the shelf like this, but I haven't tried
# return zlib.decompress(self._data)
raise NotImplemented()
class ModificationTime(_Chunk):
'''
# Time of last Image Modification
LEN: 00 00 00 07 (7)
TYP: 74 49 4d 45 (tIME)
DAT:
- 07 e4 (YEAR)
- 01 (MONTH)
- 11 (DAY)
- 0c (HOUR)
- 29 (MINUTE)
- 0c (SECOND)
CRC: d0 2e c0 da (SUM)
'''
def __repr__(self):
yyyy = int.from_bytes(self._data[:2], 'big')
month, day, hour, minute, second = self._data[2:]
time = datetime.datetime(yyyy, month, day, hour, minute, second)
return f'<ModificationTime: {time}>'
class Background(_Chunk):
'''
# Background Color
LEN: 00 00 00 06 (6)
TYP: 62 4b 47 44 (bKGD)
DAT:
- 00 ff (RED)
- 00 ff (GREEN)
- 00 ff (BLUE)
CRC: a0 bd a7 93 (SUM)
'''
@big_endian_number
def red(self):
return self._data[0:2]
@big_endian_number
def green(self):
return self._data[2:4]
@big_endian_number
def blue(self):
return self._data[4:6]
def __repr__(self):
rgb = (self.red(), self.green(), self.blue())
color = ''.join(f'{channel:02x}' for channel in rgb)
return f'<Background: #{color}>'
class Text(_Chunk):
KEYWORDS = [
'Title', 'Author', 'Description', 'Copyright',
'Creation Time', 'Software', 'Disclaimer', 'Warning',
'Source', 'Comment'
]
def keyword(self):
content = self._data[:80].decode('utf-8')
for keyword in self.KEYWORDS:
if content.startswith(keyword):
return keyword
return f'<Weird Keyword: {content}>'
def text(self):
keyword = self.keyword()
return self._data[len(keyword):].decode('utf-8')
def __repr__(self):
return f'<Text: {self.keyword()}: "{self.text()}">'
class PhysicalDimensions(_Chunk):
# Resolution?
'''
# Physical Pixel Dimensions
LEN: 00 00 00 09 (9)
TYP: 70 48 59 73 (pHYs)
DAT:
- 00 00 0b 13 (2835) [X Pixels per unit, i.e. 72.009 dpi]
- 00 00 0b 13 (2835) [Y Pixels per unit, i.e. 72.009 dpi]
- 01 (Unit) [0=?, 1=meter]
CRC: 00 9a 9c 18 (SUM)
'''
@big_endian_number
def x_pixels_per_unit(self):
return self._data[0:4]
@big_endian_number
def y_pixels_per_unit(self):
return self._data[4:9]
def __repr__(self):
DPI_FACTOR = 0.0254
dpi = self.x_pixels_per_unit() * DPI_FACTOR
return f'<Resolution: {dpi:.2f} dpi>'
class UnknownChunk(_Chunk):
'''
Null object. I haven't implemented all chunk types.
The most important types are IHDR, IDAT, and IEND.
- PLTE (Palette Information)
Ancillary chunks (identifyable by their lowercase letter, as determined by
bit 5 of the first byte) are:
- tRNS (Transparency)
- gAMA (Gamma Correction)
- cHRM (Primary Chromaticities)
- sRGB (Standard RGB Color Space)
- iCCP (Embedded ICC Profile)
- tEXt, iTXt, and zTXt (Text, International Text, Compressed Text)
- sBIT (Significant bits)
- sPLT (Palette)
- hIST (Palette Histogram)
See: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
'''
pass
class EndOfFile(_Chunk):
pass
class Chunk:
CHUNK_TYPES = {
b'IHDR': ImageHeader,
b'IDAT': ImageData,
b'IEND': EndOfFile,
b'bKGD': Background,
b'pHYs': PhysicalDimensions,
b'tIME': ModificationTime,
b'tEXt': Text,
}
def __new__(cls, length, chunk_type, data, crc):
ChunkType = cls.CHUNK_TYPES.get(chunk_type, UnknownChunk)
return ChunkType(length, chunk_type, data, crc)
class Chunks:
def __init__(self, file):
self._file = file
@big_endian_number
def _read_number(self, n=1):
bytes_ = self._file.read(4)
if not bytes_:
raise StopIteration
return bytes_
def _write_number(self, number):
# TODO: Maybe later...
# To write, use this:
# return number.to_bytes(4, byteorder='big')
raise NotImplemented
def _read_chunk_data(self):
length = self._read_number(4)
chunk_type = self._file.read(4)
data = self._file.read(length)
checksum = self._read_number(4)
return length, chunk_type, data, checksum
def __iter__(self):
data = self._read_chunk_data()
yield ImageHeader(*data)
while True:
data = self._read_chunk_data()
yield Chunk(*data)
class Signature:
'''
The signature is built in such a way that printing the image to a
terminal will not dump all data (on msdos type), hence the "portable"...
SIG: 89
PNG: 50 4e 47 (PNG)
BRK: 0d 0a 1a 0a (CR,LF,EOF,LF)
'''
@classmethod
def read(cls, file):
# Signature
assert file.read(1) == b'\x89'
assert file.read(3) == b'PNG'
# Break: CR,LF,EOF,LF
assert file.read(4) == bytes([0x0d, 0x0a, 0x1a, 0x0a])
return cls()
def print_chunks(file):
Signature.read(file)
for chunk in Chunks(file):
print(chunk)
if __name__ == '__main__':
_, filename = sys.argv
with open(filename, 'rb') as file:
print_chunks(file)

checkers

<ImageHeader: 32x32>
<Background: #ffffff>
<Resolution: 72.01 dpi>
<ModificationTime: 2020-01-17 12:41:12>
<ImageData: IDAT (4139)>
<EndOfFile: IEND (0)>
## Annotated PNG file showing a 32x32 black and white checkers flag
## Annotated Image Header
## http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
## http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html
## COOL, somebody did that: : https://parsiya.net/blog/2018-02-25-extracting-png-chunks-with-go/#idat-chunks
# Signature
SIG: 89
PNG: 50 4e 47 (PNG)
BRK: 0d 0a 1a 0a (CR,LF,EOF,LF)
# Image Header
LEN: 00 00 00 0d (13)
TYP: 49 48 44 52 (IHDR)
DAT:
- 00 00 00 20 (WIDTH, 32)
- 00 00 00 20 (HEIGHT, 32)
- 08 (BIT DEPTH)
- 06 (COLOR TYPE) [Pixels are RGB Triple, Bit Depth must be 8 or 16]
- 00 (COMPRESSION METHOD) [0, atm]
- 00 (FILTER METHOD) [0, atm]
- 00 (INTERLACE METHOD) [No Interlace]
CRC: 73 7a 7af4 (SUM)
# Background Color
LEN: 00 00 00 06 (6)
TYP: 62 4b 47 44 (bKGD)
DAT:
- 00 ff (RED)
- 00 ff (GREEN)
- 00 ff (BLUE)
CRC: a0 bd a7 93 (SUM)
# Physical Pixel Dimensions
LEN: 00 00 00 09 (9)
TYP: 70 48 59 73 (pHYs)
DAT:
- 00 00 0b 13 (2835) [X Pixels per unit, i.e. 72.009 dpi]
- 00 00 0b 13 (2835) [Y Pixels per unit, i.e. 72.009 dpi]
- 01 (Unit) [0=?, 1=meter]
CRC: 00 9a 9c 18 (SUM)
# Time of last Image Modification
LEN: 00 00 00 07 (7)
TYP: 74 49 4d 45 (tIME)
DAT:
- 07 e4 (YEAR)
- 01 (MONTH)
- 11 (DAY)
- 0c (HOUR)
- 29 (MINUTE)
- 0c (SECOND)
CRC: d0 2e c0 da (SUM)
# Image Data, Multiple Possible
LEN: 00 00 10 2b (4139)
TYP: 49 44 41 54 (IDAT)
DAT: (ZLIB FORMAT)
# Filter Type Byte Per Scanline
- 58 [88, ZLIB DEFLATE STUFF]
- 09 [9, ZLIB DEFLATE STUFF]
01 20
10 df ef 00ff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ff00 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 00ff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ff00 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 00ff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ff00
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
00ff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ff00 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 00ff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ff00 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 00ff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ff00 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 00ff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ff00 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 00ff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ff00 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 00ff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ff00 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 00ff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ff00 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 00ff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ff00
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
00ff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ff00 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 00ff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ffff ffff ff00 0000 ffff ffff ff00
0000 ff00 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 0000 00ff ffff ffff 0000 00ff
ffff ffff 00ff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ffff ffff ff00 0000 ffff ffff
ff00 0000 ff00 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 0000 00ff ffff ffff 0000
00ff ffff ffff 00ff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ffff ffff ff00 0000 ffff
ffff ff00 0000 ff00 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 0000 00ff ffff ffff
0000 00ff ffff ffff 00ff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ffff ffff ff00 0000
ffff ffff ff00 0000 ff00 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff 0000 00ff ffff
ffff 0000 00ff ffff ffff
CHK: 27 df f6 88 (ADLER32, ZLIB CHECK)
CRC: e9 75 90 8a (SUM)
LEN: 00 00 00 00 (0)
TYP: 49 45 4e 44 (IEND)
CRC: ae 42 60 82 (SUM)
### CRC: TYP + DAT
>>> import zlib
>>> data = bytes([0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff])
>>> format(zlib.crc32(data), 'x')
'a0bda793'
import sys
class Chunk:
def __init__(self, length, chunk_type, data, crc):
self._length = length
self._chunk_type = chunk_type
self._data = data
self._crc = crc
def __repr__(self):
name = self.__class__.__name__
type_ = self._chunk_type.decode('ascii')
return f'<{name}: {type_} ({self._length})>'
class Chunks:
def __init__(self, file):
self._file = file
def _read_number(self, n=1):
bytes_ = self._file.read(4)
if not bytes_:
raise StopIteration
return int.from_bytes(bytes_, 'big')
def __iter__(self):
while True:
length = self._read_number(4)
chunk_type = self._file.read(4)
data = self._file.read(length)
checksum = self._read_number(4)
yield Chunk(length, chunk_type, data, checksum)
class Signature:
@classmethod
def read(cls, file):
# Signature
assert file.read(4) == b'\x89PNG'
# Break: CR,LF,EOF,LF
assert file.read(4) == bytes([0x0d, 0x0a, 0x1a, 0x0a])
if __name__ == '__main__':
_, filename = sys.argv
with open(filename, 'rb') as file:
Signature.read(file)
for chunk in Chunks(file):
print(chunk)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment