PNG (Portable Network Graphics) file: Python parsing library

NOTICE: Many of the documentation comments (or docstrings) in this file were copied from or derived from the Portable Network Graphics (PNG) Specification (Third Edition). Copyright © 1996-2025 World Wide Web Consortium. https://www.w3.org/copyright/software-license-2023/

The full text of the license for the original W3C PNG specification is provided below:

Software and Document license - 2023 version

This work is being provided by the copyright holders under the following license.

License

By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.

Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, including modifications:

  • The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
  • Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C software and document short notice should be included.
  • Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived from [title and URI of the W3C document]. Copyright © [$year-of-document] World Wide Web Consortium. https://www.w3.org/copyright/software-license-2023/"

Disclaimers

THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.

COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.

The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders.


Test files for APNG can be found at the following locations:

This page hosts a formal specification of PNG (Portable Network Graphics) file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All Python code generated by Kaitai Struct depends on the Kaitai Struct runtime library for Python. You must add this dependency to your project before you can parse or serialize any data.

The Python runtime library can be installed from PyPI:

python3 -m pip install kaitaistruct

Code

Parse a local file and get structure in memory:

data = Png.from_file("path/to/local/file.png")

Or parse structure from a bytes:

from kaitaistruct import KaitaiStream, BytesIO

raw = b"\x00\x01\x02..."
data = Png(KaitaiStream(BytesIO(raw)))

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic # => get magic

Python source code to parse PNG (Portable Network Graphics) file

png.py

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
# type: ignore

import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
import exif
import icc_4
from enum import IntEnum
import zlib


if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 11):
    raise Exception("Incompatible Kaitai Struct Python API: 0.11 or later is required, but you have %s" % (kaitaistruct.__version__))

