first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
# IFD Parse
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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`
```ts
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`.