solution_248d.py

#!/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()