PNG (Portable Network Graphics) file: C++11/STL parsing library

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 parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.png", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    png_t data(&ks);
    

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

data.magic() // => get magic

C++11/STL source code to parse PNG (Portable Network Graphics) file

png.h

#pragma once

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * Test files for APNG can be found at the following locations:
 * 
 *   * <https://philip.html5.org/tests/apng/tests.html>
 *   * <http://littlesvr.ca/apng/>
 */

class png_t : public kaitai::kstruct {

public:
    class rgb_t;
    class chunk_t;
    class bkgd_indexed_t;
    class point_t;
    class bkgd_greyscale_t;
    class chrm_chunk_t;
    class ihdr_chunk_t;
    class plte_chunk_t;
    class srgb_chunk_t;
    class compressed_text_chunk_t;
    class frame_data_chunk_t;
    class bkgd_truecolor_t;
    class gama_chunk_t;
    class bkgd_chunk_t;
    class phys_chunk_t;
    class frame_control_chunk_t;
    class international_text_chunk_t;
    class text_chunk_t;
    class animation_control_chunk_t;
    class time_chunk_t;

    enum phys_unit_t {
        PHYS_UNIT_UNKNOWN = 0,
        PHYS_UNIT_METER = 1
    };

    enum blend_op_values_t {
        BLEND_OP_VALUES_SOURCE = 0,
        BLEND_OP_VALUES_OVER = 1
    };

    enum compression_methods_t {
        COMPRESSION_METHODS_ZLIB = 0
    };

    enum dispose_op_values_t {
        DISPOSE_OP_VALUES_NONE = 0,
        DISPOSE_OP_VALUES_BACKGROUND = 1,
        DISPOSE_OP_VALUES_PREVIOUS = 2
    };

    enum color_type_t {
        COLOR_TYPE_GREYSCALE = 0,
        COLOR_TYPE_TRUECOLOR = 2,
        COLOR_TYPE_INDEXED = 3,
        COLOR_TYPE_GREYSCALE_ALPHA = 4,
        COLOR_TYPE_TRUECOLOR_ALPHA = 6
    };

    png_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, png_t* p__root = nullptr);

private:
    void _read();
    void _clean_up();

