Module refinery.lib.png

Shared PNG parsing library. Provides structure definitions and constants for parsing Portable Network Graphics (PNG) files, used by both the format extractor unit and the PNG carver.

Expand source code Browse git
"""
Shared PNG parsing library. Provides structure definitions and constants for parsing
Portable Network Graphics (PNG) files, used by both the format extractor unit and the
PNG carver.
"""
from __future__ import annotations

import enum
import zlib

from refinery.lib.structures import Struct, StructReader

PNG_SIGNATURE = b'\x89PNG\r\n\x1A\n'


class PngChunkType(bytes, enum.Enum):
    IHDR = b'IHDR'
    PLTE = b'PLTE'
    IDAT = b'IDAT'
    IEND = b'IEND'
    bKGD = b'bKGD'
    cHRM = b'cHRM'
    cICP = b'cICP'
    dSIG = b'dSIG'
    eXIf = b'eXIf'
    gAMA = b'gAMA'
    hIST = b'hIST'
    iCCP = b'iCCP'
    iTXt = b'iTXt'
    pHYs = b'pHYs'
    sBIT = b'sBIT'
    sPLT = b'sPLT'
    sRGB = b'sRGB'
    sTER = b'sTER'
    tEXt = b'tEXt'
    tIME = b'tIME'
    tRNS = b'tRNS'
    zTXt = b'zTXt'


PNG_CHUNK_TYPES = frozenset(t.value for t in PngChunkType)


class PngChunk(Struct[memoryview]):
    def __init__(self, reader: StructReader[memoryview]):
        self.offset = reader.tell()
        self.size = reader.u32()
        self.type_tag = bytes(reader.read_exactly(4))
        try:
            self.type = PngChunkType(self.type_tag)
        except ValueError:
            self.type = None
        self.data = reader.read_exactly(self.size)
        self.crc = reader.u32()

    @property
    def valid(self) -> bool:
        cs = zlib.crc32(self.type_tag)
        cs = zlib.crc32(self.data, cs)
        return cs & 0xFFFFFFFF == self.crc

    @property
    def type_name(self) -> str:
        if isinstance(self.type, PngChunkType):
            return self.type.name
        return self.type_tag.decode('ascii', errors='replace')


class PngIHDR(Struct):
    def __init__(self, reader: StructReader):
        reader.bigendian = True
        self.width = reader.u32()
        self.height = reader.u32()
        self.bit_depth = reader.u8()
        self.color_type = reader.u8()
        self.compression = reader.u8()
        self.filter = reader.u8()
        self.interlace = reader.u8()


class Png(Struct[memoryview]):
    def __init__(self, reader: StructReader[memoryview]):
        reader.bigendian = True
        signature = reader.read_exactly(8)
        if bytes(signature) != PNG_SIGNATURE:
            raise ValueError('Invalid PNG signature.')
        self.ihdr: PngIHDR | None = None
        self.chunks: list[PngChunk] = []
        self.text_chunks: list[PngChunk] = []
        self.meta_chunks: list[PngChunk] = []
        while not reader.eof:
            chunk = PngChunk(reader)
            self.chunks.append(chunk)
            if chunk.type == PngChunkType.IHDR:
                if self.ihdr is not None:
                    raise ValueError('Duplicate IHDR chunk in PNG file.')
                self.ihdr = PngIHDR.Parse(chunk.data)
            elif chunk.type in (PngChunkType.tEXt, PngChunkType.zTXt, PngChunkType.iTXt):
                self.text_chunks.append(chunk)
            elif isinstance(chunk.type, PngChunkType) and chunk.type not in (
                PngChunkType.IHDR,
                PngChunkType.PLTE,
                PngChunkType.IDAT,
                PngChunkType.IEND,
            ):
                self.meta_chunks.append(chunk)
            if chunk.type == PngChunkType.IEND:
                break

Classes

class PngChunkType (*args, **kwds)

bytes(iterable_of_ints) -> bytes bytes(string, encoding[, errors]) -> bytes bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer bytes(int) -> bytes object of size given by the parameter initialized with null bytes bytes() -> empty bytes object

Construct an immutable array of bytes from: - an iterable yielding integers in range(256) - a text string encoded using the specified encoding - any object implementing the buffer API. - an integer

Expand source code Browse git
class PngChunkType(bytes, enum.Enum):
    IHDR = b'IHDR'
    PLTE = b'PLTE'
    IDAT = b'IDAT'
    IEND = b'IEND'
    bKGD = b'bKGD'
    cHRM = b'cHRM'
    cICP = b'cICP'
    dSIG = b'dSIG'
    eXIf = b'eXIf'
    gAMA = b'gAMA'
    hIST = b'hIST'
    iCCP = b'iCCP'
    iTXt = b'iTXt'
    pHYs = b'pHYs'
    sBIT = b'sBIT'
    sPLT = b'sPLT'
    sRGB = b'sRGB'
    sTER = b'sTER'
    tEXt = b'tEXt'
    tIME = b'tIME'
    tRNS = b'tRNS'
    zTXt = b'zTXt'

Ancestors

  • builtins.bytes
  • enum.Enum

Class variables

var IHDR

The type of the None singleton.

