Sony/Nikon MakerNote Parser

Sony/Nikon MakerNote Parser

RawCull reads autofocus points and embedded JPEG locations directly from RAW metadata. This is implemented in RawParserKit, while the app consumes the parser through the vendor-neutral RawFormat protocol.

Source Map

AreaFiles
Format protocol/registryRawParserKit/Sources/RawParserKit/RawFormat.swift, RawFormatRegistry.swift
Sony parserSonyMakerNoteParser.swift, SonyRawFormat.swift
Nikon parserNikonMakerNoteParser.swift, NikonRawFormat.swift
Thumbnail/JPEG extractorsSonyThumbnailExtractor.swift, NikonThumbnailExtractor.swift, JPGSonyARWExtractor.swift, JPGNikonNEFExtractor.swift
DiagnosticsRawParserDiagnostics.swift, app RawFileDiagnostics.swift
App scan consumerActors/ScanFiles.swift
Focus-point normalizationRawCullCore/Sources/RawCullCore/FocusPointParser.swift
TestsRawParserKit/Tests/RawParserKitTests/, RawCullCore/Tests/RawCullCoreTests/FocusPointParserTests.swift

Vendor-Neutral Contract

App code usually does not call SonyMakerNoteParser or NikonMakerNoteParser directly. It resolves a format first:

guard let format = RawFormatRegistry.format(for: url) else { return }
let focus = format.focusLocation(from: url)

RawFormat also exposes thumbnail extraction, full embedded JPEG extraction, compression labels, and size-class thresholds. This keeps ScanFiles, thumbnail actors, and diagnostics mostly vendor-neutral.

Focus Location Shape

Both parsers return the same string shape:

imageWidth imageHeight focusX focusY

Example:

6000 4000 3000 1000

FocusPointParser.normalizedPoint(from:) converts that into a normalized CGPoint:

x = focusX / imageWidth
y = focusY / imageHeight

Invalid dimensions, malformed strings, and out-of-range points are rejected.

Sony ARW Structure

Sony ARW is TIFF-based. The focus point is found by walking TIFF/EXIF structures:

flowchart TD
    A["TIFF header"] --> B["IFD0"]
    B --> C["ExifIFD tag 0x8769"]
    C --> D["MakerNote tag 0x927C"]
    D --> E["Sony MakerNote IFD"]
    E --> F["FocusLocation tag 0x2027"]

The Sony parser also locates embedded JPEG candidates:

CandidateUse
thumbnailSmall embedded preview
previewPreferred scoring/zoom preview when available
fullJPEGLargest embedded JPEG candidate

For newer Sony files where ImageIO cannot expose a preview, focus scoring can use the parser’s embedded JPEG fallback.

Nikon NEF Structure

Nikon NEF is also TIFF-based, but the MakerNote has a Nikon Type-3 inner TIFF layout.

flowchart TD
    A["TIFF IFD0"] --> B["ExifIFD tag 0x8769"]
    B --> C["MakerNote tag 0x927C"]
    C --> D["Nikon signature + inner TIFF header"]
    D --> E["Nikon MakerNote IFD"]
    E --> F["AFInfo2 tag 0x00B7"]

The Nikon parser supports modern Z-series style AFInfo versions used by bodies such as Z9/Z8/Z7/Z6 class cameras. Tests also cover unsupported older AFInfo layouts returning nil instead of producing misleading coordinates.

For embedded JPEGs, Nikon parsing can walk TIFF SubIFDs when ImageIO does not expose the preview as a top-level image.

Diagnostics

RawFileDiagnostics.log(for:) is the developer entry point from the app. It reports:

  • file identity and size,
  • selected RawFormat,
  • scanned EXIF metadata,
  • ImageIO image count and per-index properties,
  • compression-code labels,
  • parser traces for embedded JPEG locations,
  • parser traces for AF focus location.

Diagnostics are intentionally read-only. They are useful when adding a camera body or debugging a RAW file that does not produce focus points or previews.

Tests

Test fileCovers
SonyMakerNoteParserTests.swiftSony focus and embedded JPEG parsing
NikonMakerNoteParserTests.swiftNikon focus and embedded JPEG parsing with synthetic NEF blobs
RawFormatRegistryTests.swiftExtension-to-format dispatch
SonyRAWJPEGCreatorTests.swiftSony RAW JPEG creation behavior
FocusPointParserTests.swiftNormalizing parser strings into points

The Nikon tests use synthetic binary blobs so parser offsets and AFInfo behavior can be checked without real camera files.

Adding A New RAW Format

  1. Add a new RawFormat conformer.
  2. Implement thumbnail extraction, full JPEG extraction, focus location, compression labels, and size thresholds.
  3. Register the conformer in RawFormatRegistry.all.
  4. Add parser/extractor diagnostics.
  5. Add package tests for registry dispatch and parser edge cases.
  6. Update scan/thumbnail docs if the new format changes behavior.

What To Check When Changing This Area

  • Parser failures should return nil or diagnostics, not crash.
  • Keep byte-offset code covered by synthetic tests where possible.
  • Do not let app code branch deeply on file extension when RawFormat can dispatch.
  • If focus-location string shape changes, update FocusPointParser and scan consumers together.
  • Embedded JPEG parser changes can affect thumbnails, zoom previews, and sharpness scoring.

Last modified July 30, 2026: new TechDocRawCull (29788c3)