public:
    ~png_t();

    class rgb_t : public kaitai::kstruct {

    public:

        rgb_t(kaitai::kstream* p__io, png_t::plte_chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~rgb_t();

    private:
        uint8_t m_r;
        uint8_t m_g;
        uint8_t m_b;
        png_t* m__root;
        png_t::plte_chunk_t* m__parent;

    public:
        uint8_t r() const { return m_r; }
        uint8_t g() const { return m_g; }
        uint8_t b() const { return m_b; }
        png_t* _root() const { return m__root; }
        png_t::plte_chunk_t* _parent() const { return m__parent; }
    };

    class chunk_t : public kaitai::kstruct {

    public:

        chunk_t(kaitai::kstream* p__io, png_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~chunk_t();

    private:
        uint32_t m_len;
        std::string m_type;
        std::unique_ptr<kaitai::kstruct> m_body;
        bool n_body;

    public:
        bool _is_null_body() { body(); return n_body; };

    private:
        std::string m_crc;
        png_t* m__root;
        png_t* m__parent;
        std::string m__raw_body;
        std::unique_ptr<kaitai::kstream> m__io__raw_body;

    public:
        uint32_t len() const { return m_len; }
        std::string type() const { return m_type; }
        kaitai::kstruct* body() const { return m_body.get(); }
        std::string crc() const { return m_crc; }
        png_t* _root() const { return m__root; }
        png_t* _parent() const { return m__parent; }
        std::string _raw_body() const { return m__raw_body; }
        kaitai::kstream* _io__raw_body() const { return m__io__raw_body.get(); }
    };

    /**
     * Background chunk for images with indexed palette.
     */

    class bkgd_indexed_t : public kaitai::kstruct {

    public:

        bkgd_indexed_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_indexed_t();

    private:
        uint8_t m_palette_index;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint8_t palette_index() const { return m_palette_index; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    class point_t : public kaitai::kstruct {

    public:

        point_t(kaitai::kstream* p__io, png_t::chrm_chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~point_t();

    private:
        bool f_x;
        double m_x;

    public:
        double x();

    private:
        bool f_y;
        double m_y;

    public:
        double y();

    private:
        uint32_t m_x_int;
        uint32_t m_y_int;
        png_t* m__root;
        png_t::chrm_chunk_t* m__parent;

    public:
        uint32_t x_int() const { return m_x_int; }
        uint32_t y_int() const { return m_y_int; }
        png_t* _root() const { return m__root; }
        png_t::chrm_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk for greyscale images.
     */

    class bkgd_greyscale_t : public kaitai::kstruct {

    public:

        bkgd_greyscale_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_greyscale_t();

    private:
        uint16_t m_value;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint16_t value() const { return m_value; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11cHRM Source
     */

    class chrm_chunk_t : public kaitai::kstruct {

    public:

        chrm_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~chrm_chunk_t();

    private:
        std::unique_ptr<point_t> m_white_point;
        std::unique_ptr<point_t> m_red;
        std::unique_ptr<point_t> m_green;
        std::unique_ptr<point_t> m_blue;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        point_t* white_point() const { return m_white_point.get(); }
        point_t* red() const { return m_red.get(); }
        point_t* green() const { return m_green.get(); }
        point_t* blue() const { return m_blue.get(); }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11IHDR Source
     */

    class ihdr_chunk_t : public kaitai::kstruct {

    public:

        ihdr_chunk_t(kaitai::kstream* p__io, png_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~ihdr_chunk_t();

    private:
        uint32_t m_width;
        uint32_t m_height;
        uint8_t m_bit_depth;
        color_type_t m_color_type;
        uint8_t m_compression_method;
        uint8_t m_filter_method;
        uint8_t m_interlace_method;
        png_t* m__root;
        png_t* m__parent;

    public:
        uint32_t width() const { return m_width; }
        uint32_t height() const { return m_height; }
        uint8_t bit_depth() const { return m_bit_depth; }
        color_type_t color_type() const { return m_color_type; }
        uint8_t compression_method() const { return m_compression_method; }
        uint8_t filter_method() const { return m_filter_method; }
        uint8_t interlace_method() const { return m_interlace_method; }
        png_t* _root() const { return m__root; }
        png_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11PLTE Source
     */

    class plte_chunk_t : public kaitai::kstruct {

    public:

        plte_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~plte_chunk_t();

    private:
        std::unique_ptr<std::vector<std::unique_ptr<rgb_t>>> m_entries;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        std::vector<std::unique_ptr<rgb_t>>* entries() const { return m_entries.get(); }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11sRGB Source
     */

    class srgb_chunk_t : public kaitai::kstruct {

    public:

        enum intent_t {
            INTENT_PERCEPTUAL = 0,
            INTENT_RELATIVE_COLORIMETRIC = 1,
            INTENT_SATURATION = 2,
            INTENT_ABSOLUTE_COLORIMETRIC = 3
        };

        srgb_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~srgb_chunk_t();

    private:
        intent_t m_render_intent;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        intent_t render_intent() const { return m_render_intent; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Compressed text chunk effectively allows to store key-value
     * string pairs in PNG container, compressing "value" part (which
     * can be quite lengthy) with zlib compression.
     * \sa https://www.w3.org/TR/png/#11zTXt Source
     */

    class compressed_text_chunk_t : public kaitai::kstruct {

    public:

        compressed_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~compressed_text_chunk_t();

    private:
        std::string m_keyword;
        compression_methods_t m_compression_method;
        std::string m_text_datastream;
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_text_datastream;
        std::unique_ptr<kaitai::kstream> m__io_text_datastream;

    public:

        /**
         * Indicates purpose of the following text data.
         */
        std::string keyword() const { return m_keyword; }
        compression_methods_t compression_method() const { return m_compression_method; }
        std::string text_datastream() const { return m_text_datastream; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_text_datastream() const { return m__raw_text_datastream; }
        kaitai::kstream* _io_text_datastream() const { return m__io_text_datastream.get(); }
    };

    /**
     * \sa https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk Source
     */

    class frame_data_chunk_t : public kaitai::kstruct {

    public:

        frame_data_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~frame_data_chunk_t();

    private:
        uint32_t m_sequence_number;
        std::string m_frame_data;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Sequence number of the animation chunk. The fcTL and fdAT chunks
         * have a 4 byte sequence number. Both chunk types share the sequence.
         * The first fcTL chunk must contain sequence number 0, and the sequence
         * numbers in the remaining fcTL and fdAT chunks must be in order, with
         * no gaps or duplicates.
         */
        uint32_t sequence_number() const { return m_sequence_number; }

        /**
         * Frame data for the frame. At least one fdAT chunk is required for
         * each frame. The compressed datastream is the concatenation of the
         * contents of the data fields of all the fdAT chunks within a frame.
         */
        std::string frame_data() const { return m_frame_data; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk for truecolor images.
     */

    class bkgd_truecolor_t : public kaitai::kstruct {

    public:

        bkgd_truecolor_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_truecolor_t();

    private:
        uint16_t m_red;
        uint16_t m_green;
        uint16_t m_blue;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint16_t red() const { return m_red; }
        uint16_t green() const { return m_green; }
        uint16_t blue() const { return m_blue; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11gAMA Source
     */

    class gama_chunk_t : public kaitai::kstruct {

    public:

        gama_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~gama_chunk_t();

    private:
        bool f_gamma_ratio;
        double m_gamma_ratio;

    public:
        double gamma_ratio();

    private:
        uint32_t m_gamma_int;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        uint32_t gamma_int() const { return m_gamma_int; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk stores default background color to display this
     * image against. Contents depend on `color_type` of the image.
     * \sa https://www.w3.org/TR/png/#11bKGD Source
     */

    class bkgd_chunk_t : public kaitai::kstruct {

    public:

        bkgd_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_chunk_t();

    private:
        std::unique_ptr<kaitai::kstruct> m_bkgd;
        bool n_bkgd;

    public:
        bool _is_null_bkgd() { bkgd(); return n_bkgd; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        kaitai::kstruct* bkgd() const { return m_bkgd.get(); }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * "Physical size" chunk stores data that allows to translate
     * logical pixels into physical units (meters, etc) and vice-versa.
     * \sa https://www.w3.org/TR/png/#11pHYs Source
     */

    class phys_chunk_t : public kaitai::kstruct {

    public:

        phys_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~phys_chunk_t();

    private:
        uint32_t m_pixels_per_unit_x;
        uint32_t m_pixels_per_unit_y;
        phys_unit_t m_unit;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Number of pixels per physical unit (typically, 1 meter) by X
         * axis.
         */
        uint32_t pixels_per_unit_x() const { return m_pixels_per_unit_x; }

        /**
         * Number of pixels per physical unit (typically, 1 meter) by Y
         * axis.
         */
        uint32_t pixels_per_unit_y() const { return m_pixels_per_unit_y; }
        phys_unit_t unit() const { return m_unit; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk Source
     */

    class frame_control_chunk_t : public kaitai::kstruct {

    public:

        frame_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~frame_control_chunk_t();

    private:
        bool f_delay;
        double m_delay;

    public:

        /**
         * Time to display this frame, in seconds
         */
        double delay();

    private:
        uint32_t m_sequence_number;
        uint32_t m_width;
        uint32_t m_height;
        uint32_t m_x_offset;
        uint32_t m_y_offset;
        uint16_t m_delay_num;
        uint16_t m_delay_den;
        dispose_op_values_t m_dispose_op;
        blend_op_values_t m_blend_op;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Sequence number of the animation chunk
         */
        uint32_t sequence_number() const { return m_sequence_number; }

        /**
         * Width of the following frame
         */
        uint32_t width() const { return m_width; }

        /**
         * Height of the following frame
         */
        uint32_t height() const { return m_height; }

        /**
         * X position at which to render the following frame
         */
        uint32_t x_offset() const { return m_x_offset; }

        /**
         * Y position at which to render the following frame
         */
        uint32_t y_offset() const { return m_y_offset; }

        /**
         * Frame delay fraction numerator
         */
        uint16_t delay_num() const { return m_delay_num; }

        /**
         * Frame delay fraction denominator
         */
        uint16_t delay_den() const { return m_delay_den; }

        /**
         * Type of frame area disposal to be done after rendering this frame
         */
        dispose_op_values_t dispose_op() const { return m_dispose_op; }

        /**
         * Type of frame area rendering for this frame
         */
        blend_op_values_t blend_op() const { return m_blend_op; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * International text chunk effectively allows to store key-value string pairs in
     * PNG container. Both "key" (keyword) and "value" (text) parts are
     * given in pre-defined subset of iso8859-1 without control
     * characters.
     * \sa https://www.w3.org/TR/png/#11iTXt Source
     */

    class international_text_chunk_t : public kaitai::kstruct {

    public:

        international_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~international_text_chunk_t();

    private:
        std::string m_keyword;
        uint8_t m_compression_flag;
        compression_methods_t m_compression_method;
        std::string m_language_tag;
        std::string m_translated_keyword;
        std::string m_text;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Indicates purpose of the following text data.
         */
        std::string keyword() const { return m_keyword; }

        /**
         * 0 = text is uncompressed, 1 = text is compressed with a
         * method specified in `compression_method`.
         */
        uint8_t compression_flag() const { return m_compression_flag; }
        compression_methods_t compression_method() const { return m_compression_method; }

        /**
         * Human language used in `translated_keyword` and `text`
         * attributes - should be a language code conforming to ISO
         * 646.IRV:1991.
         */
        std::string language_tag() const { return m_language_tag; }

        /**
         * Keyword translated into language specified in
         * `language_tag`. Line breaks are not allowed.
         */
        std::string translated_keyword() const { return m_translated_keyword; }

        /**
         * Text contents ("value" of this key-value pair), written in
         * language specified in `language_tag`. Line breaks are
         * allowed.
         */
        std::string text() const { return m_text; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Text chunk effectively allows to store key-value string pairs in
     * PNG container. Both "key" (keyword) and "value" (text) parts are
     * given in pre-defined subset of iso8859-1 without control
     * characters.
     * \sa https://www.w3.org/TR/png/#11tEXt Source
     */

    class text_chunk_t : public kaitai::kstruct {

    public:

        text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~text_chunk_t();

    private:
        std::string m_keyword;
        std::string m_text;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Indicates purpose of the following text data.
         */
        std::string keyword() const { return m_keyword; }
        std::string text() const { return m_text; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk Source
     */

    class animation_control_chunk_t : public kaitai::kstruct {

    public:

        animation_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~animation_control_chunk_t();

    private:
        uint32_t m_num_frames;
        uint32_t m_num_plays;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Number of frames, must be equal to the number of `frame_control_chunk`s
         */
        uint32_t num_frames() const { return m_num_frames; }

        /**
         * Number of times to loop, 0 indicates infinite looping.
         */
        uint32_t num_plays() const { return m_num_plays; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Time chunk stores time stamp of last modification of this image,
     * up to 1 second precision in UTC timezone.
     * \sa https://www.w3.org/TR/png/#11tIME Source
     */

    class time_chunk_t : public kaitai::kstruct {

    public:

        time_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = nullptr, png_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~time_chunk_t();

    private:
        uint16_t m_year;
        uint8_t m_month;
        uint8_t m_day;
        uint8_t m_hour;
        uint8_t m_minute;
        uint8_t m_second;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        uint16_t year() const { return m_year; }
        uint8_t month() const { return m_month; }
        uint8_t day() const { return m_day; }
        uint8_t hour() const { return m_hour; }
        uint8_t minute() const { return m_minute; }
        uint8_t second() const { return m_second; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

private:
    std::string m_magic;
    uint32_t m_ihdr_len;
    std::string m_ihdr_type;
    std::unique_ptr<ihdr_chunk_t> m_ihdr;
    std::string m_ihdr_crc;
    std::unique_ptr<std::vector<std::unique_ptr<chunk_t>>> m_chunks;
    png_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string magic() const { return m_magic; }
    uint32_t ihdr_len() const { return m_ihdr_len; }
    std::string ihdr_type() const { return m_ihdr_type; }
    ihdr_chunk_t* ihdr() const { return m_ihdr.get(); }
    std::string ihdr_crc() const { return m_ihdr_crc; }
    std::vector<std::unique_ptr<chunk_t>>* chunks() const { return m_chunks.get(); }
    png_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

png.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "png.h"
#include "kaitai/exceptions.h"

png_t::png_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_ihdr = nullptr;
    m_chunks = nullptr;
    _read();
}

void png_t::_read() {
    m_magic = m__io->read_bytes(8);
    if (!(magic() == std::string("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8), magic(), _io(), std::string("/seq/0"));
    }
    m_ihdr_len = m__io->read_u4be();
    if (!(ihdr_len() == 13)) {
        throw kaitai::validation_not_equal_error<uint32_t>(13, ihdr_len(), _io(), std::string("/seq/1"));
    }
    m_ihdr_type = m__io->read_bytes(4);
    if (!(ihdr_type() == std::string("\x49\x48\x44\x52", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x49\x48\x44\x52", 4), ihdr_type(), _io(), std::string("/seq/2"));
    }
    m_ihdr = std::unique_ptr<ihdr_chunk_t>(new ihdr_chunk_t(m__io, this, m__root));
    m_ihdr_crc = m__io->read_bytes(4);
    m_chunks = std::unique_ptr<std::vector<std::unique_ptr<chunk_t>>>(new std::vector<std::unique_ptr<chunk_t>>());
    {
        int i = 0;
        chunk_t* _;
        do {
            _ = new chunk_t(m__io, this, m__root);
            m_chunks->push_back(std::move(std::unique_ptr<chunk_t>(_)));
            i++;
        } while (!( ((_->type() == (std::string("IEND"))) || (_io()->is_eof())) ));
    }
}

png_t::~png_t() {
    _clean_up();
}

void png_t::_clean_up() {
}

png_t::rgb_t::rgb_t(kaitai::kstream* p__io, png_t::plte_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::rgb_t::_read() {
    m_r = m__io->read_u1();
    m_g = m__io->read_u1();
    m_b = m__io->read_u1();
}

png_t::rgb_t::~rgb_t() {
    _clean_up();
}

void png_t::rgb_t::_clean_up() {
}

png_t::chunk_t::chunk_t(kaitai::kstream* p__io, png_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__io__raw_body = nullptr;
    _read();
}

void png_t::chunk_t::_read() {
    m_len = m__io->read_u4be();
    m_type = kaitai::kstream::bytes_to_str(m__io->read_bytes(4), std::string("UTF-8"));
    n_body = true;
    {
        std::string on = type();
        if (on == std::string("iTXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<international_text_chunk_t>(new international_text_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("gAMA")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<gama_chunk_t>(new gama_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("tIME")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<time_chunk_t>(new time_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("PLTE")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<plte_chunk_t>(new plte_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("bKGD")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<bkgd_chunk_t>(new bkgd_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("pHYs")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<phys_chunk_t>(new phys_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("fdAT")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<frame_data_chunk_t>(new frame_data_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("tEXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<text_chunk_t>(new text_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("cHRM")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<chrm_chunk_t>(new chrm_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("acTL")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<animation_control_chunk_t>(new animation_control_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("sRGB")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<srgb_chunk_t>(new srgb_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("zTXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<compressed_text_chunk_t>(new compressed_text_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else if (on == std::string("fcTL")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_body));
            m_body = std::unique_ptr<frame_control_chunk_t>(new frame_control_chunk_t(m__io__raw_body.get(), this, m__root));
        }
        else {
            m__raw_body = m__io->read_bytes(len());
        }
    }
    m_crc = m__io->read_bytes(4);
}

png_t::chunk_t::~chunk_t() {
    _clean_up();
}

void png_t::chunk_t::_clean_up() {
    if (!n_body) {
    }
}

png_t::bkgd_indexed_t::bkgd_indexed_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::bkgd_indexed_t::_read() {
    m_palette_index = m__io->read_u1();
}

png_t::bkgd_indexed_t::~bkgd_indexed_t() {
    _clean_up();
}

void png_t::bkgd_indexed_t::_clean_up() {
}

png_t::point_t::point_t(kaitai::kstream* p__io, png_t::chrm_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_x = false;
    f_y = false;
    _read();
}

void png_t::point_t::_read() {
    m_x_int = m__io->read_u4be();
    m_y_int = m__io->read_u4be();
}

png_t::point_t::~point_t() {
    _clean_up();
}

void png_t::point_t::_clean_up() {
}

double png_t::point_t::x() {
    if (f_x)
        return m_x;
    m_x = (x_int() / 100000.0);
    f_x = true;
    return m_x;
}

double png_t::point_t::y() {
    if (f_y)
        return m_y;
    m_y = (y_int() / 100000.0);
    f_y = true;
    return m_y;
}

png_t::bkgd_greyscale_t::bkgd_greyscale_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::bkgd_greyscale_t::_read() {
    m_value = m__io->read_u2be();
}

png_t::bkgd_greyscale_t::~bkgd_greyscale_t() {
    _clean_up();
}

void png_t::bkgd_greyscale_t::_clean_up() {
}

png_t::chrm_chunk_t::chrm_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_white_point = nullptr;
    m_red = nullptr;
    m_green = nullptr;
    m_blue = nullptr;
    _read();
}

void png_t::chrm_chunk_t::_read() {
    m_white_point = std::unique_ptr<point_t>(new point_t(m__io, this, m__root));
    m_red = std::unique_ptr<point_t>(new point_t(m__io, this, m__root));
    m_green = std::unique_ptr<point_t>(new point_t(m__io, this, m__root));
    m_blue = std::unique_ptr<point_t>(new point_t(m__io, this, m__root));
}

png_t::chrm_chunk_t::~chrm_chunk_t() {
    _clean_up();
}

void png_t::chrm_chunk_t::_clean_up() {
}

png_t::ihdr_chunk_t::ihdr_chunk_t(kaitai::kstream* p__io, png_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::ihdr_chunk_t::_read() {
    m_width = m__io->read_u4be();
    m_height = m__io->read_u4be();
    m_bit_depth = m__io->read_u1();
    m_color_type = static_cast<png_t::color_type_t>(m__io->read_u1());
    m_compression_method = m__io->read_u1();
    m_filter_method = m__io->read_u1();
    m_interlace_method = m__io->read_u1();
}

png_t::ihdr_chunk_t::~ihdr_chunk_t() {
    _clean_up();
}

void png_t::ihdr_chunk_t::_clean_up() {
}

png_t::plte_chunk_t::plte_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = nullptr;
    _read();
}

void png_t::plte_chunk_t::_read() {
    m_entries = std::unique_ptr<std::vector<std::unique_ptr<rgb_t>>>(new std::vector<std::unique_ptr<rgb_t>>());
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(std::move(std::unique_ptr<rgb_t>(new rgb_t(m__io, this, m__root))));
            i++;
        }
    }
}

png_t::plte_chunk_t::~plte_chunk_t() {
    _clean_up();
}

void png_t::plte_chunk_t::_clean_up() {
}

png_t::srgb_chunk_t::srgb_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::srgb_chunk_t::_read() {
    m_render_intent = static_cast<png_t::srgb_chunk_t::intent_t>(m__io->read_u1());
}

png_t::srgb_chunk_t::~srgb_chunk_t() {
    _clean_up();
}

void png_t::srgb_chunk_t::_clean_up() {
}

png_t::compressed_text_chunk_t::compressed_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__io_text_datastream = nullptr;
    _read();
}

void png_t::compressed_text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    m__raw_text_datastream = m__io->read_bytes_full();
    m_text_datastream = kaitai::kstream::process_zlib(m__raw_text_datastream);
}

png_t::compressed_text_chunk_t::~compressed_text_chunk_t() {
    _clean_up();
}

void png_t::compressed_text_chunk_t::_clean_up() {
}

png_t::frame_data_chunk_t::frame_data_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::frame_data_chunk_t::_read() {
    m_sequence_number = m__io->read_u4be();
    m_frame_data = m__io->read_bytes_full();
}

png_t::frame_data_chunk_t::~frame_data_chunk_t() {
    _clean_up();
}

void png_t::frame_data_chunk_t::_clean_up() {
}

png_t::bkgd_truecolor_t::bkgd_truecolor_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::bkgd_truecolor_t::_read() {
    m_red = m__io->read_u2be();
    m_green = m__io->read_u2be();
    m_blue = m__io->read_u2be();
}

png_t::bkgd_truecolor_t::~bkgd_truecolor_t() {
    _clean_up();
}

void png_t::bkgd_truecolor_t::_clean_up() {
}

png_t::gama_chunk_t::gama_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_gamma_ratio = false;
    _read();
}

void png_t::gama_chunk_t::_read() {
    m_gamma_int = m__io->read_u4be();
}

png_t::gama_chunk_t::~gama_chunk_t() {
    _clean_up();
}

void png_t::gama_chunk_t::_clean_up() {
}

double png_t::gama_chunk_t::gamma_ratio() {
    if (f_gamma_ratio)
        return m_gamma_ratio;
    m_gamma_ratio = (100000.0 / gamma_int());
    f_gamma_ratio = true;
    return m_gamma_ratio;
}

png_t::bkgd_chunk_t::bkgd_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::bkgd_chunk_t::_read() {
    n_bkgd = true;
    switch (_root()->ihdr()->color_type()) {
    case png_t::COLOR_TYPE_INDEXED: {
        n_bkgd = false;
        m_bkgd = std::unique_ptr<bkgd_indexed_t>(new bkgd_indexed_t(m__io, this, m__root));
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR_ALPHA: {
        n_bkgd = false;
        m_bkgd = std::unique_ptr<bkgd_truecolor_t>(new bkgd_truecolor_t(m__io, this, m__root));
        break;
    }
    case png_t::COLOR_TYPE_GREYSCALE_ALPHA: {
        n_bkgd = false;
        m_bkgd = std::unique_ptr<bkgd_greyscale_t>(new bkgd_greyscale_t(m__io, this, m__root));
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR: {
        n_bkgd = false;
        m_bkgd = std::unique_ptr<bkgd_truecolor_t>(new bkgd_truecolor_t(m__io, this, m__root));
        break;
    }
    case png_t::COLOR_TYPE_GREYSCALE: {
        n_bkgd = false;
        m_bkgd = std::unique_ptr<bkgd_greyscale_t>(new bkgd_greyscale_t(m__io, this, m__root));
        break;
    }
    }
}

png_t::bkgd_chunk_t::~bkgd_chunk_t() {
    _clean_up();
}

void png_t::bkgd_chunk_t::_clean_up() {
    if (!n_bkgd) {
    }
}

png_t::phys_chunk_t::phys_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::phys_chunk_t::_read() {
    m_pixels_per_unit_x = m__io->read_u4be();
    m_pixels_per_unit_y = m__io->read_u4be();
    m_unit = static_cast<png_t::phys_unit_t>(m__io->read_u1());
}

png_t::phys_chunk_t::~phys_chunk_t() {
    _clean_up();
}

void png_t::phys_chunk_t::_clean_up() {
}

png_t::frame_control_chunk_t::frame_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_delay = false;
    _read();
}

void png_t::frame_control_chunk_t::_read() {
    m_sequence_number = m__io->read_u4be();
    m_width = m__io->read_u4be();
    if (!(width() >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, width(), _io(), std::string("/types/frame_control_chunk/seq/1"));
    }
    if (!(width() <= _root()->ihdr()->width())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->width(), width(), _io(), std::string("/types/frame_control_chunk/seq/1"));
    }
    m_height = m__io->read_u4be();
    if (!(height() >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, height(), _io(), std::string("/types/frame_control_chunk/seq/2"));
    }
    if (!(height() <= _root()->ihdr()->height())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->height(), height(), _io(), std::string("/types/frame_control_chunk/seq/2"));
    }
    m_x_offset = m__io->read_u4be();
    if (!(x_offset() <= (_root()->ihdr()->width() - width()))) {
        throw kaitai::validation_greater_than_error<uint32_t>((_root()->ihdr()->width() - width()), x_offset(), _io(), std::string("/types/frame_control_chunk/seq/3"));
    }
    m_y_offset = m__io->read_u4be();
    if (!(y_offset() <= (_root()->ihdr()->height() - height()))) {
        throw kaitai::validation_greater_than_error<uint32_t>((_root()->ihdr()->height() - height()), y_offset(), _io(), std::string("/types/frame_control_chunk/seq/4"));
    }
    m_delay_num = m__io->read_u2be();
    m_delay_den = m__io->read_u2be();
    m_dispose_op = static_cast<png_t::dispose_op_values_t>(m__io->read_u1());
    m_blend_op = static_cast<png_t::blend_op_values_t>(m__io->read_u1());
}

png_t::frame_control_chunk_t::~frame_control_chunk_t() {
    _clean_up();
}

void png_t::frame_control_chunk_t::_clean_up() {
}

double png_t::frame_control_chunk_t::delay() {
    if (f_delay)
        return m_delay;
    m_delay = (delay_num() / ((delay_den() == 0) ? (100.0) : (delay_den())));
    f_delay = true;
    return m_delay;
}

png_t::international_text_chunk_t::international_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::international_text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
    m_compression_flag = m__io->read_u1();
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    m_language_tag = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
    m_translated_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
    m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), std::string("UTF-8"));
}

png_t::international_text_chunk_t::~international_text_chunk_t() {
    _clean_up();
}

void png_t::international_text_chunk_t::_clean_up() {
}

png_t::text_chunk_t::text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("iso8859-1"));
    m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), std::string("iso8859-1"));
}

png_t::text_chunk_t::~text_chunk_t() {
    _clean_up();
}

void png_t::text_chunk_t::_clean_up() {
}

png_t::animation_control_chunk_t::animation_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::animation_control_chunk_t::_read() {
    m_num_frames = m__io->read_u4be();
    m_num_plays = m__io->read_u4be();
}

png_t::animation_control_chunk_t::~animation_control_chunk_t() {
    _clean_up();
}

void png_t::animation_control_chunk_t::_clean_up() {
}

png_t::time_chunk_t::time_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void png_t::time_chunk_t::_read() {
    m_year = m__io->read_u2be();
    m_month = m__io->read_u1();
    m_day = m__io->read_u1();
    m_hour = m__io->read_u1();
    m_minute = m__io->read_u1();
    m_second = m__io->read_u1();
}

png_t::time_chunk_t::~time_chunk_t() {
    _clean_up();
}

void png_t::time_chunk_t::_clean_up() {
}