flutter_fast_exif_reader

pub package license

A blazing-fast, universal EXIF & MakerNote reader for Flutter with zero disk re-reads, powered by native C++17 FFI.

Designed for high-throughput photo pipelines, face clustering engines, gallery viewers, and batch scanners where extracting metadata from thousands of photos must take < 0.1ms per file.


🌟 Supported Platforms

Platform Support Backend Architecture
Windows βœ… Supported Native C++17 DLL FFI
Android βœ… Supported Native C++17 NDK .so FFI (armeabi-v7a, arm64-v8a, x86_64)

πŸš€ Key Features

  • ⚑ Ultra-Fast (< 0.1ms per file): Direct in-memory C++ pointer operations with 0 bytes written to disk.
  • πŸ“· Multi-Brand RAW & Image Format Support:
    • Canon: .CR3 (ISOBMFF multi-block CMT1–CMT4), .CR2
    • Nikon: .NEF, .NRW (Type 3 MakerNotes, Shutter Count, Lens Data)
    • Sony: .ARW, .SR2
    • Olympus / OM System: .ORF (IIRO / IIRS headers)
    • Fujifilm: .RAF (Embedded container & Film Simulation modes)
    • Panasonic / Lumix: .RW2 (IIU\0 headers)
    • Leica / Adobe / Ricoh / Pentax: .DNG, .PEF
    • Standard: .JPG, .JPEG, .JFIF
  • 🏷️ Deep Vendor MakerNotes Extraction:
    • Canon: Shooting mode, LensModel, CameraSettings, ShotInfo
    • Nikon: Total Shutter Count (MakerNote TotalShutterReleases), ISO settings, Lens Data
    • Sony: Sony Model ID, Creative Style, DRO, Long Exposure NR
    • Fujifilm: Film Simulation, Dynamic Range, Film Mode
    • Olympus & Panasonic: Equipment, Quality, Macro, Firmware
  • πŸ“ GPS Geolocation Telemetry: Latitude, Longitude, Altitude, TimeStamp.
  • πŸ”„ Dual Dart API:
    • Strongly-typed FastExifData with parsed getters.
    • 1:1 exact drop-in replacement for package:exif (readExifFromBytes, IfdTag, Ratio).
  • πŸ› οΈ C++ Photo Pipeline Interface: Direct fast_exif::Reader::extract and fast_exif::Reader::sniffFormat for native C++ pipelines.

πŸ“¦ Getting Started

Add flutter_fast_exif_reader to your pubspec.yaml:

dependencies:
  flutter_fast_exif_reader: ^0.1.0

πŸ“± Android Setup (GPS & Media Location Permissions)

