tiff-reader 0.6.0

Pure-Rust, read-only TIFF/BigTIFF file decoder with no C dependencies
Documentation

geotiff-rust

tiff-core crates.io tiff-core docs.rs tiff-reader crates.io tiff-reader docs.rs tiff-writer crates.io tiff-writer docs.rs geotiff-core crates.io geotiff-core docs.rs geotiff-reader crates.io geotiff-reader docs.rs geotiff-writer crates.io geotiff-writer docs.rs

Pure-Rust TIFF/BigTIFF and GeoTIFF/COG readers and writers. No C libraries, no build scripts, no unsafe beyond memmap2.

Crates

Crate Description
tiff-core Shared TIFF types: ByteOrder, tags, sample traits, compression/predictor enums, and color-model metadata
tiff-reader TIFF/BigTIFF decoder with mmap, strip/tile reads, storage-domain reads, and explicit decoded pixel access
tiff-writer TIFF/BigTIFF encoder with streaming writes, compression, predictors, and BigTIFF
geotiff-core Shared GeoTIFF types: GeoKeyDirectory, CRS, GeoTransform, tag constants
geotiff-reader GeoTIFF reader with CRS/transform extraction, overview discovery, and optional HTTP COG access
geotiff-writer GeoTIFF/COG writer with fluent builder, tile-wise GeoTIFF writes, and overview generation

Reading

use geotiff_reader::GeoTiffFile;

let file = GeoTiffFile::open("dem.tif")?;
println!("EPSG: {:?}, bounds: {:?}", file.epsg(), file.geo_bounds());
let raster: ndarray::ArrayD<f32> = file.read_raster()?;

Use read_decoded_raster / read_decoded_window on GeoTiffFile and read_decoded_image / read_decoded_window on TiffFile when you want palette expansion or color-space conversion (for example palette TIFF, YCbCr, or CMYK) instead of storage-domain samples.

Use read_band / read_band_window on GeoTiffFile and TiffFile when you only need one storage-domain band as a [rows, cols] array.

Writing

use geotiff_writer::{GeoTiffBuilder, Compression};
use ndarray::Array2;

let data = Array2::<f32>::zeros((256, 256));
GeoTiffBuilder::new(256, 256)
    .epsg(4326)
    .pixel_scale(0.01, 0.01)
    .origin(-180.0, 90.0)
    .nodata("-9999")
    .compression(Compression::Deflate)
    .write_2d("output.tif", data.view())?;

For separate-planar multiband output, set planar_configuration(PlanarConfiguration::Planar) on ImageBuilder or GeoTiffBuilder.

Streaming tile writes

use geotiff_writer::GeoTiffBuilder;
use ndarray::Array2;

let builder = GeoTiffBuilder::new(512, 512)
    .tile_size(256, 256)
    .epsg(4326);
let mut writer = builder.tile_writer_file::<f32, _>("large.tif")?;
for (x, y, tile) in tiles {
    writer.write_tile(x, y, &tile.view())?;
}
writer.finish()?;

COG with overviews

use geotiff_writer::{GeoTiffBuilder, CogBuilder, Resampling, Compression};
use ndarray::Array2;

let data = Array2::<u8>::zeros((1024, 1024));
CogBuilder::new(
    GeoTiffBuilder::new(1024, 1024)
        .tile_size(256, 256)
        .compression(Compression::Deflate)
        .epsg(4326)
)
.overview_levels(vec![2, 4, 8])
.resampling(Resampling::Average)
.write_2d("output.tif", data.view())?;

For multi-band COG output, use write_3d/write_3d_to or write_tile_3d with bands(...) and optional planar_configuration(PlanarConfiguration::Planar).

Features

