Exchangeable image file format (Exif): Python parsing library

This page hosts a formal specification of Exchangeable image file format (Exif) 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 = Exif.from_file("path/to/local/file.bin")

Or parse structure from a bytes:

from kaitaistruct import KaitaiStream, BytesIO

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

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

data.endianness # => get endianness

Python source code to parse Exchangeable image file format (Exif)

exif.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
from enum import IntEnum


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 Exif(KaitaiStruct):
    """Sample files (numbers in parentheses show how many files per extension contain
    Exif metadata out of the total):
    
    * <https://github.com/ianare/exif-py/tree/a69bf74770caf6b333221658f5092ed69f99faac/tests/resources/jpg> (84/93 .jpg, 1/1 .jpeg)
    * <https://github.com/exiftool/exiftool/tree/2200871d9cef988051d2a99d67df3bda6cbb30a8/t/images> (34/41 .jpg, 0/1 .png)
    * <https://github.com/Exiv2/exiv2/tree/648ada43dcb35ce6077f38183ace52d5e2071f64/test/data> (85/155 .jpg, 5/23 .png)
    * <https://github.com/python-pillow/Pillow/tree/807d689a83738027b6f6e0f219a6a6dd30e01c08/Tests/images> (36/55 .jpg, 3/420 .png)
    * <https://github.com/drewnoakes/metadata-extractor-images/tree/651ad0e67aa8d43d358ad05f9bc07b52d8b9ac6e/jpg> (335/430 .jpg)
    * <https://github.com/libexif/libexif-testsuite/tree/8c1f5bbc18d2cbc80b01b3f9b3eb29546310acf2> (15/18 .jpg)
    
    .. seealso::
       Exif Version 3.1 - https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-008-2026-E
    
    
    .. seealso::
       Exif Version 3.0 - https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-008-2024-E
    
    
    .. seealso::
       Exif Version 2.32 - https://web.archive.org/web/20190624045241id_/https://www.cipa.jp/std/documents/e/DC-008-Translation-2019-E.pdf
    
    
    .. seealso::
       Exif Version 2.31 - https://web.archive.org/web/20190712232333id_/https://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
    
    
    .. seealso::
       Exif Version 2.3 (with Corrigendum at the end) - https://www.cipa.jp/std/documents/e/DC-008-2012_E_C.pdf
    
    
    .. seealso::
       Exif Version 2.21 (2003 draft) - https://web.archive.org/web/20051228234707id_/https://tsc.jeita.or.jp/avs/data/cp3451_1.pdf
    
    
    .. seealso::
       Exif Version 2.2 - https://web.archive.org/web/20131018091152id_/https://exif.org/Exif2-2.PDF
    
    
    .. seealso::
       Exif Version 2.1 - https://web.archive.org/web/20131111073619id_/https://exif.org/Exif2-1.PDF
    """

    class FieldType(IntEnum):
        byte = 1
        ascii = 2
        short = 3
        long = 4
        rational = 5
        sbyte = 6
        undefined = 7
        sshort = 8
        slong = 9
        srational = 10
        float = 11
        double = 12
        ifd = 13
        utf8 = 129

    class GpsTag(IntEnum):
        gps_version_id = 0
        gps_latitude_ref = 1
        gps_latitude = 2
        gps_longitude_ref = 3
        gps_longitude = 4
        gps_altitude_ref = 5
        gps_altitude = 6
        gps_time_stamp = 7
        gps_satellites = 8
        gps_status = 9
        gps_measure_mode = 10
        gps_dop = 11
        gps_speed_ref = 12
        gps_speed = 13
        gps_track_ref = 14
        gps_track = 15
        gps_img_direction_ref = 16
        gps_img_direction = 17
        gps_map_datum = 18
        gps_dest_latitude_ref = 19
        gps_dest_latitude = 20
        gps_dest_longitude_ref = 21
        gps_dest_longitude = 22
        gps_dest_bearing_ref = 23
        gps_dest_bearing = 24
        gps_dest_distance_ref = 25
        gps_dest_distance = 26
        gps_processing_method = 27
        gps_area_information = 28
        gps_date_stamp = 29
        gps_differential = 30
        gps_h_positioning_error = 31

    class Tag(IntEnum):
        interop_index = 1
        interop_version = 2
        image_width = 256
        image_height = 257
        bits_per_sample = 258
        compression = 259
        photometric_interpretation = 262
        thresholding = 263
        cell_width = 264
        cell_length = 265
        fill_order = 266
        document_name = 269
        image_description = 270
        make = 271
        model = 272
        strip_offsets = 273
        orientation = 274
        samples_per_pixel = 277
        rows_per_strip = 278
        strip_byte_counts = 279
        min_sample_value = 280
        max_sample_value = 281
        x_resolution = 282
        y_resolution = 283
        planar_configuration = 284
        page_name = 285
        x_position = 286
        y_position = 287
        free_offsets = 288
        free_byte_counts = 289
        gray_response_unit = 290
        gray_response_curve = 291
        t4_options = 292
        t6_options = 293
        resolution_unit = 296
        page_number = 297
        color_response_unit = 300
        transfer_function = 301
        software = 305
        modify_date = 306
        artist = 315
        host_computer = 316
        predictor = 317
        white_point = 318
        primary_chromaticities = 319
        color_map = 320
        halftone_hints = 321
        tile_width = 322
        tile_length = 323
        tile_offsets = 324
        tile_byte_counts = 325
        bad_fax_lines = 326
        clean_fax_data = 327
        consecutive_bad_fax_lines = 328
        sub_ifd = 330
        ink_set = 332
        ink_names = 333
        numberof_inks = 334
        dot_range = 336
        target_printer = 337
        extra_samples = 338
        sample_format = 339
        s_min_sample_value = 340
        s_max_sample_value = 341
        transfer_range = 342
        clip_path = 343
        x_clip_path_units = 344
        y_clip_path_units = 345
        indexed = 346
        jpeg_tables = 347
        opi_proxy = 351
        global_parameters_ifd = 400
        profile_type = 401
        fax_profile = 402
        coding_methods = 403
        version_year = 404
        mode_number = 405
        decode = 433
        default_image_color = 434
        t82_options = 435
        jpeg_tables2 = 437
        jpeg_proc = 512
        thumbnail_offset = 513
        thumbnail_length = 514
        jpeg_restart_interval = 515
        jpeg_lossless_predictors = 517
        jpeg_point_transforms = 518
        jpegq_tables = 519
        jpegdc_tables = 520
        jpegac_tables = 521
        y_cb_cr_coefficients = 529
        y_cb_cr_sub_sampling = 530
        y_cb_cr_positioning = 531
        reference_black_white = 532
        strip_row_counts = 559
        application_notes = 700
        uspto_miscellaneous = 999
        related_image_file_format = 4096
        related_image_width = 4097
        related_image_height = 4098
        rating = 18246
        xp_dip_xml = 18247
        stitch_info = 18248
        rating_percent = 18249
        sony_raw_file_type = 28672
        light_falloff_params = 28722
        chromatic_aberration_corr_params = 28725
        distortion_corr_params = 28727
        image_id = 32781
        wang_tag1 = 32931
        wang_annotation = 32932
        wang_tag3 = 32933
        wang_tag4 = 32934
        image_reference_points = 32953
        region_xform_tack_point = 32954
        warp_quadrilateral = 32955
        affine_transform_mat = 32956
        matteing = 32995
        data_type = 32996
        image_depth = 32997
        tile_depth = 32998
        image_full_width = 33300
        image_full_height = 33301
        texture_format = 33302
        wrap_modes = 33303
        fov_cot = 33304
        matrix_world_to_screen = 33305
        matrix_world_to_camera = 33306
        model2 = 33405
        cfa_repeat_pattern_dim = 33421
        cfa_pattern2 = 33422
        battery_level = 33423
        kodak_ifd = 33424
        copyright = 33432
        exposure_time = 33434
        f_number = 33437
        md_file_tag = 33445
        md_scale_pixel = 33446
        md_color_table = 33447
        md_lab_name = 33448
        md_sample_info = 33449
        md_prep_date = 33450
        md_prep_time = 33451
        md_file_units = 33452
        pixel_scale = 33550
        advent_scale = 33589
        advent_revision = 33590
        uic1_tag = 33628
        uic2_tag = 33629
        uic3_tag = 33630
        uic4_tag = 33631
        iptc_naa = 33723
        intergraph_packet_data = 33918
        intergraph_flag_registers = 33919
        intergraph_matrix = 33920
        ingr_reserved = 33921
        model_tie_point = 33922
        site = 34016
        color_sequence = 34017
        it8_header = 34018
        raster_padding = 34019
        bits_per_run_length = 34020
        bits_per_extended_run_length = 34021
        color_table = 34022
        image_color_indicator = 34023
        background_color_indicator = 34024
        image_color_value = 34025
        background_color_value = 34026
        pixel_intensity_range = 34027
        transparency_indicator = 34028
        color_characterization = 34029
        hc_usage = 34030
        trap_indicator = 34031
        cmyk_equivalent = 34032
        sem_info = 34118
        afcp_iptc = 34152
        pixel_magic_jbig_options = 34232
        jpl_carto_ifd = 34263
        model_transform = 34264
        wb_grgb_levels = 34306
        leaf_data = 34310
        photoshop_settings = 34377
        exif_offset = 34665
        icc_profile = 34675
        tiff_fx_extensions = 34687
        multi_profiles = 34688
        shared_data = 34689
        t88_options = 34690
        image_layer = 34732
        geo_tiff_directory = 34735
        geo_tiff_double_params = 34736
        geo_tiff_ascii_params = 34737
        jbig_options = 34750
        exposure_program = 34850
        spectral_sensitivity = 34852
        gps_info = 34853
        iso = 34855
        opto_electric_conv_factor = 34856
        interlace = 34857
        time_zone_offset = 34858
        self_timer_mode = 34859
        sensitivity_type = 34864
        standard_output_sensitivity = 34865
        recommended_exposure_index = 34866
        iso_speed = 34867
        iso_speed_latitudeyyy = 34868
        iso_speed_latitudezzz = 34869
        fax_recv_params = 34908
        fax_sub_address = 34909
        fax_recv_time = 34910
        fedex_edr = 34929
        leaf_sub_ifd = 34954
        exif_version = 36864
        date_time_original = 36867
        create_date = 36868
        google_plus_upload_code = 36873
        offset_time = 36880
        offset_time_original = 36881
        offset_time_digitized = 36882
        components_configuration = 37121
        compressed_bits_per_pixel = 37122
        shutter_speed_value = 37377
        aperture_value = 37378
        brightness_value = 37379
        exposure_compensation = 37380
        max_aperture_value = 37381
        subject_distance = 37382
        metering_mode = 37383
        light_source = 37384
        flash = 37385
        focal_length = 37386
        flash_energy = 37387
        spatial_frequency_response = 37388
        noise = 37389
        focal_plane_x_resolution = 37390
        focal_plane_y_resolution = 37391
        focal_plane_resolution_unit = 37392
        image_number = 37393
        security_classification = 37394
        image_history = 37395
        subject_area = 37396
        exposure_index = 37397
        tiff_ep_standard_id = 37398
        sensing_method = 37399
        cip3_data_file = 37434
        cip3_sheet = 37435
        cip3_side = 37436
        sto_nits = 37439
        maker_note = 37500
        user_comment = 37510
        sub_sec_time = 37520
        sub_sec_time_original = 37521
        sub_sec_time_digitized = 37522
        ms_document_text = 37679
        ms_property_set_storage = 37680
        ms_document_text_position = 37681
        image_source_data = 37724
        ambient_temperature = 37888
        humidity = 37889
        pressure = 37890
        water_depth = 37891
        acceleration = 37892
        camera_elevation_angle = 37893
        xp_title = 40091
        xp_comment = 40092
        xp_author = 40093
        xp_keywords = 40094
        xp_subject = 40095
        flashpix_version = 40960
        color_space = 40961
        exif_image_width = 40962
        exif_image_height = 40963
        related_sound_file = 40964
        interop_offset = 40965
        samsung_raw_pointers_offset = 40976
        samsung_raw_pointers_length = 40977
        samsung_raw_byte_order = 41217
        samsung_raw_unknown = 41218
        flash_energy2 = 41483
        spatial_frequency_response2 = 41484
        noise2 = 41485
        focal_plane_x_resolution2 = 41486
        focal_plane_y_resolution2 = 41487
        focal_plane_resolution_unit2 = 41488
        image_number2 = 41489
        security_classification2 = 41490
        image_history2 = 41491
        subject_location = 41492
        exposure_index2 = 41493
        tiff_ep_standard_id2 = 41494
        sensing_method2 = 41495
        file_source = 41728
        scene_type = 41729
        cfa_pattern = 41730
        custom_rendered = 41985
        exposure_mode = 41986
        white_balance = 41987
        digital_zoom_ratio = 41988
        focal_length_in35mm_format = 41989
        scene_capture_type = 41990
        gain_control = 41991
        contrast = 41992
        saturation = 41993
        sharpness = 41994
        device_setting_description = 41995
        subject_distance_range = 41996
        image_unique_id = 42016
        owner_name = 42032
        serial_number = 42033
        lens_info = 42034
        lens_make = 42035
        lens_model = 42036
        lens_serial_number = 42037
        gdal_metadata = 42112
        gdal_no_data = 42113
        gamma = 42240
        expand_software = 44992
        expand_lens = 44993
        expand_film = 44994
        expand_filter_lens = 44995
        expand_scanner = 44996
        expand_flash_lamp = 44997
        pixel_format = 48129
        transformation = 48130
        uncompressed = 48131
        image_type = 48132
        image_width2 = 48256
        image_height2 = 48257
        width_resolution = 48258
        height_resolution = 48259
        image_offset = 48320
        image_byte_count = 48321
        alpha_offset = 48322
        alpha_byte_count = 48323
        image_data_discard = 48324
        alpha_data_discard = 48325
        oce_scanjob_desc = 50215
        oce_application_selector = 50216
        oce_id_number = 50217
        oce_image_logic = 50218
        annotations = 50255
        print_im = 50341
        original_file_name = 50547
        uspto_original_content_type = 50560
        dng_version = 50706
        dng_backward_version = 50707
        unique_camera_model = 50708
        localized_camera_model = 50709
        cfa_plane_color = 50710
        cfa_layout = 50711
        linearization_table = 50712
        black_level_repeat_dim = 50713
        black_level = 50714
        black_level_delta_h = 50715
        black_level_delta_v = 50716
        white_level = 50717
        default_scale = 50718
        default_crop_origin = 50719
        default_crop_size = 50720
        color_matrix1 = 50721
        color_matrix2 = 50722
        camera_calibration1 = 50723
        camera_calibration2 = 50724
        reduction_matrix1 = 50725
        reduction_matrix2 = 50726
        analog_balance = 50727
        as_shot_neutral = 50728
        as_shot_white_xy = 50729
        baseline_exposure = 50730
        baseline_noise = 50731
        baseline_sharpness = 50732
        bayer_green_split = 50733
        linear_response_limit = 50734
        camera_serial_number = 50735
        dng_lens_info = 50736
        chroma_blur_radius = 50737
        anti_alias_strength = 50738
        shadow_scale = 50739
        sr2_private = 50740
        maker_note_safety = 50741
        raw_image_segmentation = 50752
        calibration_illuminant1 = 50778
        calibration_illuminant2 = 50779
        best_quality_scale = 50780
        raw_data_unique_id = 50781
        alias_layer_metadata = 50784
        original_raw_file_name = 50827
        original_raw_file_data = 50828
        active_area = 50829
        masked_areas = 50830
        as_shot_icc_profile = 50831
        as_shot_pre_profile_matrix = 50832
        current_icc_profile = 50833
        current_pre_profile_matrix = 50834
        colorimetric_reference = 50879
        s_raw_type = 50885
        panasonic_title = 50898
        panasonic_title2 = 50899
        camera_calibration_sig = 50931
        profile_calibration_sig = 50932
        profile_ifd = 50933
        as_shot_profile_name = 50934
        noise_reduction_applied = 50935
        profile_name = 50936
        profile_hue_sat_map_dims = 50937
        profile_hue_sat_map_data1 = 50938
        profile_hue_sat_map_data2 = 50939
        profile_tone_curve = 50940
        profile_embed_policy = 50941
        profile_copyright = 50942
        forward_matrix1 = 50964
        forward_matrix2 = 50965
        preview_application_name = 50966
        preview_application_version = 50967
        preview_settings_name = 50968
        preview_settings_digest = 50969
        preview_color_space = 50970
        preview_date_time = 50971
        raw_image_digest = 50972
        original_raw_file_digest = 50973
        sub_tile_block_size = 50974
        row_interleave_factor = 50975
        profile_look_table_dims = 50981
        profile_look_table_data = 50982
        opcode_list1 = 51008
        opcode_list2 = 51009
        opcode_list3 = 51022
        noise_profile = 51041
        time_codes = 51043
        frame_rate = 51044
        t_stop = 51058
        reel_name = 51081
        original_default_final_size = 51089
        original_best_quality_size = 51090
        original_default_crop_size = 51091
        camera_label = 51105
        profile_hue_sat_map_encoding = 51107
        profile_look_table_encoding = 51108
        baseline_exposure_offset = 51109
        default_black_render = 51110
        new_raw_image_digest = 51111
        raw_to_preview_gain = 51112
        default_user_crop = 51125
        padding = 59932
        offset_schema = 59933
        owner_name2 = 65000
        serial_number2 = 65001
        lens = 65002
        kdc_ifd = 65024
        raw_file = 65100
        converter = 65101
        white_balance2 = 65102
        exposure = 65105
        shadows = 65106
        brightness = 65107
        contrast2 = 65108
        saturation2 = 65109
        sharpness2 = 65110
        smoothness = 65111
        moire_filter = 65112
    def __init__(self, _io, _parent=None, _root=None):
        super(Exif, self).__init__(_io)
        self._parent = _parent
        self._root = _root or self
        self._read()

    def _read(self):
        self.endianness = self._io.read_u2le()
        self.body = Exif.ExifBody(self._io, self, self._root)


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

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

        def _read(self):
            _on = self._root.endianness
            if _on == 18761:
                pass
                self._is_le = True
            elif _on == 19789:
                pass
                self._is_le = False
            if not hasattr(self, '_is_le'):
                raise kaitaistruct.UndecidedEndiannessError("/types/exif_body")
            elif self._is_le == True:
                self._read_le()
            elif self._is_le == False:
                self._read_be()

        def _read_le(self):
            self.magic = self._io.read_u2le()
            if not self.magic == 42:
                raise kaitaistruct.ValidationNotEqualError(42, self.magic, self._io, u"/types/exif_body/seq/0")
            self.ofs_ifd0 = self._io.read_u4le()

        def _read_be(self):
            self.magic = self._io.read_u2be()
            if not self.magic == 42:
                raise kaitaistruct.ValidationNotEqualError(42, self.magic, self._io, u"/types/exif_body/seq/0")
            self.ofs_ifd0 = self._io.read_u4be()


        def _fetch_instances(self):
            pass
            _ = self.ifd0
            if hasattr(self, '_m_ifd0'):
                pass
                self._m_ifd0._fetch_instances()


        class AsciiString(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.AsciiString, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/ascii_string")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.value = self._io.read_bytes_term(0, False, True, False)

            def _read_be(self):
                self.value = self._io.read_bytes_term(0, False, True, False)


            def _fetch_instances(self):
                pass


        class Doubles(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Doubles, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/doubles")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_f8le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_f8be())



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



        class Floats(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Floats, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/floats")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_f4le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_f4be())



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



        class Ifd(KaitaiStruct):
            def __init__(self, is_gps_ifd, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Ifd, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self.is_gps_ifd = is_gps_ifd
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/ifd")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.num_fields = self._io.read_u2le()
                self._raw_fields = []
                self.fields = []
                for i in range(self.num_fields):
                    self._raw_fields.append(self._io.read_bytes(12))
                    _io__raw_fields = KaitaiStream(BytesIO(self._raw_fields[i]))
                    self.fields.append(Exif.ExifBody.IfdField(_io__raw_fields, self, self._root, self._is_le))

                self.ofs_next_ifd = self._io.read_u4le()

            def _read_be(self):
                self.num_fields = self._io.read_u2be()
                self._raw_fields = []
                self.fields = []
                for i in range(self.num_fields):
                    self._raw_fields.append(self._io.read_bytes(12))
                    _io__raw_fields = KaitaiStream(BytesIO(self._raw_fields[i]))
                    self.fields.append(Exif.ExifBody.IfdField(_io__raw_fields, self, self._root, self._is_le))

                self.ofs_next_ifd = self._io.read_u4be()


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

                _ = self.next_ifd
                if hasattr(self, '_m_next_ifd'):
                    pass
                    self._m_next_ifd._fetch_instances()


            @property
            def next_ifd(self):
                if hasattr(self, '_m_next_ifd'):
                    return self._m_next_ifd

                if self.ofs_next_ifd != 0:
                    pass
                    _pos = self._io.pos()
                    self._io.seek(self.ofs_next_ifd)
                    if self._is_le:
                        self._m_next_ifd = Exif.ExifBody.Ifd(self.is_gps_ifd, self._io, self, self._root, self._is_le)
                    else:
                        self._m_next_ifd = Exif.ExifBody.Ifd(self.is_gps_ifd, self._io, self, self._root, self._is_le)
                    self._io.seek(_pos)

                return getattr(self, '_m_next_ifd', None)


        class IfdField(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.IfdField, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/ifd_field")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.tag_raw = self._io.read_u2le()
                self.field_type = KaitaiStream.resolve_enum(Exif.FieldType, self._io.read_u2le())
                self.num_values = self._io.read_u4le()
                if (not (self.has_immediate_data)):
                    pass
                    self.ofs_data = self._io.read_u4le()


            def _read_be(self):
                self.tag_raw = self._io.read_u2be()
                self.field_type = KaitaiStream.resolve_enum(Exif.FieldType, self._io.read_u2be())
                self.num_values = self._io.read_u4be()
                if (not (self.has_immediate_data)):
                    pass
                    self.ofs_data = self._io.read_u4be()



            def _fetch_instances(self):
                pass
                if (not (self.has_immediate_data)):
                    pass

                _ = self.data
                if hasattr(self, '_m_data'):
                    pass
                    _on = self.field_type
                    if _on == Exif.FieldType.ascii:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.double:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.float:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.ifd:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.long:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.rational:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.sbyte:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.short:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.slong:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.srational:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.sshort:
                        pass
                        self._m_data._fetch_instances()
                    elif _on == Exif.FieldType.utf8:
                        pass
                        self._m_data._fetch_instances()
                    else:
                        pass

                _ = self.sub_ifd
                if hasattr(self, '_m_sub_ifd'):
                    pass
                    self._m_sub_ifd._fetch_instances()


            @property
            def bytes_per_value(self):
                """Size in bytes of a single value of type `field_type`, or 0 if
                `field_type` is not one of the known types (in which case the size
                cannot be determined and `data` will be empty).
                
                .. seealso::
                   Source - https://www.media.mit.edu/pia/Research/deepview/exif.html#DataForm
                """
                if hasattr(self, '_m_bytes_per_value'):
                    return self._m_bytes_per_value

                self._m_bytes_per_value = (1 if  ((self.field_type == Exif.FieldType.byte) or (self.field_type == Exif.FieldType.ascii) or (self.field_type == Exif.FieldType.sbyte) or (self.field_type == Exif.FieldType.undefined) or (self.field_type == Exif.FieldType.utf8))  else (2 if  ((self.field_type == Exif.FieldType.short) or (self.field_type == Exif.FieldType.sshort))  else (4 if  ((self.field_type == Exif.FieldType.long) or (self.field_type == Exif.FieldType.slong) or (self.field_type == Exif.FieldType.float) or (self.field_type == Exif.FieldType.ifd))  else (8 if  ((self.field_type == Exif.FieldType.rational) or (self.field_type == Exif.FieldType.srational) or (self.field_type == Exif.FieldType.double))  else 0))))
                return getattr(self, '_m_bytes_per_value', None)

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

                io = (self._io if self.has_immediate_data else self._root._io)
                _pos = io.pos()
                io.seek((8 if self.has_immediate_data else self.ofs_data))
                if self._is_le:
                    _on = self.field_type
                    if _on == Exif.FieldType.ascii:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.AsciiString(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.double:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Doubles(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.float:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Floats(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.ifd:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Longs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.long:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Longs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.rational:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Rationals(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.sbyte:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Sbytes(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.short:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Shorts(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.slong:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Slongs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.srational:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Srationals(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.sshort:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Sshorts(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.utf8:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Utf8String(_io__raw__m_data, self, self._root, self._is_le)
                    else:
                        pass
                        self._m_data = io.read_bytes(self.len_data)
                else:
                    _on = self.field_type
                    if _on == Exif.FieldType.ascii:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.AsciiString(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.double:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Doubles(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.float:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Floats(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.ifd:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Longs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.long:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Longs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.rational:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Rationals(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.sbyte:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Sbytes(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.short:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Shorts(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.slong:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Slongs(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.srational:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Srationals(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.sshort:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Sshorts(_io__raw__m_data, self, self._root, self._is_le)
                    elif _on == Exif.FieldType.utf8:
                        pass
                        self._raw__m_data = io.read_bytes(self.len_data)
                        _io__raw__m_data = KaitaiStream(BytesIO(self._raw__m_data))
                        self._m_data = Exif.ExifBody.Utf8String(_io__raw__m_data, self, self._root, self._is_le)
                    else:
                        pass
                        self._m_data = io.read_bytes(self.len_data)
                io.seek(_pos)
                return getattr(self, '_m_data', None)

            @property
            def gps_tag(self):
                if hasattr(self, '_m_gps_tag'):
                    return self._m_gps_tag

                if self._parent.is_gps_ifd:
                    pass
                    self._m_gps_tag = KaitaiStream.resolve_enum(Exif.GpsTag, self.tag_raw)

                return getattr(self, '_m_gps_tag', None)

            @property
            def has_immediate_data(self):
                if hasattr(self, '_m_has_immediate_data'):
                    return self._m_has_immediate_data

                self._m_has_immediate_data = self.len_data <= 4
                return getattr(self, '_m_has_immediate_data', None)

            @property
            def len_data(self):
                if hasattr(self, '_m_len_data'):
                    return self._m_len_data

                self._m_len_data = self.bytes_per_value * self.num_values
                return getattr(self, '_m_len_data', None)

            @property
            def sub_ifd(self):
                """All the "IFD Pointer" tags (as the core Exif standard calls them),
                i.e. `ExifOffset`, `InteropOffset` and `GPSInfo` (using the
                [ExifTool's
                names](https://exiftool.sourceforge.net/TagNames/EXIF.html)),
                should be of type `LONG` (`field_type::long`). However, the type
                `SLONG` (`field_type::slong`) type has also been observed:
                <https://github.com/Exiv2/exiv2/blob/2cd987a731236037b6b78cbff897d08685a8ef49/test/data/FurnaceCreekInn.jpg>
                
                Both ExifTool and Exiv2 accept `LONG`, `SLONG` and also `IFD`.
                Exiv2 specifically supports only these three types - see
                <https://github.com/Exiv2/exiv2/blob/2cd987a731236037b6b78cbff897d08685a8ef49/src/tiffvisitor_int.cpp#L1141>
                (Git tag "v0.28.8"). ExifTool is more lenient - it even accepts
                any integer type. In practice, real files most likely only use one
                of the three types supported by Exiv2, so we stick with that.
                """
                if hasattr(self, '_m_sub_ifd'):
                    return self._m_sub_ifd

                if  ((self.num_values == 1) and ( ((self.field_type == Exif.FieldType.long) or (self.field_type == Exif.FieldType.ifd) or ( ((self.field_type == Exif.FieldType.slong) and (self.data.values[0] >= 0)) )) ) and ( ((self.tag == Exif.Tag.exif_offset) or (self.tag == Exif.Tag.interop_offset) or (self.tag == Exif.Tag.gps_info)) )) :
                    pass
                    io = self._root._io
                    _pos = io.pos()
                    io.seek((self.data.values[0] if self.field_type == Exif.FieldType.slong else self.data.values[0]))
                    if self._is_le:
                        self._m_sub_ifd = Exif.ExifBody.Ifd(self.tag == Exif.Tag.gps_info, io, self, self._root, self._is_le)
                    else:
                        self._m_sub_ifd = Exif.ExifBody.Ifd(self.tag == Exif.Tag.gps_info, io, self, self._root, self._is_le)
                    io.seek(_pos)

                return getattr(self, '_m_sub_ifd', None)

            @property
            def tag(self):
                if hasattr(self, '_m_tag'):
                    return self._m_tag

                if (not (self._parent.is_gps_ifd)):
                    pass
                    self._m_tag = KaitaiStream.resolve_enum(Exif.Tag, self.tag_raw)

                return getattr(self, '_m_tag', None)


        class Longs(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Longs, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/longs")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_u4le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_u4be())



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



        class Rational(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Rational, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/rational")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.value_num = self._io.read_u4le()
                self.value_den = self._io.read_u4le()

            def _read_be(self):
                self.value_num = self._io.read_u4be()
                self.value_den = self._io.read_u4be()


            def _fetch_instances(self):
                pass

            @property
            def value(self):
                """If denominator is zero, this instance is disabled to prevent
                `ZeroDivisionError` in Python.
                
                Here's a sample file with a zero denominator in the IFD fields
                `tag::x_resolution` and `tag::y_resolution` (both of which are of
                type `field_type::rational`):
                <https://github.com/python-pillow/Pillow/blob/807d689a83738027b6f6e0f219a6a6dd30e01c08/Tests/images/exif-dpi-zerodivision.jpg>
                """
                if hasattr(self, '_m_value'):
                    return self._m_value

                if self.value_den != 0:
                    pass
                    self._m_value = (self.value_num + 0.0) / self.value_den

                return getattr(self, '_m_value', None)


        class Rationals(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Rationals, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/rationals")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(Exif.ExifBody.Rational(self._io, self, self._root, self._is_le))


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(Exif.ExifBody.Rational(self._io, self, self._root, self._is_le))



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



        class Sbytes(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Sbytes, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/sbytes")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s1())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s1())



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



        class Shorts(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Shorts, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/shorts")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_u2le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_u2be())



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



        class Slongs(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Slongs, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/slongs")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s4le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s4be())



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



        class Srational(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Srational, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/srational")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.value_num = self._io.read_s4le()
                self.value_den = self._io.read_s4le()

            def _read_be(self):
                self.value_num = self._io.read_s4be()
                self.value_den = self._io.read_s4be()


            def _fetch_instances(self):
                pass

            @property
            def value(self):
                """If denominator is zero, this instance is disabled to prevent
                `ZeroDivisionError` in Python.
                
                Here's a sample file with a zero denominator in the IFD field
                `tag::exposure_compensation` of type `field_type::srational`:
                <https://github.com/drewnoakes/metadata-extractor-images/blob/651ad0e67aa8d43d358ad05f9bc07b52d8b9ac6e/jpg/Reconyx%20Hyperfire%20HP4K.jpg>
                """
                if hasattr(self, '_m_value'):
                    return self._m_value

                if self.value_den != 0:
                    pass
                    self._m_value = (self.value_num + 0.0) / self.value_den

                return getattr(self, '_m_value', None)


        class Srationals(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Srationals, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/srationals")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(Exif.ExifBody.Srational(self._io, self, self._root, self._is_le))


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(Exif.ExifBody.Srational(self._io, self, self._root, self._is_le))



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



        class Sshorts(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Sshorts, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/sshorts")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s2le())


            def _read_be(self):
                self.values = []
                for i in range(self._parent.num_values):
                    self.values.append(self._io.read_s2be())



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



        class Utf8String(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None, _is_le=None):
                super(Exif.ExifBody.Utf8String, self).__init__(_io)
                self._parent = _parent
                self._root = _root
                self._is_le = _is_le
                self._read()

            def _read(self):
                if not hasattr(self, '_is_le'):
                    raise kaitaistruct.UndecidedEndiannessError("/types/exif_body/types/utf8_string")
                elif self._is_le == True:
                    self._read_le()
                elif self._is_le == False:
                    self._read_be()

            def _read_le(self):
                self.value = (self._io.read_bytes_term(0, False, True, False)).decode(u"UTF-8")

            def _read_be(self):
                self.value = (self._io.read_bytes_term(0, False, True, False)).decode(u"UTF-8")


            def _fetch_instances(self):
                pass


        @property
        def ifd0(self):
            if hasattr(self, '_m_ifd0'):
                return self._m_ifd0

            _pos = self._io.pos()
            self._io.seek(self.ofs_ifd0)
            if self._is_le:
                self._m_ifd0 = Exif.ExifBody.Ifd(False, self._io, self, self._root, self._is_le)
            else:
                self._m_ifd0 = Exif.ExifBody.Ifd(False, self._io, self, self._root, self._is_le)
            self._io.seek(_pos)
            return getattr(self, '_m_ifd0', None)