Starting in Android 10 (API 29) and reinforced in Android 11–14, Android OS Scoped Storage automatically redacts GPS geolocation metadata from image streams (substituting 0 deg 0' 0.0000" for coordinates) unless the application declares and requests the ACCESS_MEDIA_LOCATION permission.

1. Declare Permissions in AndroidManifest.xml

Add the following to your android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required for unredacted GPS EXIF metadata access on Android 10+ (API 29..36) -->
    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

    <application ...>
        ...
    </application>
</manifest>

2. Request Runtime Permission

Before picking or opening photos on Android, request the permission using permission_handler:

import 'dart:io';
import 'package:permission_handler/permission_handler.dart';

Future<void> requestMediaLocationPermission() async {
  if (Platform.isAndroid) {
    await [
      Permission.photos,
      Permission.storage,
      Permission.accessMediaLocation,
    ].request();
  }
}

πŸ’‘ Usage

1. Modern Strongly-Typed API

import 'dart:typed_data';
import 'package:flutter_fast_exif_reader/flutter_fast_exif_reader.dart';

void parsePhoto(Uint8List imageBytes) {
  final FastExifData? exif = FlutterFastExifReader.readFromBytes(imageBytes);

  if (exif != null) {
    print('Camera: ${exif.make} ${exif.model}');
    print('Lens: ${exif.lensModel}');
    print('Settings: ${exif.fNumberString}, ${exif.exposureTimeString}, ${exif.isoString}');
    print('GPS: ${exif.latitude}, ${exif.longitude}');
    print('Process time: ${exif.processTimeMs.toStringAsFixed(2)} ms');
  }
}

2. 1:1 package:exif Drop-In Replacement

Migrate legacy code by simply updating the import statement with zero code changes:

import 'package:flutter_fast_exif_reader/flutter_fast_exif_reader.dart';

void readLegacyExif(List<int> bytes) async {
  final Map<String, IfdTag> tags = await readExifFromBytes(bytes);

  print('Make: ${tags['Image Make']}');
  print('Orientation: ${tags['Image Orientation']}');
  print('Shutter: ${tags['EXIF ExposureTime']}');
  print('Date: ${tags['EXIF DateTimeOriginal']}');
}

3. Native C++ Pipeline Integration

Include fast_exif_reader.h directly in your native processing pipeline:

#include "fast_exif_reader.h"

void processBuffer(const uint8_t* bytes, size_t len) {
    fast_exif::ExifInfo info;
    if (fast_exif::Reader::extract(bytes, len, info)) {
        printf("Camera: %s %s\n", info.make.c_str(), info.model.c_str());
        printf("Shutter: %s, Aperture: %s\n", info.getShutterSpeedString().c_str(), info.getApertureString().c_str());
        printf("Total Tags: %zu\n", info.tags.size());
    }
}

πŸ“Š Benchmark

Tested across 15 real camera RAW files (16.9MB - 85.4MB):

Format Camera Model legacy package:exif flutter_fast_exif_reader Speedup
Sony ARW Sony ILCE-7RM6 (75.2MB) 85.4 ms (97 tags) 0.20 ms (168 tags) 419x faster
Leica DNG Leica Q3 Mono (85.4MB) 23.5 ms (74 tags) 0.05 ms (73 tags) 508x faster
Leica DNG Leica M11-P (47.6MB) 5.3 ms (90 tags) 0.06 ms (89 tags) 87x faster
Sony ARW Sony ILCE-7M5 (19.4MB) 8.0 ms (97 tags) 0.10 ms (167 tags) 76x faster
Ricoh DNG Ricoh GR IV (30.4MB) 1.6 ms (62 tags) 0.05 ms (63 tags) 32x faster
Sony ARW Sony ILCE-7M2 (46.9MB) 4.7 ms (73 tags) 0.09 ms (137 tags) 52x faster
Nikon NEF Nikon Z 8 (30.5MB) 22.4 ms (145 tags) 0.09 ms (109 tags) 239x faster
Sony ARW Sony DSC-RX10M5 (25.4MB) 4.2 ms (96 tags) 0.10 ms (164 tags) 41x faster
Olympus ORF OM-5 Mark II (16.9MB) FAILED (0 tags) 0.04 ms (61 tags) Instant (vs Fail)
Canon CR3 Canon EOS R5m2 (45.9MB) FAILED (0 tags) 0.07 ms (104 tags) Instant (vs Fail)
Olympus ORF OM-5 Mark II (20.1MB) FAILED (0 tags) 0.04 ms (61 tags) Instant (vs Fail)
Fujifilm RAF Fujifilm X-T30 III (55MB) FAILED (0 tags) 0.11 ms (156 tags) Instant (vs Fail)
Panasonic RW2 Lumix S1M2 (26.9MB) FAILED (0 tags) 0.05 ms (88 tags) Instant (vs Fail)
Leica DNG Leica SL3-P (56.9MB) 3.9 ms (77 tags) 0.06 ms (77 tags) 66x faster
Panasonic RW2 Lumix S1RM2 (52.3MB) FAILED (0 tags) 0.06 ms (88 tags) Instant (vs Fail)

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.