All formats

LibTiff.Net

LanguageC# (.NET Standard 2.0, .NET Framework 4.6.1+, .NET 5+)
LicenseNew BSD
Repositorygithub.com/BitMiracle/libtiff.net
DocumentationAPI reference and samples
Depends onnothing (pure managed code)
Read / writeyes / yes, multi-page, strips and tiles, all bit depths, LZW, Deflate, PackBits, JPEG, CCITT Group 3/4

LibTiff.Net is a line-by-line port of libtiff to C#. The API mirrors libtiff: Tiff.Open, GetField, SetField, ReadScanline, WriteScanline, ReadDirectory, WriteDirectory. Code and documentation written for libtiff translate almost directly. It runs everywhere .NET runs, with no native libraries.

It works with raw samples and tags, not with a bitmap object: you get bytes or packed RGBA integers and convert them to your image type. It doesn’t read BigTIFF. Releases are infrequent, but the code is stable.

Installation#

Shell
dotnet add package BitMiracle.LibTiff.NET

Basic usage: read a TIFF as RGBA#

C#
using System;
using BitMiracle.LibTiff.Classic;

using Tiff tif = Tiff.Open("in.tif", "r");
if (tif == null) {
    Console.Error.WriteLine("cannot open file");
    return;
}
int width = tif.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int height = tif.GetField(TiffTag.IMAGELENGTH)[0].ToInt();

int[] raster = new int[width * height];
if (!tif.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT)) {
    Console.Error.WriteLine("cannot decode image");
    return;
}
int p = raster[0];
Console.WriteLine($"{width} x {height}, first pixel R={Tiff.GetR(p)} G={Tiff.GetG(p)} B={Tiff.GetB(p)} A={Tiff.GetA(p)}");

How to write an RGB TIFF with LZW compression#

C#
using BitMiracle.LibTiff.Classic;

static void WriteRgb(string path, byte[] pixels, int width, int height) {
    using Tiff tif = Tiff.Open(path, "w");
    tif.SetField(TiffTag.IMAGEWIDTH, width);
    tif.SetField(TiffTag.IMAGELENGTH, height);
    tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);
    tif.SetField(TiffTag.BITSPERSAMPLE, 8);
    tif.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
    tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
    tif.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT);
    tif.SetField(TiffTag.COMPRESSION, Compression.LZW);
    tif.SetField(TiffTag.ROWSPERSTRIP, tif.DefaultStripSize(0));

    byte[] row = new byte[width * 3];
    for (int y = 0; y < height; y++) {
        Buffer.BlockCopy(pixels, y * width * 3, row, 0, row.Length);
        tif.WriteScanline(row, y);
    }
}

How to iterate the pages of a multi-page TIFF#

C#
using Tiff tif = Tiff.Open("scan.tif", "r");
short pages = tif.NumberOfDirectories();
for (short i = 0; i < pages; i++) {
    tif.SetDirectory(i);
    int w = tif.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
    int h = tif.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
    Console.WriteLine($"page {i}: {w} x {h}");
}

How to write a multi-page TIFF#

Write each page’s tags and rows, then call WriteDirectory():

C#
using Tiff tif = Tiff.Open("pages.tif", "w");
for (int page = 0; page < pageCount; page++) {
    tif.SetField(TiffTag.SUBFILETYPE, FileType.PAGE);
    tif.SetField(TiffTag.PAGENUMBER, page, pageCount);
    // ... set IMAGEWIDTH, IMAGELENGTH, BITSPERSAMPLE etc. and WriteScanline each row ...
    tif.WriteDirectory();
}

How to read tags#

GetField returns null when the tag is missing:

C#
FieldValue[] software = tif.GetField(TiffTag.SOFTWARE);
if (software != null)
    Console.WriteLine($"Software: {software[0].ToString()}");

FieldValue[] compression = tif.GetFieldDefaulted(TiffTag.COMPRESSION);
Console.WriteLine($"Compression: {(Compression)compression[0].ToInt()}");

FieldValue[] xres = tif.GetField(TiffTag.XRESOLUTION);
if (xres != null)
    Console.WriteLine($"X resolution: {xres[0].ToFloat()}");

Tips#