Files
skills/docs/media/image/exif/src/ifd/parse.md
2026-05-29 08:20:56 +01:00

3.2 KiB

IFD Parse

(no submodules)

Purpose

Implements TIFF IFD (Image File Directory) parsing, the core data structure used by EXIF to store metadata. Each IFD entry is a 12-byte record containing a tag number, data type, count, and either an inline value or an offset to data elsewhere in the TIFF stream. This module handles all 12 standard TIFF data types and provides typed accessor helpers for downstream EXIF, GPS, and sub-IFD readers.

Features

  • Parses all 12 TIFF primitive types: BYTE, ASCII, SHORT, LONG, RATIONAL, SBYTE, UNDEFINED, SSHORT, SLONG, SRATIONAL, FLOAT, DOUBLE
  • Inline vs. offset-stored value selection based on total data size
  • Graceful handling of malformed entries (skips rather than throwing)
  • Typed accessors: getTagNumber, getTagString, getTagRational, getTagRationalArray
  • Bounds-checked with ExifError(PARSE_ERROR) on structural violations

Usage: Basic Example

import { ByteReader } from "@suaveplan/exif/binary";
import { parseIfd, getTagString } from "@suaveplan/exif";

const reader = new ByteReader(tiffBuffer, "BE");
const ifd0 = parseIfd(reader, 8);

const make = getTagString(ifd0, 0x010F);  // Make tag
const model = getTagString(ifd0, 0x0110); // Model tag
console.log(`${make} ${model}`);

Usage: Advanced Example

import { ByteReader } from "@suaveplan/exif/binary";
import { parseIfd, getTagNumber, getTagRational } from "@suaveplan/exif";

const reader = new ByteReader(tiffBuffer, "LE");

// Parse IFD0 then follow ExifIFD pointer
const ifd0 = parseIfd(reader, 8);
const exifIfdOffset = getTagNumber(ifd0, 0x8769); // ExifIFD pointer

if (exifIfdOffset !== undefined) {
  const exifIfd = parseIfd(reader, exifIfdOffset);
  const exposureTime = getTagRational(exifIfd, 0x829A);
  const fNumber = getTagRational(exifIfd, 0x829D);
}

API Reference

IfdEntry

interface IfdEntry {
  tag: number;
  type: number;
  count: number;
  value: unknown;
}

parseIfd(reader: ByteReader, ifdOffset: number): Map<number, IfdEntry>

Parses the IFD at ifdOffset within reader. Returns a Map<tag, IfdEntry>. Throws ExifError(PARSE_ERROR) if the IFD count field extends beyond the buffer. Malformed individual entries are silently skipped.

getTagNumber(entries: Map<number, IfdEntry>, tag: number): number | undefined

Extracts a SHORT or LONG tag as a number.

getTagString(entries: Map<number, IfdEntry>, tag: number): string | undefined

Extracts an ASCII tag as a string.

getTagRational(entries: Map<number, IfdEntry>, tag: number): Rational | undefined

Extracts a single RATIONAL or SRATIONAL tag.

getTagRationalArray(entries: Map<number, IfdEntry>, tag: number): Rational[] | undefined

Extracts an array of RATIONAL values (e.g. GPS coordinate triples where each axis is three RATIONALs for degrees/minutes/seconds).

Implementation Notes

Inline storage applies when typeSize * count <= 4: the value is packed into the 4-byte value/offset field of the IFD entry. For larger values, the field contains a file offset to the actual data. ASCII strings have trailing nulls stripped. The UNDEFINED type (7) returns a raw Uint8Array. Sign extension for SBYTE uses the left-shift/right-shift trick << 24 >> 24.