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.