var PLTE

The type of the None singleton.

var IDAT

The type of the None singleton.

var IEND

The type of the None singleton.

var bKGD

The type of the None singleton.

var cHRM

The type of the None singleton.

var cICP

The type of the None singleton.

var dSIG

The type of the None singleton.

var eXIf

The type of the None singleton.

var gAMA

The type of the None singleton.

var hIST

The type of the None singleton.

var iCCP

The type of the None singleton.

var iTXt

The type of the None singleton.

var pHYs

The type of the None singleton.

var sBIT

The type of the None singleton.

var sPLT

The type of the None singleton.

var sRGB

The type of the None singleton.

var sTER

The type of the None singleton.

var tEXt

The type of the None singleton.

var tIME

The type of the None singleton.

var tRNS

The type of the None singleton.

var zTXt

The type of the None singleton.

class PngChunk (reader)

A class to parse structured data. A Struct class can be instantiated as follows:

foo = Struct(data, bar=29)

The initialization routine of the structure will be called with a single argument reader. If the object data is already a StructReader, then it will be passed as reader. Otherwise, the argument will be wrapped in a StructReader. Additional arguments to the struct are passed through.

Expand source code Browse git
class PngChunk(Struct[memoryview]):
    def __init__(self, reader: StructReader[memoryview]):
        self.offset = reader.tell()
        self.size = reader.u32()
        self.type_tag = bytes(reader.read_exactly(4))
        try:
            self.type = PngChunkType(self.type_tag)
        except ValueError:
            self.type = None
        self.data = reader.read_exactly(self.size)
        self.crc = reader.u32()

    @property
    def valid(self) -> bool:
        cs = zlib.crc32(self.type_tag)
        cs = zlib.crc32(self.data, cs)
        return cs & 0xFFFFFFFF == self.crc

    @property
    def type_name(self) -> str:
        if isinstance(self.type, PngChunkType):
            return self.type.name
        return self.type_tag.decode('ascii', errors='replace')

Ancestors

  • Struct
  • typing.Generic
  • collections.abc.Buffer

Static methods

def Parse(reader, *args, **kwargs)

Instance variables

var valid
Expand source code Browse git
@property
def valid(self) -> bool:
    cs = zlib.crc32(self.type_tag)
    cs = zlib.crc32(self.data, cs)
    return cs & 0xFFFFFFFF == self.crc
var type_name
Expand source code Browse git
@property
def type_name(self) -> str:
    if isinstance(self.type, PngChunkType):
        return self.type.name
    return self.type_tag.decode('ascii', errors='replace')
class PngIHDR (reader)

A class to parse structured data. A Struct class can be instantiated as follows:

foo = Struct(data, bar=29)

The initialization routine of the structure will be called with a single argument reader. If the object data is already a StructReader, then it will be passed as reader. Otherwise, the argument will be wrapped in a StructReader. Additional arguments to the struct are passed through.

Expand source code Browse git
class PngIHDR(Struct):
    def __init__(self, reader: StructReader):
        reader.bigendian = True
        self.width = reader.u32()
        self.height = reader.u32()
        self.bit_depth = reader.u8()
        self.color_type = reader.u8()
        self.compression = reader.u8()
        self.filter = reader.u8()
        self.interlace = reader.u8()

Ancestors

  • Struct
  • typing.Generic
  • collections.abc.Buffer

Static methods

def Parse(reader, *args, **kwargs)
class Png (reader)

A class to parse structured data. A Struct class can be instantiated as follows:

foo = Struct(data, bar=29)

The initialization routine of the structure will be called with a single argument reader. If the object data is already a StructReader, then it will be passed as reader. Otherwise, the argument will be wrapped in a StructReader. Additional arguments to the struct are passed through.

Expand source code Browse git
class Png(Struct[memoryview]):
    def __init__(self, reader: StructReader[memoryview]):
        reader.bigendian = True
        signature = reader.read_exactly(8)
        if bytes(signature) != PNG_SIGNATURE:
            raise ValueError('Invalid PNG signature.')
        self.ihdr: PngIHDR | None = None
        self.chunks: list[PngChunk] = []
        self.text_chunks: list[PngChunk] = []
        self.meta_chunks: list[PngChunk] = []
        while not reader.eof:
            chunk = PngChunk(reader)
            self.chunks.append(chunk)
            if chunk.type == PngChunkType.IHDR:
                if self.ihdr is not None:
                    raise ValueError('Duplicate IHDR chunk in PNG file.')
                self.ihdr = PngIHDR.Parse(chunk.data)
            elif chunk.type in (PngChunkType.tEXt, PngChunkType.zTXt, PngChunkType.iTXt):
                self.text_chunks.append(chunk)
            elif isinstance(chunk.type, PngChunkType) and chunk.type not in (
                PngChunkType.IHDR,
                PngChunkType.PLTE,
                PngChunkType.IDAT,
                PngChunkType.IEND,
            ):
                self.meta_chunks.append(chunk)
            if chunk.type == PngChunkType.IEND:
                break

Ancestors

  • Struct
  • typing.Generic
  • collections.abc.Buffer

Static methods

def Parse(reader, *args, **kwargs)