#!/usr/bin/python3
# ===============================================================
# interpret/display PNG file header information
# ===============================================================
# ---------------------------------------------------------------
# ---- display raw bytes
# ---------------------------------------------------------------
def display_raw_bytes(offset:int,length:int,
bary:bytearray) -> None:
line_cnt = 0 # line byte count
line_max = 10 # max bytes per line
idx = offset # byte array index
while True:
if idx > offset+length:
if idx != 0: print()
break
print(f'{bary[idx]:#x}, ',end='')
idx += 1
if line_cnt > line_max:
print()
line_cnt = 0
continue
line_cnt += 1
# ---------------------------------------------------------------
# ---- PNG color type
# ---------------------------------------------------------------
def png_color_type(type:int) -> str:
match type:
case 0:
return 'Grayscale'
case 2:
return 'Truecolor'
case 3:
return 'Indexed'
case 4:
return 'Grayscale and alpha'
case 6:
return 'Truecolor and alpha'
return 'unknown'
# ---------------------------------------------------------------
# ---- PNG pixel_format (color type,channels)
# ---------------------------------------------------------------
def png_pixel_format(pixfmt:int) -> str:
match pixfmt:
case 0:
return ('Indexed',1)
case 1:
return ('Grayscale',1)
case 2:
return ('Grayscale and alpha',2)
case 3:
return ('Truecolor and alpha',4)
return ('unknown',0)
# ---------------------------------------------------------------
# ---- crc check
# ---------------------------------------------------------------
def crc_check(bary:bytearray) -> bool:
return True
# ---------------------------------------------------------------
# ---- IHDR header data
# ---------------------------------------------------------------
def IHDR_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- sRGB header data
# ---------------------------------------------------------------
def sRGB_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- gAMA header data
# ---------------------------------------------------------------
def gAMA_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- PHYs header data
# ---------------------------------------------------------------
def PHYs_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- IDAT header data
# ---------------------------------------------------------------
def IDAT_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- IEND header data
# ---------------------------------------------------------------
def IEND_header_data(bary:bytearray) -> None:
pass
# ---------------------------------------------------------------
# ---- display PNG file header and chunks
# ---------------------------------------------------------------
def display_png_header_and_chunks(file_path,png_bytes:str) -> None:
print('---- PNG Header -------------------------------')
byt1 = int.from_bytes(png_bytes[0:1],'little')
format = png_bytes[1:4].decode()
line_end1 = int.from_bytes(png_bytes[4:5],'little')
line_end2 = int.from_bytes(png_bytes[5:6],'little')
dos_eof_char = int.from_bytes(png_bytes[6:7],'little')
unix_lf = int.from_bytes(png_bytes[7:8],'little')
print(f'file = {file_path}')
print(f'file size = {len(png_bytes)}')
print(f'byt1 = {byt1:02x} (hex)')
print(f'format = {format}')
print(f'line end = {line_end1:02X} {line_end2:02X}')
print(f'DOS EOL char = {dos_eof_char:02X}')
print(f'Unix LF = {unix_lf:02X}')
c_offset = 8
while c_offset < len(png_bytes):
print('---- chunk ------------------------------------')
start = c_offset + 0
end = c_offset + 4
c_len = int.from_bytes(png_bytes[start:end],'big')
start = end
end = end + 4
c_type = png_bytes[start:end].decode()
start = end
end = end + c_len
c_data = png_bytes[start:end]
start = end
end = end + 4
c_crc = int.from_bytes(png_bytes[start:end],'big')
print(f'chunk len = {c_len} (data length)')
print(f'chunk type = {c_type}')
print(f'chunk crc = {c_crc}')
c_offset = end
# ---------------------------------------------------------------
# ---- open PNG and read it directly into a byte array
# ----
# ---- It is not necessary to read in the complete file
# ---- if you are only accessing the headers. For example,
# ---- to read the first 1000 bytes?
# ----
# ---- def load_PNG_to_array(PNG_file_path):
# ---- f = open(PNG_file_path,'rb')
# ---- png_bytes = f.read(1000)
# ---- close(f)
# ---- return png_bytes
# ----
# ---------------------------------------------------------------
def load_png_to_array(png_file_path:str,) -> bytearray:
with open(png_file_path,'rb') as file:
png_bytes = file.read()
return png_bytes
# ---------------------------------------------------------------
# ---- main
# ---------------------------------------------------------------
if __name__ == '__main__':
png_file_paths = [ 'mona_lisa_small_a.png' ]
for file_path in png_file_paths:
print()
png_bytes = load_png_to_array(file_path)
display_png_header_and_chunks(file_path,png_bytes)
print()