Read

  • Classic TIFF and BigTIFF
  • Little-endian and big-endian byte orders
  • Strip and tile data access with windowed reads
  • Chunky and separate planar sample layouts
  • Full-raster and windowed single-band reads, optimized for separate-planar rasters
  • Compression: Deflate, LZW, PackBits, LERC, LERC+DEFLATE, JPEG (optional), ZSTD (optional), LERC+ZSTD (optional)
  • Bounded IFD parsing and block decompression budgets for untrusted input
  • Parallel decompression via Rayon
  • Storage-domain typed sample reads via read_* / read_*_samples
  • Explicit decoded pixel reads via read_decoded_* for standard TIFF color models, including palette expansion, YCbCr/CMYK conversion, and sub-byte grayscale/palette decode
  • Structured photometric/color-model metadata: palette ColorMap, ExtraSamples, CMYK, and YCbCr
  • GeoKey directory, structured CRS metadata (projected, geographic, geocentric, vertical, compound), transforms, NoData
  • Overview discovery from both reduced-resolution top-level IFDs and recursive base-image SubIFD-backed overview trees
  • Optional HTTP range-backed remote COG access

Write

  • Classic TIFF and BigTIFF with auto-detection
  • Strip and tile layouts
  • Compression: Deflate, LZW, JPEG (optional), LERC, LERC+DEFLATE, ZSTD (optional), LERC+ZSTD (optional)
  • Predictors: horizontal differencing, floating-point byte interleaving
  • Chunky and separate planar multi-band layouts and all sample types (u8 through f64)
  • Photometric/color-model tags: palette ColorMap, ExtraSamples alpha, CMYK (Separated + InkSet), and YCbCr 4:4:4
  • Streaming tile-by-tile GeoTIFF writes for large rasters
  • GeoTIFF metadata: GeoTIFF 1.1 key-directory emission, projected/geographic/geocentric/vertical compound CRS keys, pixel scale, origin, affine transforms, NoData
  • COG output with GDAL-compatible ghost-area metadata, overview generation (nearest-neighbor, average), top-level or SubIFD-backed overview IFDs, and multi-band chunky/planar rasters
  • Disk-backed tile-wise COG assembly via CogTileWriter (base tiles are staged in a temporary raw tile store before final emission)

Codec Notes

JPEG-in-TIFF write uses standard compression code 7 with full JPEG interchange streams per strip/tile. The supported interoperable layouts are single-band chunky output and multi-band separate-planar output, which keeps TIFF, GeoTIFF, and COG files compatible with GDAL/libtiff without requiring TIFF-side shared JPEGTables.

TIFF LERC writing records the registered LERC2 2.4 parameter version used by GDAL/libtiff. If the encoder produces a different LERC2 container version, the writer rejects the block before writing incompatible TIFF metadata.

Robustness Notes

TiffFile::open_with_options, from_bytes_with_options, and from_source_with_options accept OpenOptions with ParseBudgets for bounding IFD chain length, tag counts, and metadata payload bytes. GeoTiffFile exposes the same settings as GeoTiffOpenOptions, and HTTP COG opens pass them through HttpOpenOptions::tiff_options. The reader also derives encoded strip/tile read limits and decompressed output limits from the raster layout before reading block payloads.

Feature flags

Flag Default Description
local yes Local file reading via tiff-reader (geotiff-reader)
rayon yes Parallel strip/tile decompression (tiff-reader, geotiff-reader)
jpeg yes JPEG-in-TIFF read/write support (tiff-reader, tiff-writer)
zstd yes ZSTD compression, including TIFF LERC+ZSTD read/write support (tiff-reader, tiff-writer)
cog no HTTP range-backed remote COG open (geotiff-reader)

Testing

cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings

Reference-library parity tests are included for tiff-reader and geotiff-reader. They compare this workspace against GDAL/libtiff when those tools are available locally; otherwise they self-skip. Lossless codecs use exact byte and hash parity. The JPEG fixture uses a strict bounded-delta check because compliant decoders can differ by +/-1 in a small number of samples.

For a reproducible reference environment, run the Docker harness:

./scripts/run-reference-parity.sh

For reference comparisons and current benchmark results against GDAL/libtiff, see docs/benchmark-report.md.

For the workspace release order and package verification notes, see docs/publishing.md.

License

MIT OR Apache-2.0