function info = __tiff_imfinfo__ (filename)
  tif = Tiff (filename);
  dir_count = tif.numberOfDirectories ();

  info = struct ();

  if __have_feature__ ("MAGICK")
    ## Obtain data from magick if available (as it handles EXIF tags)
    info = __imfinfo__ (filename);
  else
    ## Otherwise just obtain common tags and ignore EXIF tags
    [file_stat, err, msg] = stat (filename);
    if err > 0
      error ("imfinfo: Failed to access file: %s", msg);
    endif

    for dir_idx = 1:dir_count
      info(dir_idx).Filename = filename;
      info(dir_idx).FileModDate = strftime ("%e-%b-%Y %H:%M:%S",
                                  localtime (file_stat.ctime));
      info(dir_idx).FileSize = file_stat.size;
      info(dir_idx).Format = "tif";
      info(dir_idx).FormatVersion = "";
    endfor
  endif

  for dir_idx = 1:dir_count
    tif.setDirectory (dir_idx);

    ## Read the file signature (first 4 bytes)
    a = fopen (filename);
    info(dir_idx).FormatSignature = reshape (fread (a, 4, "uint8"), [1, 4]);
    fclose (a);

    info(dir_idx).Width = tif.getTag (Tiff.TagID.ImageWidth);
    info(dir_idx).Height = tif.getTag (Tiff.TagID.ImageLength);

    info(dir_idx).SamplesPerPixel = tif.getTag (Tiff.TagID.SamplesPerPixel);
    info(dir_idx).BitsPerSample = tif.getTag (Tiff.TagID.BitsPerSample);
    info(dir_idx).BitDepth = info(dir_idx).SamplesPerPixel ...
                             * info(dir_idx).BitsPerSample;
    
    planar = tif.getTag (Tiff.TagID.PlanarConfiguration);
    if planar == Tiff.PlanarConfiguration.Chunky
      info(dir_idx).PlanarConfiguration = "Chunky";
    elseif planar == Tiff.PlanarConfiguration.Separate
      info(dir_idx).PlanarConfiguration = "Separate";
    else
      info(dir_idx).PlanarConfiguration = "Unrecognized";
    endif

    info(dir_idx).Colormap = [];

    photometrics = [Tiff.Photometric.MinIsBlack, Tiff.Photometric.MinIsWhite,...
                    Tiff.Photometric.RGB, Tiff.Photometric.Palette,...
                    Tiff.Photometric.Separated];
    color_types = {"grayscale", "grayscale", "truecolor", "indexed", "cmyk"};
    photometric_strs = {"BlackIsZero", "WhiteIsZero", "RGB",...
                        "RGB Palette", "CMYK"};
    
    photometric = get_tag_defaulted (tif, Tiff.TagID.Photometric,
                                     Tiff.Photometric.MinIsBlack);
    idx = (photometric == photometrics);
    if ! any (idx)
      info(dir_idx).ColorType = "undefined";
      info(dir_idx).PhotometricInterpretation = "undefined";
    else
      info(dir_idx).ColorType = color_types{idx};
      info(dir_idx).PhotometricInterpretation = photometric_strs{idx};
      if (photometric == Tiff.Photometric.Palette)
        info(dir_idx).Colormap ...
          = get_tag_defaulted (tif, Tiff.TagID.ColorMap, []);
      endif
    endif
    
    ## FIXME: implement isBigEndian
    # if (tif.isBigEndian ())
    #   info(dir_idx).ByteOrder = "big-endian";
    # else
    #   info(dir_idx).ByteOrder = "little-endian";
    # endif
    
    info(dir_idx).NewSubFileType ...
      = get_tag_defaulted (tif, Tiff.TagID.SubFileType,
                                Tiff.SubFileType.Default);


    info(dir_idx).Compression = "unrecognized";
    compressions = fieldnames (Tiff.Compression);
    compression = tif.getTag (Tiff.TagID.Compression);
    for comp_idx = 1:numel (compressions)
      if (compression == Tiff.Compression.(compressions{comp_idx}))
        info(dir_idx).Compression = compressions{comp_idx};
        break;
      endif
    endfor

    info(dir_idx).TileLength ...
      = get_tag_defaulted (tif, Tiff.TagID.TileLength, []);
    info(dir_idx).TileWidth ...
      = get_tag_defaulted (tif, Tiff.TagID.TileWidth, []);
    
    info(dir_idx).TileOffsets ...
      = get_tag_defaulted (tif, Tiff.TagID.TileOffsets, []);
    info(dir_idx).TileByteCounts ...
      = get_tag_defaulted (tif, Tiff.TagID.TileByteCounts, []);
    
    info(dir_idx).RowsPerStrip ...
      = get_tag_defaulted (tif, Tiff.TagID.RowsPerStrip, []);
    info(dir_idx).StripOffsets ...
      = get_tag_defaulted (tif, Tiff.TagID.StripOffsets, []);
    info(dir_idx).StripByteCounts ...
      = get_tag_defaulted (tif, Tiff.TagID.StripByteCounts, []);
    
    info(dir_idx).XResolution ...
      = get_tag_defaulted (tif, Tiff.TagID.XResolution, []);
    info(dir_idx).YResolution ...
      = get_tag_defaulted (tif, Tiff.TagID.YResolution, []);

    info(dir_idx).ResolutionUnit = "Inch";
    try
      resunit = tif.getTag (tiff.TagID.ResolutionUnit);
      if (resunit == Tiff.ResolutionUnit.None)
        info(dir_idx).ResolutionUnit = "None";
      elseif (resuint == Tiff.ResolutionUnit.Centimeter)
        info(dir_idx).ResolutionUnit = "Centimeter";
      endif
    end_try_catch

    info(dir_idx).Orientation ...
      = get_tag_defaulted (tif, Tiff.TagID.Orientation,
                           Tiff.Orientation.TopLeft);

    info(dir_idx).FillOrder ...
      = get_tag_defaulted (tif, Tiff.TagID.FillOrder, 1);
    
    gray_unit = get_tag_defaulted (tif, Tiff.TagID.GrayResponseUnit, 2);
    info(dir_idx).GrayResponseUnit = 1 / (10 ^ gray_unit);
    
    info(dir_idx).MinSampleValue ...
      = get_tag_defaulted (tif, Tiff.TagID.MinSampleValue, 0);
    info(dir_idx).MaxSampleValue ...
      = get_tag_defaulted (tif, Tiff.TagID.MaxSampleValue,
                           2^info(dir_idx).BitsPerSample - 1);
    info(dir_idx).MinSampleValue ...
      = repmat (info(dir_idx).MinSampleValue,
                [1, info(dir_idx).SamplesPerPixel]);
    info(dir_idx).MaxSampleValue ...
      = repmat (info(dir_idx).MaxSampleValue,
                [1, info(dir_idx).SamplesPerPixel]);

    info(dir_idx).Thresholding ...
      = get_tag_defaulted (tif, Tiff.TagID.Thresholding,
                           Tiff.Thresholding.BiLevel);

    ## FIXME: implement getDirectoryOffset method
    # info(dir_idx).Offset = tif.getDirectoryOffset ();

    info(dir_idx).ImageDescription ...
      = get_tag_defaulted (tif, Tiff.TagID.ImageDescription, "");
    
    info(dir_idx).Photoshop ...
     = get_tag_defaulted (tif, Tiff.TagID.Photoshop, []);
    
    info(dir_idx).XMP ...
      = get_tag_defaulted (tif, Tiff.TagID.XMP, "");
  endfor
endfunction

function tag_val = get_tag_defaulted (tif, tag, default_val)
  tag_val = default_val;
  try
    tag_val = tif.getTag (tag);
  end_try_catch
endfunction