class Png(KaitaiStruct):
    """NOTICE: Many of the documentation comments (or docstrings) in this file were
    copied from or derived from the [Portable Network Graphics (PNG) Specification
    (Third Edition)](https://www.w3.org/TR/2025/REC-png-3-20250624/).
    Copyright © 1996-2025 [World Wide Web Consortium](https://www.w3.org/).
    <https://www.w3.org/copyright/software-license-2023/>
    
    The full text of the license for the original W3C PNG specification is
    provided below:
    
    > ## Software and Document license - 2023 version
    >
    > This work is being provided by the copyright holders under the following
    > license.
    >
    > ### License
    >
    > By obtaining and/or copying this work, you (the licensee) agree that you
    > have read, understood, and will comply with the following terms and
    > conditions.
    >
    > Permission to copy, modify, and distribute this work, with or without
    > modification, for any purpose and without fee or royalty is hereby granted,
    > provided that you include the following on ALL copies of the work or
    > portions thereof, including modifications:
    >
    > * The full text of this NOTICE in a location viewable to users of the
    >   redistributed or derivative work.
    > * Any pre-existing intellectual property disclaimers, notices, or terms and
    >   conditions. If none exist, the [W3C software and document short
    >   notice](https://www.w3.org/Consortium/Legal/2023/copyright-software-short-notice.html)
    >   should be included.
    > * Notice of any changes or modifications, through a copyright statement on
    >   the new code or document such as "This software or document includes
    >   material copied from or derived from [title and URI of the W3C document].
    >   Copyright © [$year-of-document] [World Wide Web
    >   Consortium](https://www.w3.org/).
    >   <https://www.w3.org/copyright/software-license-2023/>"
    >
    > ### Disclaimers
    >
    > THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
    > OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES
    > OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
    > THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
    > COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
    >
    > COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
    > CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
    >
    > The name and trademarks of copyright holders may NOT be used in advertising
    > or publicity pertaining to the work without specific, written prior
    > permission. Title to copyright in this work will at all times remain with
    > copyright holders.
    
    ---
    
    Test files for APNG can be found at the following locations:
    
      * <https://philip.html5.org/tests/apng/tests.html>
      * <http://littlesvr.ca/apng/>
    """

    class BlendOpValues(IntEnum):
        source = 0
        over = 1

    class ColorType(IntEnum):
        greyscale = 0
        truecolor = 2
        indexed = 3
        greyscale_alpha = 4
        truecolor_alpha = 6

    class CompressionMethods(IntEnum):
        zlib = 0

    class DisposeOpValues(IntEnum):
        none = 0
        background = 1
        previous = 2

    class FilterMethod(IntEnum):
        base = 0

    class InterlaceMethod(IntEnum):
        none = 0
        adam7 = 1

    class PhysUnit(IntEnum):
        unknown = 0
        meter = 1
    def __init__(self, _io, _parent=None, _root=None):
        super(Png, self).__init__(_io)
        self._parent = _parent
        self._root = _root or self
        self._read()

    def _read(self):
        self.magic = self._io.read_bytes(8)
        if not self.magic == b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A":
            raise kaitaistruct.ValidationNotEqualError(b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", self.magic, self._io, u"/seq/0")
        self.ihdr_len = self._io.read_u4be()
        if not self.ihdr_len == 13:
            raise kaitaistruct.ValidationNotEqualError(13, self.ihdr_len, self._io, u"/seq/1")
        self.ihdr_type = self._io.read_bytes(4)
        if not self.ihdr_type == b"\x49\x48\x44\x52":
            raise kaitaistruct.ValidationNotEqualError(b"\x49\x48\x44\x52", self.ihdr_type, self._io, u"/seq/2")
        self.ihdr = Png.IhdrChunk(self._io, self, self._root)
        self.ihdr_crc = self._io.read_u4be()
        self.chunks = []
        i = 0
        while True:
            _ = Png.Chunk(self._io, self, self._root)
            self.chunks.append(_)
            if  ((_.type == u"IEND") or (self._io.is_eof())) :
                break
            i += 1


    def _fetch_instances(self):
        pass
        self.ihdr._fetch_instances()
        for i in range(len(self.chunks)):
            pass
            self.chunks[i]._fetch_instances()


    class AdobeFireworksChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://stackoverflow.com/questions/4242402/the-fireworks-png-format-any-insight-any-libs/51683285#51683285
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.AdobeFireworksChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self._raw_preview_data = self._io.read_bytes_full()
            self.preview_data = zlib.decompress(self._raw_preview_data)


        def _fetch_instances(self):
            pass


    class AnimationControlChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#acTL-chunk
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.AnimationControlChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.num_frames = self._io.read_u4be()
            self.num_plays = self._io.read_u4be()


        def _fetch_instances(self):
            pass


    class AtchChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://github.com/skeeto/scratch/tree/58470254f4a95cdf7a53888e405c851c21eb2cae/pngattach
        
        
        .. seealso::
           A new protocol and tool for PNG file attachments - https://nullprogram.com/blog/2021/12/31/
        """

        class CompressionAttachMethods(IntEnum):
            none = 0
            zlib = 1
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.AtchChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.file_name = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
            _ = self.file_name
            if not  ((len(_) != 0) and (_[0:1] != u".")) :
                raise kaitaistruct.ValidationExprError(self.file_name, self._io, u"/types/atch_chunk/seq/0")
            self.compression = KaitaiStream.resolve_enum(Png.AtchChunk.CompressionAttachMethods, self._io.read_u1())
            if not isinstance(self.compression, Png.AtchChunk.CompressionAttachMethods):
                raise kaitaistruct.ValidationNotInEnumError(self.compression, self._io, u"/types/atch_chunk/seq/1")
            if self.compression == Png.AtchChunk.CompressionAttachMethods.none:
                pass
                self.data_plain = self._io.read_bytes_full()

            if self.compression == Png.AtchChunk.CompressionAttachMethods.zlib:
                pass
                self._raw_data_zlib = self._io.read_bytes_full()
                self.data_zlib = zlib.decompress(self._raw_data_zlib)



        def _fetch_instances(self):
            pass
            if self.compression == Png.AtchChunk.CompressionAttachMethods.none:
                pass

            if self.compression == Png.AtchChunk.CompressionAttachMethods.zlib:
                pass


        @property
        def data(self):
            if hasattr(self, '_m_data'):
                return self._m_data

            self._m_data = (self.data_plain if self.compression == Png.AtchChunk.CompressionAttachMethods.none else self.data_zlib)
            return getattr(self, '_m_data', None)


    class BkgdChunk(KaitaiStruct):
        """Background chunk stores default background color to display this
        image against. Contents depend on `color_type` of the image.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11bKGD
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.BkgdChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.bkgd = Png.BkgdGreyscale(self._io, self, self._root)
            elif _on == Png.ColorType.greyscale_alpha:
                pass
                self.bkgd = Png.BkgdGreyscale(self._io, self, self._root)
            elif _on == Png.ColorType.indexed:
                pass
                self.bkgd = Png.BkgdIndexed(self._io, self, self._root)
            elif _on == Png.ColorType.truecolor:
                pass
                self.bkgd = Png.BkgdTruecolor(self._io, self, self._root)
            elif _on == Png.ColorType.truecolor_alpha:
                pass
                self.bkgd = Png.BkgdTruecolor(self._io, self, self._root)


        def _fetch_instances(self):
            pass
            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.bkgd._fetch_instances()
            elif _on == Png.ColorType.greyscale_alpha:
                pass
                self.bkgd._fetch_instances()
            elif _on == Png.ColorType.indexed:
                pass
                self.bkgd._fetch_instances()
            elif _on == Png.ColorType.truecolor:
                pass
                self.bkgd._fetch_instances()
            elif _on == Png.ColorType.truecolor_alpha:
                pass
                self.bkgd._fetch_instances()


    class BkgdGreyscale(KaitaiStruct):
        """Background chunk for greyscale images."""
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.BkgdGreyscale, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.value = self._io.read_u2be()


        def _fetch_instances(self):
            pass


    class BkgdIndexed(KaitaiStruct):
        """Background chunk for images with indexed palette."""
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.BkgdIndexed, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.palette_index = self._io.read_u1()


        def _fetch_instances(self):
            pass


    class BkgdTruecolor(KaitaiStruct):
        """Background chunk for truecolor images."""
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.BkgdTruecolor, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.red = self._io.read_u2be()
            self.green = self._io.read_u2be()
            self.blue = self._io.read_u2be()


        def _fetch_instances(self):
            pass


    class ChrmChromaticity(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.ChrmChromaticity, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.x_int = self._io.read_u4be()
            self.y_int = self._io.read_u4be()


        def _fetch_instances(self):
            pass

        @property
        def x(self):
            if hasattr(self, '_m_x'):
                return self._m_x

            self._m_x = self.x_int / 100000.0
            return getattr(self, '_m_x', None)

        @property
        def y(self):
            if hasattr(self, '_m_y'):
                return self._m_y

            self._m_y = self.y_int / 100000.0
            return getattr(self, '_m_y', None)


    class ChrmChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#11cHRM
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.ChrmChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.white_point = Png.ChrmChromaticity(self._io, self, self._root)
            self.red = Png.ChrmChromaticity(self._io, self, self._root)
            self.green = Png.ChrmChromaticity(self._io, self, self._root)
            self.blue = Png.ChrmChromaticity(self._io, self, self._root)


        def _fetch_instances(self):
            pass
            self.white_point._fetch_instances()
            self.red._fetch_instances()
            self.green._fetch_instances()
            self.blue._fetch_instances()


    class Chunk(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.Chunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.len = self._io.read_u4be()
            self.type_raw = self._io.read_bytes(4)
            _ = self.type_raw
            if not  (( (( ((KaitaiStream.byte_array_index(_, 0) >= 65) and (KaitaiStream.byte_array_index(_, 0) <= 90)) ) or ( ((KaitaiStream.byte_array_index(_, 0) >= 97) and (KaitaiStream.byte_array_index(_, 0) <= 122)) )) ) and ( (( ((KaitaiStream.byte_array_index(_, 1) >= 65) and (KaitaiStream.byte_array_index(_, 1) <= 90)) ) or ( ((KaitaiStream.byte_array_index(_, 1) >= 97) and (KaitaiStream.byte_array_index(_, 1) <= 122)) )) ) and ( (( ((KaitaiStream.byte_array_index(_, 2) >= 65) and (KaitaiStream.byte_array_index(_, 2) <= 90)) ) or ( ((KaitaiStream.byte_array_index(_, 2) >= 97) and (KaitaiStream.byte_array_index(_, 2) <= 122)) )) ) and ( (( ((KaitaiStream.byte_array_index(_, 3) >= 65) and (KaitaiStream.byte_array_index(_, 3) <= 90)) ) or ( ((KaitaiStream.byte_array_index(_, 3) >= 97) and (KaitaiStream.byte_array_index(_, 3) <= 122)) )) )) :
                raise kaitaistruct.ValidationExprError(self.type_raw, self._io, u"/types/chunk/seq/1")
            _on = self.type
            if _on == u"PLTE":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.PlteChunk(_io__raw_body, self, self._root)
            elif _on == u"acTL":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.AnimationControlChunk(_io__raw_body, self, self._root)
            elif _on == u"atCh":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.AtchChunk(_io__raw_body, self, self._root)
            elif _on == u"bKGD":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.BkgdChunk(_io__raw_body, self, self._root)
            elif _on == u"cHRM":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.ChrmChunk(_io__raw_body, self, self._root)
            elif _on == u"cICP":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.CicpChunk(_io__raw_body, self, self._root)
            elif _on == u"cLLI":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.ClliChunk(_io__raw_body, self, self._root)
            elif _on == u"eXIf":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.ExifChunk(_io__raw_body, self, self._root)
            elif _on == u"fcTL":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.FrameControlChunk(_io__raw_body, self, self._root)
            elif _on == u"fdAT":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.FrameDataChunk(_io__raw_body, self, self._root)
            elif _on == u"gAMA":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.GamaChunk(_io__raw_body, self, self._root)
            elif _on == u"hIST":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.HistChunk(_io__raw_body, self, self._root)
            elif _on == u"iCCP":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.IccpChunk(_io__raw_body, self, self._root)
            elif _on == u"iTXt":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.InternationalTextChunk(_io__raw_body, self, self._root)
            elif _on == u"mDCV":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.MdcvChunk(_io__raw_body, self, self._root)
            elif _on == u"mkBS":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.AdobeFireworksChunk(_io__raw_body, self, self._root)
            elif _on == u"mkTS":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.AdobeFireworksChunk(_io__raw_body, self, self._root)
            elif _on == u"pHYs":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.PhysChunk(_io__raw_body, self, self._root)
            elif _on == u"prVW":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.AdobeFireworksChunk(_io__raw_body, self, self._root)
            elif _on == u"sBIT":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.SbitChunk(_io__raw_body, self, self._root)
            elif _on == u"sPLT":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.SpltChunk(_io__raw_body, self, self._root)
            elif _on == u"sRGB":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.SrgbChunk(_io__raw_body, self, self._root)
            elif _on == u"skMf":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.EvernoteSkmfChunk(_io__raw_body, self, self._root)
            elif _on == u"skRf":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.EvernoteSkrfChunk(_io__raw_body, self, self._root)
            elif _on == u"tEXt":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.TextChunk(_io__raw_body, self, self._root)
            elif _on == u"tIME":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.TimeChunk(_io__raw_body, self, self._root)
            elif _on == u"tRNS":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.TrnsChunk(_io__raw_body, self, self._root)
            elif _on == u"zTXt":
                pass
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = Png.CompressedTextChunk(_io__raw_body, self, self._root)
            else:
                pass
                self.body = self._io.read_bytes(self.len)
            self.crc = self._io.read_u4be()


        def _fetch_instances(self):
            pass
            _on = self.type
            if _on == u"PLTE":
                pass
                self.body._fetch_instances()
            elif _on == u"acTL":
                pass
                self.body._fetch_instances()
            elif _on == u"atCh":
                pass
                self.body._fetch_instances()
            elif _on == u"bKGD":
                pass
                self.body._fetch_instances()
            elif _on == u"cHRM":
                pass
                self.body._fetch_instances()
            elif _on == u"cICP":
                pass
                self.body._fetch_instances()
            elif _on == u"cLLI":
                pass
                self.body._fetch_instances()
            elif _on == u"eXIf":
                pass
                self.body._fetch_instances()
            elif _on == u"fcTL":
                pass
                self.body._fetch_instances()
            elif _on == u"fdAT":
                pass
                self.body._fetch_instances()
            elif _on == u"gAMA":
                pass
                self.body._fetch_instances()
            elif _on == u"hIST":
                pass
                self.body._fetch_instances()
            elif _on == u"iCCP":
                pass
                self.body._fetch_instances()
            elif _on == u"iTXt":
                pass
                self.body._fetch_instances()
            elif _on == u"mDCV":
                pass
                self.body._fetch_instances()
            elif _on == u"mkBS":
                pass
                self.body._fetch_instances()
            elif _on == u"mkTS":
                pass
                self.body._fetch_instances()
            elif _on == u"pHYs":
                pass
                self.body._fetch_instances()
            elif _on == u"prVW":
                pass
                self.body._fetch_instances()
            elif _on == u"sBIT":
                pass
                self.body._fetch_instances()
            elif _on == u"sPLT":
                pass
                self.body._fetch_instances()
            elif _on == u"sRGB":
                pass
                self.body._fetch_instances()
            elif _on == u"skMf":
                pass
                self.body._fetch_instances()
            elif _on == u"skRf":
                pass
                self.body._fetch_instances()
            elif _on == u"tEXt":
                pass
                self.body._fetch_instances()
            elif _on == u"tIME":
                pass
                self.body._fetch_instances()
            elif _on == u"tRNS":
                pass
                self.body._fetch_instances()
            elif _on == u"zTXt":
                pass
                self.body._fetch_instances()
            else:
                pass

        @property
        def is_ancillary(self):
            """false = critical chunk, true = ancillary chunk
            """
            if hasattr(self, '_m_is_ancillary'):
                return self._m_is_ancillary

            self._m_is_ancillary = KaitaiStream.byte_array_index(self.type_raw, 0) & 32 != 0
            return getattr(self, '_m_is_ancillary', None)

        @property
        def is_private(self):
            """false = public chunk (defined by the W3C), true = private chunk (can
            be defined by anyone)
            """
            if hasattr(self, '_m_is_private'):
                return self._m_is_private

            self._m_is_private = KaitaiStream.byte_array_index(self.type_raw, 1) & 32 != 0
            return getattr(self, '_m_is_private', None)

        @property
        def is_safe_to_copy(self):
            """Defines whether the chunk may be copied if the image data (i.e.
            pixels) is modified. This tells PNG editors how to handle unknown
            chunks - see section [14.2 Behavior of PNG
            editors](https://www.w3.org/TR/2025/REC-png-3-20250624/#14Ordering) in
            the official specification.
            """
            if hasattr(self, '_m_is_safe_to_copy'):
                return self._m_is_safe_to_copy

            self._m_is_safe_to_copy = KaitaiStream.byte_array_index(self.type_raw, 3) & 32 != 0
            return getattr(self, '_m_is_safe_to_copy', None)

        @property
        def reserved_bit(self):
            """Should be `false`, i.e. all chunk types should have uppercase third
            letters (the lowercase third letter is reserved for possible future
            extensions to the PNG standard)
            """
            if hasattr(self, '_m_reserved_bit'):
                return self._m_reserved_bit

            self._m_reserved_bit = KaitaiStream.byte_array_index(self.type_raw, 2) & 32 != 0
            return getattr(self, '_m_reserved_bit', None)

        @property
        def type(self):
            if hasattr(self, '_m_type'):
                return self._m_type

            self._m_type = (self.type_raw).decode(u"ASCII")
            return getattr(self, '_m_type', None)


    class CicpChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#cICP-chunk
        
        
        .. seealso::
           Source - https://w3c.github.io/png/Implementation_Report_3e/#cicp
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.CicpChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.color_primaries = self._io.read_u1()
            self.transfer_function = self._io.read_u1()
            self.matrix_coefficients = self._io.read_u1()
            if not self.matrix_coefficients == 0:
                raise kaitaistruct.ValidationNotEqualError(0, self.matrix_coefficients, self._io, u"/types/cicp_chunk/seq/2")
            self.video_full_range_flag = self._io.read_u1()
            if not  ((self.video_full_range_flag == 0) or (self.video_full_range_flag == 1)) :
                raise kaitaistruct.ValidationNotAnyOfError(self.video_full_range_flag, self._io, u"/types/cicp_chunk/seq/3")


        def _fetch_instances(self):
            pass


    class ClliChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#cLLI-chunk
        
        
        .. seealso::
           Source - https://w3c.github.io/png/Implementation_Report_3e/#light
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.ClliChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.max_content_light_level_int = self._io.read_u4be()
            self.max_frame_average_light_level_int = self._io.read_u4be()


        def _fetch_instances(self):
            pass

        @property
        def max_content_light_level(self):
            """Maximum Content Light Level (MaxCLL), in cd/m^2."""
            if hasattr(self, '_m_max_content_light_level'):
                return self._m_max_content_light_level

            self._m_max_content_light_level = self.max_content_light_level_int * 0.0001
            return getattr(self, '_m_max_content_light_level', None)

        @property
        def max_frame_average_light_level(self):
            """Maximum Frame Average Light Level (MaxFALL), in cd/m^2."""
            if hasattr(self, '_m_max_frame_average_light_level'):
                return self._m_max_frame_average_light_level

            self._m_max_frame_average_light_level = self.max_frame_average_light_level_int * 0.0001
            return getattr(self, '_m_max_frame_average_light_level', None)


    class CompressedText(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.CompressedText, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.value = (self._io.read_bytes_full()).decode(u"ISO-8859-1")


        def _fetch_instances(self):
            pass


    class CompressedTextChunk(KaitaiStruct):
        """Compressed textual data (`zTXt`) chunk effectively allows you to store
        key-value string pairs in the PNG container, compressing the "value" part
        (which can be quite lengthy) with zlib compression.
        
        The `zTXt` and `tEXt` chunks are semantically equivalent, but the `zTXt`
        chunk is recommended for storing large blocks of text.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11zTXt
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.CompressedTextChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.keyword = (self._io.read_bytes_term(0, False, True, True)).decode(u"ISO-8859-1")
            self.compression_method = KaitaiStream.resolve_enum(Png.CompressionMethods, self._io.read_u1())
            if not self.compression_method == Png.CompressionMethods.zlib:
                raise kaitaistruct.ValidationNotEqualError(Png.CompressionMethods.zlib, self.compression_method, self._io, u"/types/compressed_text_chunk/seq/1")
            self._raw__raw_text = self._io.read_bytes_full()
            self._raw_text = zlib.decompress(self._raw__raw_text)
            _io__raw_text = KaitaiStream(BytesIO(self._raw_text))
            self.text = Png.CompressedText(_io__raw_text, self, self._root)


        def _fetch_instances(self):
            pass
            self.text._fetch_instances()


    class EvernoteSkmfChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://web.archive.org/web/20210302212148/https://discussion.evernote.com/forums/topic/88532-how-to-extract-annotation-information-from-annotated-evernoteskitch-images/#comment-451501
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.EvernoteSkmfChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.json = (self._io.read_bytes_full()).decode(u"UTF-8")


        def _fetch_instances(self):
            pass


    class EvernoteSkrfChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://web.archive.org/web/20210302212148/https://discussion.evernote.com/forums/topic/88532-how-to-extract-annotation-information-from-annotated-evernoteskitch-images/#comment-451501
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.EvernoteSkrfChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.uuid = self._io.read_bytes(16)
            self.orig_img = self._io.read_bytes_full()


        def _fetch_instances(self):
            pass


    class ExifChunk(KaitaiStruct):
        """Exchangeable Image File (Exif) Profile (`eXIf`) chunk.
        
        Only one `eXIf` chunk is allowed in a PNG datastream.
        
        The `eXIf` chunk contains metadata concerning the original image data. If
        the image has been edited subsequent to creation of the Exif profile, this
        data might no longer apply to the PNG image data.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#eXIf
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.ExifChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.exif = exif.Exif(self._io)


        def _fetch_instances(self):
            pass
            self.exif._fetch_instances()


    class FrameControlChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#fcTL-chunk
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.FrameControlChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.sequence_number = self._io.read_u4be()
            self.width = self._io.read_u4be()
            if not self.width >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.width, self._io, u"/types/frame_control_chunk/seq/1")
            if not self.width <= self._root.ihdr.width:
                raise kaitaistruct.ValidationGreaterThanError(self._root.ihdr.width, self.width, self._io, u"/types/frame_control_chunk/seq/1")
            self.height = self._io.read_u4be()
            if not self.height >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.height, self._io, u"/types/frame_control_chunk/seq/2")
            if not self.height <= self._root.ihdr.height:
                raise kaitaistruct.ValidationGreaterThanError(self._root.ihdr.height, self.height, self._io, u"/types/frame_control_chunk/seq/2")
            self.x_offset = self._io.read_u4be()
            if not self.x_offset <= self._root.ihdr.width - self.width:
                raise kaitaistruct.ValidationGreaterThanError(self._root.ihdr.width - self.width, self.x_offset, self._io, u"/types/frame_control_chunk/seq/3")
            self.y_offset = self._io.read_u4be()
            if not self.y_offset <= self._root.ihdr.height - self.height:
                raise kaitaistruct.ValidationGreaterThanError(self._root.ihdr.height - self.height, self.y_offset, self._io, u"/types/frame_control_chunk/seq/4")
            self.delay_num = self._io.read_u2be()
            self.delay_den = self._io.read_u2be()
            self.dispose_op = KaitaiStream.resolve_enum(Png.DisposeOpValues, self._io.read_u1())
            if not isinstance(self.dispose_op, Png.DisposeOpValues):
                raise kaitaistruct.ValidationNotInEnumError(self.dispose_op, self._io, u"/types/frame_control_chunk/seq/7")
            self.blend_op = KaitaiStream.resolve_enum(Png.BlendOpValues, self._io.read_u1())
            if not isinstance(self.blend_op, Png.BlendOpValues):
                raise kaitaistruct.ValidationNotInEnumError(self.blend_op, self._io, u"/types/frame_control_chunk/seq/8")


        def _fetch_instances(self):
            pass

        @property
        def delay(self):
            """Time to display this frame, in seconds."""
            if hasattr(self, '_m_delay'):
                return self._m_delay

            self._m_delay = self.delay_num / (100.0 if self.delay_den == 0 else self.delay_den)
            return getattr(self, '_m_delay', None)


    class FrameDataChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#fdAT-chunk
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.FrameDataChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.sequence_number = self._io.read_u4be()
            self.frame_data = self._io.read_bytes_full()


        def _fetch_instances(self):
            pass


    class GamaChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#11gAMA
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.GamaChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.gamma_int = self._io.read_u4be()
            _ = self.gamma_int
            if not _ != 0:
                raise kaitaistruct.ValidationExprError(self.gamma_int, self._io, u"/types/gama_chunk/seq/0")


        def _fetch_instances(self):
            pass

        @property
        def gamma(self):
            """Image gamma, typically 0.45455 = 1/2.2."""
            if hasattr(self, '_m_gamma'):
                return self._m_gamma

            self._m_gamma = self.gamma_int / 100000.0
            return getattr(self, '_m_gamma', None)

        @property
        def inv_gamma(self):
            """Inverse of the image gamma (1 / gamma), typically 2.2 (not considering
            rounding)
            """
            if hasattr(self, '_m_inv_gamma'):
                return self._m_inv_gamma

            self._m_inv_gamma = 100000.0 / self.gamma_int
            return getattr(self, '_m_inv_gamma', None)


    class HistChunk(KaitaiStruct):
        """Image histogram (`hIST`) chunk gives the approximate usage frequency of
        each color in the palette. A histogram chunk can appear only when a `PLTE`
        chunk appears.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11hIST
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.HistChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.usage_freqs = []
            i = 0
            while not self._io.is_eof():
                self.usage_freqs.append(self._io.read_u2be())
                i += 1



        def _fetch_instances(self):
            pass
            for i in range(len(self.usage_freqs)):
                pass



    class IccpChunk(KaitaiStruct):
        """Embedded ICC profile (`iCCP`) chunk.
        
        If the `iCCP` chunk is present, the image samples conform to the color
        space represented by the embedded ICC profile as defined by the
        International Color Consortium.
        
        This chunk is ignored unless it is the [highest-precedence color
        chunk](https://www.w3.org/TR/png/#color-chunk-precendence) understood by
        the decoder. Unless a `cICP` chunk exists, a PNG datastream should contain
        at most one embedded profile, whether specified explicitly with an `iCCP`
        or implicitly with an `sRGB` chunk.
        
        It is recommended that the `sRGB` and `iCCP` chunks do not appear
        simultaneously in a PNG datastream.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11iCCP
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.IccpChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.profile_name = (self._io.read_bytes_term(0, False, True, True)).decode(u"ISO-8859-1")
            self.compression_method = KaitaiStream.resolve_enum(Png.CompressionMethods, self._io.read_u1())
            if not self.compression_method == Png.CompressionMethods.zlib:
                raise kaitaistruct.ValidationNotEqualError(Png.CompressionMethods.zlib, self.compression_method, self._io, u"/types/iccp_chunk/seq/1")
            self._raw__raw_profile = self._io.read_bytes_full()
            self._raw_profile = zlib.decompress(self._raw__raw_profile)
            _io__raw_profile = KaitaiStream(BytesIO(self._raw_profile))
            self.profile = icc_4.Icc4(_io__raw_profile)


        def _fetch_instances(self):
            pass
            self.profile._fetch_instances()


    class IhdrChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#11IHDR
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.IhdrChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.width = self._io.read_u4be()
            if not self.width >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.width, self._io, u"/types/ihdr_chunk/seq/0")
            self.height = self._io.read_u4be()
            if not self.height >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.height, self._io, u"/types/ihdr_chunk/seq/1")
            self.bit_depth = self._io.read_u1()
            if not  ((self.bit_depth == 1) or (self.bit_depth == 2) or (self.bit_depth == 4) or (self.bit_depth == 8) or (self.bit_depth == 16)) :
                raise kaitaistruct.ValidationNotAnyOfError(self.bit_depth, self._io, u"/types/ihdr_chunk/seq/2")
            self.color_type = KaitaiStream.resolve_enum(Png.ColorType, self._io.read_u1())
            if not isinstance(self.color_type, Png.ColorType):
                raise kaitaistruct.ValidationNotInEnumError(self.color_type, self._io, u"/types/ihdr_chunk/seq/3")
            self.compression_method = KaitaiStream.resolve_enum(Png.CompressionMethods, self._io.read_u1())
            if not isinstance(self.compression_method, Png.CompressionMethods):
                raise kaitaistruct.ValidationNotInEnumError(self.compression_method, self._io, u"/types/ihdr_chunk/seq/4")
            self.filter_method = KaitaiStream.resolve_enum(Png.FilterMethod, self._io.read_u1())
            if not isinstance(self.filter_method, Png.FilterMethod):
                raise kaitaistruct.ValidationNotInEnumError(self.filter_method, self._io, u"/types/ihdr_chunk/seq/5")
            self.interlace_method = KaitaiStream.resolve_enum(Png.InterlaceMethod, self._io.read_u1())
            if not isinstance(self.interlace_method, Png.InterlaceMethod):
                raise kaitaistruct.ValidationNotInEnumError(self.interlace_method, self._io, u"/types/ihdr_chunk/seq/6")


        def _fetch_instances(self):
            pass


    class InternationalText(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.InternationalText, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.value = (self._io.read_bytes_full()).decode(u"UTF-8")


        def _fetch_instances(self):
            pass


    class InternationalTextChunk(KaitaiStruct):
        """International textual data (`iTXt`) chunk effectively allows you to store
        key-value string pairs in the PNG container.
        
        The "key" part (`keyword`) is restricted to printable ISO-8859-1 (Latin-1)
        characters and spaces. The translated keyword and the "value" part
        (`text`) are stored in UTF-8 and thus can store text in any language -
        this language can be indicated via the language tag (`language_tag`).
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11iTXt
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.InternationalTextChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.keyword = (self._io.read_bytes_term(0, False, True, True)).decode(u"ISO-8859-1")
            self.compression_flag = self._io.read_u1()
            if not  ((self.compression_flag == 0) or (self.compression_flag == 1)) :
                raise kaitaistruct.ValidationNotAnyOfError(self.compression_flag, self._io, u"/types/international_text_chunk/seq/1")
            self.compression_method = KaitaiStream.resolve_enum(Png.CompressionMethods, self._io.read_u1())
            if not self.compression_method == (Png.CompressionMethods.zlib if self.compression_flag == 1 else self.compression_method):
                raise kaitaistruct.ValidationNotEqualError((Png.CompressionMethods.zlib if self.compression_flag == 1 else self.compression_method), self.compression_method, self._io, u"/types/international_text_chunk/seq/2")
            self.language_tag = (self._io.read_bytes_term(0, False, True, True)).decode(u"ASCII")
            self.translated_keyword = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
            if self.compression_flag == 0:
                pass
                self._raw_text_plain = self._io.read_bytes_full()
                _io__raw_text_plain = KaitaiStream(BytesIO(self._raw_text_plain))
                self.text_plain = Png.InternationalText(_io__raw_text_plain, self, self._root)

            if self.compression_flag == 1:
                pass
                self._raw__raw_text_zlib = self._io.read_bytes_full()
                self._raw_text_zlib = zlib.decompress(self._raw__raw_text_zlib)
                _io__raw_text_zlib = KaitaiStream(BytesIO(self._raw_text_zlib))
                self.text_zlib = Png.InternationalText(_io__raw_text_zlib, self, self._root)



        def _fetch_instances(self):
            pass
            if self.compression_flag == 0:
                pass
                self.text_plain._fetch_instances()

            if self.compression_flag == 1:
                pass
                self.text_zlib._fetch_instances()


        @property
        def text(self):
            """Text string (the "value" of this key-value pair), written in language
            specified in `language_tag`.
            
            Although it is not null-terminated (unlike other textual data in the
            `iTXt` chunk), it must not contain a zero byte
            (U+0000 NULL character). A newline should be represented by a single
            U+000A LINE FEED (LF) character (aka `\n`). The remaining control
            characters (U+0001..U+0009, U+000B..0+001F, U+007F..U+009F) are
            discouraged.
            """
            if hasattr(self, '_m_text'):
                return self._m_text

            self._m_text = (self.text_plain if self.compression_flag == 0 else self.text_zlib).value
            return getattr(self, '_m_text', None)


    class MdcvChromaticity(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.MdcvChromaticity, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.x_int = self._io.read_u2be()
            self.y_int = self._io.read_u2be()


        def _fetch_instances(self):
            pass

        @property
        def x(self):
            if hasattr(self, '_m_x'):
                return self._m_x

            self._m_x = self.x_int * 0.00002
            return getattr(self, '_m_x', None)

        @property
        def y(self):
            if hasattr(self, '_m_y'):
                return self._m_y

            self._m_y = self.y_int * 0.00002
            return getattr(self, '_m_y', None)


    class MdcvChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#mDCV-chunk
        
        
        .. seealso::
           Source - https://w3c.github.io/png/Implementation_Report_3e/#mastering
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.MdcvChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.red = Png.MdcvChromaticity(self._io, self, self._root)
            self.green = Png.MdcvChromaticity(self._io, self, self._root)
            self.blue = Png.MdcvChromaticity(self._io, self, self._root)
            self.white_point = Png.MdcvChromaticity(self._io, self, self._root)
            self.max_luminance_int = self._io.read_u4be()
            self.min_luminance_int = self._io.read_u4be()


        def _fetch_instances(self):
            pass
            self.red._fetch_instances()
            self.green._fetch_instances()
            self.blue._fetch_instances()
            self.white_point._fetch_instances()

        @property
        def max_luminance(self):
            """Maximum luminance in cd/m^2."""
            if hasattr(self, '_m_max_luminance'):
                return self._m_max_luminance

            self._m_max_luminance = self.max_luminance_int * 0.0001
            return getattr(self, '_m_max_luminance', None)

        @property
        def min_luminance(self):
            """Minimum luminance in cd/m^2."""
            if hasattr(self, '_m_min_luminance'):
                return self._m_min_luminance

            self._m_min_luminance = self.min_luminance_int * 0.0001
            return getattr(self, '_m_min_luminance', None)


    class PhysChunk(KaitaiStruct):
        """Physical pixel dimensions (`pHYs`) chunk specifies the intended physical
        size of the pixels (in meters) or pixel aspect ratio for display of the
        image.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11pHYs
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.PhysChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.pixels_per_unit_x = self._io.read_u4be()
            self.pixels_per_unit_y = self._io.read_u4be()
            self.unit = KaitaiStream.resolve_enum(Png.PhysUnit, self._io.read_u1())
            if not isinstance(self.unit, Png.PhysUnit):
                raise kaitaistruct.ValidationNotInEnumError(self.unit, self._io, u"/types/phys_chunk/seq/2")


        def _fetch_instances(self):
            pass

        @property
        def dots_per_inch_x(self):
            """Horizontal resolution (DPI)."""
            if hasattr(self, '_m_dots_per_inch_x'):
                return self._m_dots_per_inch_x

            if self.unit == Png.PhysUnit.meter:
                pass
                self._m_dots_per_inch_x = self.pixels_per_unit_x * 0.0254

            return getattr(self, '_m_dots_per_inch_x', None)

        @property
        def dots_per_inch_y(self):
            """Vertical resolution (DPI)."""
            if hasattr(self, '_m_dots_per_inch_y'):
                return self._m_dots_per_inch_y

            if self.unit == Png.PhysUnit.meter:
                pass
                self._m_dots_per_inch_y = self.pixels_per_unit_y * 0.0254

            return getattr(self, '_m_dots_per_inch_y', None)


    class PlteChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#11PLTE
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.PlteChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.entries = []
            i = 0
            while not self._io.is_eof():
                self.entries.append(Png.Rgb(self._io, self, self._root))
                i += 1



        def _fetch_instances(self):
            pass
            for i in range(len(self.entries)):
                pass
                self.entries[i]._fetch_instances()



    class Rgb(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.Rgb, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.r = self._io.read_u1()
            self.g = self._io.read_u1()
            self.b = self._io.read_u1()


        def _fetch_instances(self):
            pass


    class SbitChunk(KaitaiStruct):
        """Significant bits (`sBIT`) chunk stores the original number of significant
        bits of the sample values (which can be less than or equal to the sample
        depth). This allows PNG decoders to recover the original data losslessly
        even if the data had a sample depth not directly supported by PNG.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11sBIT
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.SbitChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.significant_bits = Png.SbitGreyscale(False, self._io, self, self._root)
            elif _on == Png.ColorType.greyscale_alpha:
                pass
                self.significant_bits = Png.SbitGreyscale(True, self._io, self, self._root)
            elif _on == Png.ColorType.indexed:
                pass
                self.significant_bits = Png.SbitTruecolor(False, self._io, self, self._root)
            elif _on == Png.ColorType.truecolor:
                pass
                self.significant_bits = Png.SbitTruecolor(False, self._io, self, self._root)
            elif _on == Png.ColorType.truecolor_alpha:
                pass
                self.significant_bits = Png.SbitTruecolor(True, self._io, self, self._root)


        def _fetch_instances(self):
            pass
            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.significant_bits._fetch_instances()
            elif _on == Png.ColorType.greyscale_alpha:
                pass
                self.significant_bits._fetch_instances()
            elif _on == Png.ColorType.indexed:
                pass
                self.significant_bits._fetch_instances()
            elif _on == Png.ColorType.truecolor:
                pass
                self.significant_bits._fetch_instances()
            elif _on == Png.ColorType.truecolor_alpha:
                pass
                self.significant_bits._fetch_instances()

        @property
        def sample_depth(self):
            if hasattr(self, '_m_sample_depth'):
                return self._m_sample_depth

            self._m_sample_depth = (8 if self._root.ihdr.color_type == Png.ColorType.indexed else self._root.ihdr.bit_depth)
            return getattr(self, '_m_sample_depth', None)


    class SbitGreyscale(KaitaiStruct):
        def __init__(self, has_alpha, _io, _parent=None, _root=None):
            super(Png.SbitGreyscale, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self.has_alpha = has_alpha
            self._read()

        def _read(self):
            self.grey = self._io.read_u1()
            if not self.grey >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.grey, self._io, u"/types/sbit_greyscale/seq/0")
            if not self.grey <= self._parent.sample_depth:
                raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.grey, self._io, u"/types/sbit_greyscale/seq/0")
            if self.has_alpha:
                pass
                self.alpha = self._io.read_u1()
                if not self.alpha >= 1:
                    raise kaitaistruct.ValidationLessThanError(1, self.alpha, self._io, u"/types/sbit_greyscale/seq/1")
                if not self.alpha <= self._parent.sample_depth:
                    raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.alpha, self._io, u"/types/sbit_greyscale/seq/1")



        def _fetch_instances(self):
            pass
            if self.has_alpha:
                pass



    class SbitTruecolor(KaitaiStruct):
        def __init__(self, has_alpha, _io, _parent=None, _root=None):
            super(Png.SbitTruecolor, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self.has_alpha = has_alpha
            self._read()

        def _read(self):
            self.red = self._io.read_u1()
            if not self.red >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.red, self._io, u"/types/sbit_truecolor/seq/0")
            if not self.red <= self._parent.sample_depth:
                raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.red, self._io, u"/types/sbit_truecolor/seq/0")
            self.green = self._io.read_u1()
            if not self.green >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.green, self._io, u"/types/sbit_truecolor/seq/1")
            if not self.green <= self._parent.sample_depth:
                raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.green, self._io, u"/types/sbit_truecolor/seq/1")
            self.blue = self._io.read_u1()
            if not self.blue >= 1:
                raise kaitaistruct.ValidationLessThanError(1, self.blue, self._io, u"/types/sbit_truecolor/seq/2")
            if not self.blue <= self._parent.sample_depth:
                raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.blue, self._io, u"/types/sbit_truecolor/seq/2")
            if self.has_alpha:
                pass
                self.alpha = self._io.read_u1()
                if not self.alpha >= 1:
                    raise kaitaistruct.ValidationLessThanError(1, self.alpha, self._io, u"/types/sbit_truecolor/seq/3")
                if not self.alpha <= self._parent.sample_depth:
                    raise kaitaistruct.ValidationGreaterThanError(self._parent.sample_depth, self.alpha, self._io, u"/types/sbit_truecolor/seq/3")



        def _fetch_instances(self):
            pass
            if self.has_alpha:
                pass



    class SpltChunk(KaitaiStruct):
        """Suggested palette (`sPLT`) chunk.
        
        Multiple `sPLT` chunks are permitted, but each must have a different
        palette name.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11sPLT
        
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#12Suggested-palettes
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.SpltChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.palette_name = (self._io.read_bytes_term(0, False, True, True)).decode(u"ISO-8859-1")
            self.sample_depth = self._io.read_u1()
            if not  ((self.sample_depth == 8) or (self.sample_depth == 16)) :
                raise kaitaistruct.ValidationNotAnyOfError(self.sample_depth, self._io, u"/types/splt_chunk/seq/1")
            self.entries = []
            i = 0
            while not self._io.is_eof():
                self.entries.append(Png.SpltEntry(self._io, self, self._root))
                i += 1



        def _fetch_instances(self):
            pass
            for i in range(len(self.entries)):
                pass
                self.entries[i]._fetch_instances()



    class SpltEntry(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.SpltEntry, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            _on = self._parent.sample_depth
            if _on == 8:
                pass
                self.red = self._io.read_u1()
            else:
                pass
                self.red = self._io.read_u2be()
            _on = self._parent.sample_depth
            if _on == 8:
                pass
                self.green = self._io.read_u1()
            else:
                pass
                self.green = self._io.read_u2be()
            _on = self._parent.sample_depth
            if _on == 8:
                pass
                self.blue = self._io.read_u1()
            else:
                pass
                self.blue = self._io.read_u2be()
            _on = self._parent.sample_depth
            if _on == 8:
                pass
                self.alpha = self._io.read_u1()
            else:
                pass
                self.alpha = self._io.read_u2be()
            self.freq = self._io.read_u2be()


        def _fetch_instances(self):
            pass
            _on = self._parent.sample_depth
            if _on == 8:
                pass
            else:
                pass
            _on = self._parent.sample_depth
            if _on == 8:
                pass
            else:
                pass
            _on = self._parent.sample_depth
            if _on == 8:
                pass
            else:
                pass
            _on = self._parent.sample_depth
            if _on == 8:
                pass
            else:
                pass


    class SrgbChunk(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.w3.org/TR/png/#11sRGB
        """

        class Intent(IntEnum):
            perceptual = 0
            relative_colorimetric = 1
            saturation = 2
            absolute_colorimetric = 3
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.SrgbChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.render_intent = KaitaiStream.resolve_enum(Png.SrgbChunk.Intent, self._io.read_u1())
            if not isinstance(self.render_intent, Png.SrgbChunk.Intent):
                raise kaitaistruct.ValidationNotInEnumError(self.render_intent, self._io, u"/types/srgb_chunk/seq/0")


        def _fetch_instances(self):
            pass


    class TextChunk(KaitaiStruct):
        """Textual data (`tEXt`) chunk effectively allows you to store key-value
        string pairs in the PNG container.
        
        Both the "key" (`keyword`) and "value" (`text`) parts are restricted to
        printable ISO-8859-1 (Latin-1) characters and ASCII spaces, with the
        exception that `text` can also contain newlines (U+000A LINE FEED (LF)
        characters) and U+00A0 NON-BREAKING SPACE characters.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11tEXt
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.TextChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.keyword = (self._io.read_bytes_term(0, False, True, True)).decode(u"ISO-8859-1")
            self.text = (self._io.read_bytes_full()).decode(u"ISO-8859-1")


        def _fetch_instances(self):
            pass


    class TimeChunk(KaitaiStruct):
        """Time chunk stores time stamp of last modification of this image,
        up to 1 second precision in UTC timezone.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11tIME
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.TimeChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.year = self._io.read_u2be()
            self.month = self._io.read_u1()
            self.day = self._io.read_u1()
            self.hour = self._io.read_u1()
            self.minute = self._io.read_u1()
            self.second = self._io.read_u1()


        def _fetch_instances(self):
            pass


    class TrnsChunk(KaitaiStruct):
        """Transparency (`tRNS`) chunk specifies either alpha values that are
        associated with palette entries (for indexed-color images) or a single
        transparent color (for greyscale and truecolor images).
        
        A `tRNS` chunk must not appear for color types
        `color_type::greyscale_alpha` = 4 and `color_type::truecolor_alpha` = 6,
        since a full alpha channel is already present in those cases.
        
        .. seealso::
           Source - https://www.w3.org/TR/png/#11tRNS
        """
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.TrnsChunk, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            if self._root.ihdr.color_type == Png.ColorType.indexed:
                pass
                self.palette_alphas = []
                i = 0
                while not self._io.is_eof():
                    self.palette_alphas.append(self._io.read_u1())
                    i += 1


            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.transparent_color = Png.TrnsGreyscaleColor(self._io, self, self._root)
            elif _on == Png.ColorType.truecolor:
                pass
                self.transparent_color = Png.TrnsTruecolorColor(self._io, self, self._root)


        def _fetch_instances(self):
            pass
            if self._root.ihdr.color_type == Png.ColorType.indexed:
                pass
                for i in range(len(self.palette_alphas)):
                    pass


            _on = self._root.ihdr.color_type
            if _on == Png.ColorType.greyscale:
                pass
                self.transparent_color._fetch_instances()
            elif _on == Png.ColorType.truecolor:
                pass
                self.transparent_color._fetch_instances()

        @property
        def sample_mask(self):
            if hasattr(self, '_m_sample_mask'):
                return self._m_sample_mask

            self._m_sample_mask = (1 << self._root.ihdr.bit_depth) - 1
            return getattr(self, '_m_sample_mask', None)


    class TrnsGreyscaleColor(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.TrnsGreyscaleColor, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.grey_raw = self._io.read_u2be()


        def _fetch_instances(self):
            pass

        @property
        def grey(self):
            if hasattr(self, '_m_grey'):
                return self._m_grey

            self._m_grey = self.grey_raw & self._parent.sample_mask
            return getattr(self, '_m_grey', None)


    class TrnsTruecolorColor(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            super(Png.TrnsTruecolorColor, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._read()

        def _read(self):
            self.red_raw = self._io.read_u2be()
            self.green_raw = self._io.read_u2be()
            self.blue_raw = self._io.read_u2be()


        def _fetch_instances(self):
            pass

        @property
        def blue(self):
            if hasattr(self, '_m_blue'):
                return self._m_blue

            self._m_blue = self.blue_raw & self._parent.sample_mask
            return getattr(self, '_m_blue', None)

        @property
        def green(self):
            if hasattr(self, '_m_green'):
                return self._m_green

            self._m_green = self.green_raw & self._parent.sample_mask
            return getattr(self, '_m_green', None)

        @property
        def red(self):
            if hasattr(self, '_m_red'):
                return self._m_red

            self._m_red = self.red_raw & self._parent.sample_mask
            return getattr(self, '_m_red', None)