Date a DNG whose IFDs follow its pixels: read the head and the tail

The scan reads the first 256 KB of a file for its metadata. A camera
writes its IFDs at the front, so that is the whole structure; the linear
DNG a merge writes puts its first IFD after the pixels, and rawler,
given the head alone, finds no decoder in it. The composite was
catalogued without a date and sorted to the very end of the grid, after
every dated photograph — which is where a panorama merged on the tablet
went unfound.

dr-decode's own TIFF reader now reads through a head and a tail at a
known offset; trailing_ifd says where the tail starts and
metadata_split reads the two together. The scan, when the head fails
and points beyond itself, fetches from the IFD to the end — kilobytes —
and dates the file from both. Tested against the writer's own output.
This commit is contained in:
2026-09-19 22:35:58 +02:00
parent 9d04ff2154
commit 48c4b403d2
5 changed files with 193 additions and 47 deletions
+20
View File
@@ -304,6 +304,26 @@ pub fn metadata(bytes: &[u8]) -> Result<Metadata, DecodeError> {
error::guarded("metadata", || metadata_unguarded(bytes))
}
/// TRACES: FR-CAT-5
/// Where a TIFF-shaped file keeps its first IFD, when the head handed to
/// [`metadata`] does not reach it — the linear DNG a merge writes puts its
/// IFDs after the pixels, and rawler, given the head alone, finds no
/// decoder in it. The caller fetches from this offset to the end and
/// reads the two ranges with [`metadata_split`].
pub fn trailing_ifd(head: &[u8]) -> Option<u64> {
locate::trailing_ifd(head)
}
/// TRACES: FR-CAT-5
/// [`metadata`] for a file read in two ranges: `head` from offset 0 and
/// `tail` from `tail_at`. The EXIF sub-IFD such a file wrote before its
/// pixels is in the head; the first IFD and its values are in the tail.
pub fn metadata_split(head: &[u8], tail: &[u8], tail_at: u64) -> Result<Metadata, DecodeError> {
error::guarded("metadata", || {
locate::tiff_metadata_split(head, tail, tail_at)
})
}
fn metadata_unguarded(bytes: &[u8]) -> Result<Metadata, DecodeError> {
use rawler::rawsource::RawSource;