mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 08:52:32 +00:00
* craft-tiff: keep shared child IFDs and every string of a multi-string ASCII value Two pointers to the same child IFD (two SubIFDs entries, or an Exif IFD shared by two directories) were treated as a loop and the second was dropped. Loops are now detected on the current parent path only; a shared IFD is parsed once and copied for later pointers, and each copy is charged its IFDs, entries and value bytes so max_ifds, max_entries_total and the value budget still hold. ASCII values keep every NUL-separated string (Value::as_strings); as_str and as_bytes return the first. Ported from storytold/lightcraft#3002. * craft-tiff: parse_with_status reports whether the whole IFD structure was read The reader is best effort: truncated tables, over-budget values, dangling pointers and unfollowed children are skipped silently. A caller that must tell a clean absence from something it could not inspect (a privacy scan for GPS data) gets the same tree plus a completeness flag. Also adds the PHOTOSHOP_IMAGE_RESOURCES and GPS destination tag constants. Ported from storytold/lightcraft#3766. On top of the shared-child change, a second pointer to an IFD already read counts as read, and a next pointer that loops back to the chain is incomplete. --------- Co-authored-by: Andrew Frahn <andrew@emmertex.com> Co-authored-by: ronibalthazar <61232335+ronibalthazar@users.noreply.github.com>
51 lines
3.5 KiB
Markdown
51 lines
3.5 KiB
Markdown
# craft-tiff
|
||
|
||
The TIFF / IFD reader and writer underneath raw decoding (DNG, CR2, NEF, ARW…) and Exif. Moved from
|
||
LightCraft's `lightcraft-tiff` as the first step of shared RAW support (craft-libs#783, #587). It depends
|
||
on no other Craft crate.
|
||
|
||
## Features
|
||
|
||
- Classic TIFF and BigTIFF, little- and big-endian (`II`/`MM`). Also accepts the TIFF-like magic numbers of
|
||
some raw formats (ORF `IIRO`/`IIRS`, RW2 `IIU`) when `ParseOptions::accept_raw_magic` is set (default).
|
||
- Every field type: BYTE, ASCII, SHORT, LONG, RATIONAL, SBYTE, UNDEFINED, SSHORT, SLONG, SRATIONAL, FLOAT,
|
||
DOUBLE, IFD, LONG8, SLONG8, IFD8.
|
||
- IFD chain + `SubIFDs` (330) + Exif (34665) / GPS (34853) / Interoperability (40965) pointer IFDs, resolved
|
||
into a tree (`Ifd::sub_ifds`, `exif`, `gps`, `interop`). `Entry::offset` records the absolute position of
|
||
each value (needed for maker notes and embedded blobs).
|
||
- `Tiff::parse_with_status` returns the best-effort tree with a completeness flag for callers that must
|
||
distinguish a clean absence from an IFD, entry, or pointer that could not be inspected.
|
||
- `parse_ifd_at(data, offset, order, base, bigtiff, opts)` parses an IFD whose offsets are relative to an
|
||
arbitrary base; `makernote::parse_makernote` knows the header / byte-order / offset-base conventions of
|
||
Canon, Nikon (types 1 and 3), Sony, Fujifilm, Olympus (old and new), Panasonic and Pentax notes.
|
||
- `image::ImageInfo` — width/height, bits, samples, compression, photometric, planar config, predictor,
|
||
sample format and the strip or tile chunk list (`chunks()`, `chunk_bytes()`); missing byte counts are
|
||
inferred.
|
||
- `directory_entries`, `entry_span`, `ParseOptions::budget_for` and `decode_value` are the reader's own rules
|
||
(directory layout and entry limit, which entries it reads, its value budget, value decoding), for callers that
|
||
must select entries exactly as `parse_ifd_at` would without decoding the whole directory (e.g. a raw decoder's
|
||
Sony `SR2Private` lookup).
|
||
- `TiffWriter` writes IFD chains with strips or tiles, `SubIFDs` and pointer IFDs, in either byte order,
|
||
classic or BigTIFF; `writer::rational` / `srational` approximate floats as rationals.
|
||
|
||
## Robustness
|
||
|
||
The reader never panics on malformed input (proptests feed random and mutated files): offsets are
|
||
bounds-checked; out-of-range or unknown-type entries are skipped; IFD loops are detected; IFD count,
|
||
nesting depth and entries per IFD are limited; at most 65,536 directory entry slots
|
||
(`ParseOptions::max_entries_total`) are accepted across the main IFD chain and all child IFDs, even when tables
|
||
overlap at distinct offsets, and an IFD past that bound ends the chain or is skipped like any other bad IFD after
|
||
the first; a next-IFD pointer is followed only when every declared entry was read, with the counts compared as
|
||
64-bit numbers (a BigTIFF declaring 4,294,967,297 entries but holding one can't pass for a whole directory on a
|
||
32-bit target); the total bytes of decoded values are capped at
|
||
`8 × input + 1 MiB` so a file cannot make us allocate far more than its own size. `parse_with_status` reports
|
||
truncated tables, skipped values, unresolved pointers and incomplete IFD chains without changing the parser's
|
||
best-effort tree.
|
||
|
||
## Sources
|
||
|
||
Public specifications only: TIFF 6.0 (Adobe, 1992), the TIFF 6.0 supplements (IFD type, SubIFDs), the
|
||
BigTIFF format description (AWare Systems), Adobe DNG Specification 1.7 (tag numbers), CIPA DC-008 Exif 2.32,
|
||
and the ExifTool tag-name documentation (maker-note header conventions, prose only). No third-party code
|
||
was consulted.
|