Exchangeable image file format (Exif): Lua 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.

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

exif.lua

-- This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
--
-- This file is compatible with Lua 5.3

local class = require("class")
require("kaitaistruct")
local enum = require("enum")
local stringstream = require("string_stream")
local utils = require("utils")
local str_decode = require("string_decode")

-- 
-- 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)
-- See also: Exif Version 3.1 (https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-008-2026-E)
-- See also: Exif Version 3.0 (https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-008-2024-E)
-- See also: Exif Version 2.32 (https://web.archive.org/web/20190624045241id_/https://www.cipa.jp/std/documents/e/DC-008-Translation-2019-E.pdf)
-- See also: Exif Version 2.31 (https://web.archive.org/web/20190712232333id_/https://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf)
-- See also: Exif Version 2.3 (with Corrigendum at the end) (https://www.cipa.jp/std/documents/e/DC-008-2012_E_C.pdf)
-- See also: Exif Version 2.21 (2003 draft) (https://web.archive.org/web/20051228234707id_/https://tsc.jeita.or.jp/avs/data/cp3451_1.pdf)
-- See also: Exif Version 2.2 (https://web.archive.org/web/20131018091152id_/https://exif.org/Exif2-2.PDF)
-- See also: Exif Version 2.1 (https://web.archive.org/web/20131111073619id_/https://exif.org/Exif2-1.PDF)
Exif = class.class(KaitaiStruct)

Exif.FieldType = enum.Enum {
  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,
}

Exif.GpsTag = enum.Enum {
  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,
}

Exif.Tag = enum.Enum {
  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,
}

function Exif:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root or self
  self:_read()
end

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


Exif.ExifBody = class.class(KaitaiStruct)

function Exif.ExifBody:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Exif.ExifBody:_read()
  local _on = self._root.endianness
  if _on == 18761 then
    self._is_le = true
  elseif _on == 19789 then
    self._is_le = false
  end

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody:_read_le()
  self.magic = self._io:read_u2le()
  if not(self.magic == 42) then
    error("not equal, expected " .. 42 .. ", but got " .. self.magic)
  end
  self.ofs_ifd0 = self._io:read_u4le()
end

function Exif.ExifBody:_read_be()
  self.magic = self._io:read_u2be()
  if not(self.magic == 42) then
    error("not equal, expected " .. 42 .. ", but got " .. self.magic)
  end
  self.ofs_ifd0 = self._io:read_u4be()
end

Exif.ExifBody.property.ifd0 = {}
function Exif.ExifBody.property.ifd0:get()
  if self._m_ifd0 ~= nil then
    return self._m_ifd0
  end

  local _pos = self._io:pos()
  self._io:seek(self.ofs_ifd0)
  if self._is_le then
    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)
  end
  self._io:seek(_pos)
  return self._m_ifd0
end


Exif.ExifBody.AsciiString = class.class(KaitaiStruct)

function Exif.ExifBody.AsciiString:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.AsciiString:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.AsciiString:_read_le()
  self.value = self._io:read_bytes_term(0, false, true, false)
end

function Exif.ExifBody.AsciiString:_read_be()
  self.value = self._io:read_bytes_term(0, false, true, false)
end

-- 
-- According to the core Exif standard, this should be ASCII, but in
-- practice, this is not always the case. From
-- [ExifTool FAQ](https://exiftool.sourceforge.net/faq.html#Q10):
-- 
-- > However, it is not uncommon for applications to write UTF-8 or
-- other encodings where ASCII is expected.
-- 
-- Therefore, this field is a byte array, not a string. This is to
-- avoid non-ASCII characters being treated as errors in some target
-- languages, such as Python. The only assumption is that a null byte
-- terminates the value (although sometimes the null byte is missing,
-- which we tolerate thanks to the `eos-error: false` setting).
-- 
-- Here is a sample JPEG file with a `tag::image_description` IFD
-- field of type `field_type::ascii` that actually contains UTF-8:
-- <https://github.com/Exiv2/exiv2/blob/2cd987a731236037b6b78cbff897d08685a8ef49/test/data/exiv2-bug501.jpg>
-- 
-- It seems that most modern applications (e.g. GIMP 3.0.6) always
-- use UTF-8 when storing Exif metadata. However, there are also
-- files with a non-UTF-8 encoding, for example
-- <https://github.com/drewnoakes/metadata-extractor-images/blob/651ad0e67aa8d43d358ad05f9bc07b52d8b9ac6e/jpg/Ricoh%20DC-3Z%20(low%20res).jpg>
-- has a `tag::copyright` IFD field with a value encoded in
-- ISO-8859-1 (Latin-1).

Exif.ExifBody.Doubles = class.class(KaitaiStruct)

function Exif.ExifBody.Doubles:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Doubles:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Doubles:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_f8le()
  end
end

function Exif.ExifBody.Doubles:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_f8be()
  end
end


Exif.ExifBody.Floats = class.class(KaitaiStruct)

function Exif.ExifBody.Floats:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Floats:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Floats:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_f4le()
  end
end

function Exif.ExifBody.Floats:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_f4be()
  end
end


Exif.ExifBody.Ifd = class.class(KaitaiStruct)

function Exif.ExifBody.Ifd:_init(is_gps_ifd, io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self.is_gps_ifd = is_gps_ifd
  self:_read()
end

function Exif.ExifBody.Ifd:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Ifd:_read_le()
  self.num_fields = self._io:read_u2le()
  self._raw_fields = {}
  self.fields = {}
  for i = 0, self.num_fields - 1 do
    self._raw_fields[i + 1] = self._io:read_bytes(12)
    local _io = KaitaiStream(stringstream(self._raw_fields[i + 1]))
    self.fields[i + 1] = Exif.ExifBody.IfdField(_io, self, self._root, self._is_le)
  end
  self.ofs_next_ifd = self._io:read_u4le()
end

function Exif.ExifBody.Ifd:_read_be()
  self.num_fields = self._io:read_u2be()
  self._raw_fields = {}
  self.fields = {}
  for i = 0, self.num_fields - 1 do
    self._raw_fields[i + 1] = self._io:read_bytes(12)
    local _io = KaitaiStream(stringstream(self._raw_fields[i + 1]))
    self.fields[i + 1] = Exif.ExifBody.IfdField(_io, self, self._root, self._is_le)
  end
  self.ofs_next_ifd = self._io:read_u4be()
end

Exif.ExifBody.Ifd.property.next_ifd = {}
function Exif.ExifBody.Ifd.property.next_ifd:get()
  if self._m_next_ifd ~= nil then
    return self._m_next_ifd
  end

  if self.ofs_next_ifd ~= 0 then
    local _pos = self._io:pos()
    self._io:seek(self.ofs_next_ifd)
    if self._is_le then
      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)
    end
    self._io:seek(_pos)
  end
  return self._m_next_ifd
end


Exif.ExifBody.IfdField = class.class(KaitaiStruct)

function Exif.ExifBody.IfdField:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.IfdField:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.IfdField:_read_le()
  self.tag_raw = self._io:read_u2le()
  self.field_type = Exif.FieldType(self._io:read_u2le())
  self.num_values = self._io:read_u4le()
  if not(self.has_immediate_data) then
    self.ofs_data = self._io:read_u4le()
  end
end

function Exif.ExifBody.IfdField:_read_be()
  self.tag_raw = self._io:read_u2be()
  self.field_type = Exif.FieldType(self._io:read_u2be())
  self.num_values = self._io:read_u4be()
  if not(self.has_immediate_data) then
    self.ofs_data = self._io:read_u4be()
  end
end

-- 
-- 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).
-- See also: Source (https://www.media.mit.edu/pia/Research/deepview/exif.html#DataForm)
Exif.ExifBody.IfdField.property.bytes_per_value = {}
function Exif.ExifBody.IfdField.property.bytes_per_value:get()
  if self._m_bytes_per_value ~= nil then
    return self._m_bytes_per_value
  end

  self._m_bytes_per_value = utils.box_unwrap(( ((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)) ) and utils.box_wrap(1) or (utils.box_unwrap(( ((self.field_type == Exif.FieldType.short) or (self.field_type == Exif.FieldType.sshort)) ) and utils.box_wrap(2) or (utils.box_unwrap(( ((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)) ) and utils.box_wrap(4) or (utils.box_unwrap(( ((self.field_type == Exif.FieldType.rational) or (self.field_type == Exif.FieldType.srational) or (self.field_type == Exif.FieldType.double)) ) and utils.box_wrap(8) or (0))))))))
  return self._m_bytes_per_value
end

Exif.ExifBody.IfdField.property.data = {}
function Exif.ExifBody.IfdField.property.data:get()
  if self._m_data ~= nil then
    return self._m_data
  end

  local _io = utils.box_unwrap((self.has_immediate_data) and utils.box_wrap(self._io) or (self._root._io))
  local _pos = _io:pos()
  _io:seek(utils.box_unwrap((self.has_immediate_data) and utils.box_wrap(8) or (self.ofs_data)))
  if self._is_le then
    local _on = self.field_type
    if _on == Exif.FieldType.ascii then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.AsciiString(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.double then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Doubles(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.float then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Floats(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.ifd then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Longs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.long then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Longs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.rational then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Rationals(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.sbyte then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Sbytes(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.short then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Shorts(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.slong then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Slongs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.srational then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Srationals(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.sshort then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Sshorts(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.utf8 then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Utf8String(_io, self, self._root, self._is_le)
    else
      self._m_data = _io:read_bytes(self.len_data)
    end
  else
    local _on = self.field_type
    if _on == Exif.FieldType.ascii then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.AsciiString(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.double then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Doubles(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.float then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Floats(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.ifd then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Longs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.long then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Longs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.rational then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Rationals(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.sbyte then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Sbytes(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.short then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Shorts(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.slong then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Slongs(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.srational then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Srationals(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.sshort then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Sshorts(_io, self, self._root, self._is_le)
    elseif _on == Exif.FieldType.utf8 then
      self._raw__m_data = _io:read_bytes(self.len_data)
      local _io = KaitaiStream(stringstream(self._raw__m_data))
      self._m_data = Exif.ExifBody.Utf8String(_io, self, self._root, self._is_le)
    else
      self._m_data = _io:read_bytes(self.len_data)
    end
  end
  _io:seek(_pos)
  return self._m_data
end

Exif.ExifBody.IfdField.property.gps_tag = {}
function Exif.ExifBody.IfdField.property.gps_tag:get()
  if self._m_gps_tag ~= nil then
    return self._m_gps_tag
  end

  if self._parent.is_gps_ifd then
    self._m_gps_tag = Exif.GpsTag(self.tag_raw)
  end
  return self._m_gps_tag
end

Exif.ExifBody.IfdField.property.has_immediate_data = {}
function Exif.ExifBody.IfdField.property.has_immediate_data:get()
  if self._m_has_immediate_data ~= nil then
    return self._m_has_immediate_data
  end

  self._m_has_immediate_data = self.len_data <= 4
  return self._m_has_immediate_data
end

Exif.ExifBody.IfdField.property.len_data = {}
function Exif.ExifBody.IfdField.property.len_data:get()
  if self._m_len_data ~= nil then
    return self._m_len_data
  end

  self._m_len_data = self.bytes_per_value * self.num_values
  return self._m_len_data
end

-- 
-- 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.
Exif.ExifBody.IfdField.property.sub_ifd = {}
function Exif.ExifBody.IfdField.property.sub_ifd:get()
  if self._m_sub_ifd ~= nil then
    return self._m_sub_ifd
  end

  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[1] >= 0)) )) ) and ( ((self.tag == Exif.Tag.exif_offset) or (self.tag == Exif.Tag.interop_offset) or (self.tag == Exif.Tag.gps_info)) ))  then
    local _io = self._root._io
    local _pos = _io:pos()
    _io:seek(utils.box_unwrap((self.field_type == Exif.FieldType.slong) and utils.box_wrap(self.data.values[1]) or (self.data.values[1])))
    if self._is_le then
      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)
    end
    _io:seek(_pos)
  end
  return self._m_sub_ifd
end

Exif.ExifBody.IfdField.property.tag = {}
function Exif.ExifBody.IfdField.property.tag:get()
  if self._m_tag ~= nil then
    return self._m_tag
  end

  if not(self._parent.is_gps_ifd) then
    self._m_tag = Exif.Tag(self.tag_raw)
  end
  return self._m_tag
end

-- 
-- Raw numeric tag. Don't read this field - access `tag` or `gps_tag`
-- instead.

Exif.ExifBody.Longs = class.class(KaitaiStruct)

function Exif.ExifBody.Longs:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Longs:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Longs:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_u4le()
  end
end

function Exif.ExifBody.Longs:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_u4be()
  end
end


Exif.ExifBody.Rational = class.class(KaitaiStruct)

function Exif.ExifBody.Rational:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Rational:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Rational:_read_le()
  self.value_num = self._io:read_u4le()
  self.value_den = self._io:read_u4le()
end

function Exif.ExifBody.Rational:_read_be()
  self.value_num = self._io:read_u4be()
  self.value_den = self._io:read_u4be()
end

-- 
-- 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>
Exif.ExifBody.Rational.property.value = {}
function Exif.ExifBody.Rational.property.value:get()
  if self._m_value ~= nil then
    return self._m_value
  end

  if self.value_den ~= 0 then
    self._m_value = (self.value_num + 0.0) / self.value_den
  end
  return self._m_value
end

-- 
-- Numerator.
-- 
-- Denominator.

Exif.ExifBody.Rationals = class.class(KaitaiStruct)

function Exif.ExifBody.Rationals:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Rationals:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Rationals:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = Exif.ExifBody.Rational(self._io, self, self._root, self._is_le)
  end
end

function Exif.ExifBody.Rationals:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = Exif.ExifBody.Rational(self._io, self, self._root, self._is_le)
  end
end


Exif.ExifBody.Sbytes = class.class(KaitaiStruct)

function Exif.ExifBody.Sbytes:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Sbytes:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Sbytes:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s1()
  end
end

function Exif.ExifBody.Sbytes:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s1()
  end
end


Exif.ExifBody.Shorts = class.class(KaitaiStruct)

function Exif.ExifBody.Shorts:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Shorts:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Shorts:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_u2le()
  end
end

function Exif.ExifBody.Shorts:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_u2be()
  end
end


Exif.ExifBody.Slongs = class.class(KaitaiStruct)

function Exif.ExifBody.Slongs:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Slongs:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Slongs:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s4le()
  end
end

function Exif.ExifBody.Slongs:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s4be()
  end
end


Exif.ExifBody.Srational = class.class(KaitaiStruct)

function Exif.ExifBody.Srational:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Srational:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Srational:_read_le()
  self.value_num = self._io:read_s4le()
  self.value_den = self._io:read_s4le()
end

function Exif.ExifBody.Srational:_read_be()
  self.value_num = self._io:read_s4be()
  self.value_den = self._io:read_s4be()
end

-- 
-- 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>
Exif.ExifBody.Srational.property.value = {}
function Exif.ExifBody.Srational.property.value:get()
  if self._m_value ~= nil then
    return self._m_value
  end

  if self.value_den ~= 0 then
    self._m_value = (self.value_num + 0.0) / self.value_den
  end
  return self._m_value
end

-- 
-- Numerator.
-- 
-- Denominator.

Exif.ExifBody.Srationals = class.class(KaitaiStruct)

function Exif.ExifBody.Srationals:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Srationals:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Srationals:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = Exif.ExifBody.Srational(self._io, self, self._root, self._is_le)
  end
end

function Exif.ExifBody.Srationals:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = Exif.ExifBody.Srational(self._io, self, self._root, self._is_le)
  end
end


Exif.ExifBody.Sshorts = class.class(KaitaiStruct)

function Exif.ExifBody.Sshorts:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Sshorts:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Sshorts:_read_le()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s2le()
  end
end

function Exif.ExifBody.Sshorts:_read_be()
  self.values = {}
  for i = 0, self._parent.num_values - 1 do
    self.values[i + 1] = self._io:read_s2be()
  end
end


Exif.ExifBody.Utf8String = class.class(KaitaiStruct)

function Exif.ExifBody.Utf8String:_init(io, parent, root, is_le)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self._is_le = is_le
  self:_read()
end

function Exif.ExifBody.Utf8String:_read()

  if self._is_le == true then
    self:_read_le()
  elseif self._is_le == false then
    self:_read_be()
  else
    error("unable to decide endianness")
  end
end

function Exif.ExifBody.Utf8String:_read_le()
  self.value = str_decode.decode(self._io:read_bytes_term(0, false, true, false), "UTF-8")
end

function Exif.ExifBody.Utf8String:_read_be()
  self.value = str_decode.decode(self._io:read_bytes_term(0, false, true, false), "UTF-8")
end