TIFF codec support
August 19, 2026 ยท View on GitHub
This document is the capability contract for PureJsImage's first-party TIFF codec. A checked item is implemented and covered by tests or pinned fixtures. An unchecked item is planned and must remain an explicit unsupported operation until implemented.
Decode
Container and image layout
- Classic TIFF 6.0 files with 32-bit offsets
- Little-endian (
II) and big-endian (MM) byte order - Explicit top-level frame selection, defaulting to the first image
- Frame counting through the top-level IFD chain
- Strip-based images
- Chunky pixel layout (
PlanarConfiguration=1) - Planar pixel layout (
PlanarConfiguration=2) - Region decode without materializing a full RGBA source image
- Orientation metadata (
Orientationvalues 1-8) - Tiled images, including padded edges and legacy tile tables stored in strip tags
- BigTIFF with validated 64-bit IFDs, counts, values, and offsets
- Classic TIFF and BigTIFF SubIFD traversal with cycle, offset, and global directory-count validation plus alias-safe shared-directory reuse
- Reduced-resolution pyramid selection through
resolutionLevel, including nested and chained SubIFDs - Bounded validator-protected HTTP range input with request deduplication, an LRU byte cap, and selective COG-style tile reads
- Public bounded COG structural inspection reporting TIFF/BigTIFF, IFD/SubIFD layout, overview and tile dimensions, compression and sample layout, and likely remote-read issues
Document, raster, and profile APIs
- Public
openTiffDocument()entry with stable top-level and SubIFD directory objects - Bounded typed
getTag()reads, payload-freegetTagInfo()metadata, plus per-directory display and native raster decoders - Configurable physical-segment count, segment-table construction-peak, and per-segment encoded-byte limits rejected before oversized payload reads
- Native-precision planar or interleaved N-channel
RasterBlockoutput without implicit RGB conversion - Explicit ordinary TIFF
ScientificDocumentreader with native precision/components, contiguous compatible-page series, labeled page axes, SubIFD resolution levels, bounded selected metadata, identities, cancellation, and region reads - Expose GeoTIFF CRS, six-parameter affine and invertible inverse, model bounds, raster interpretation, scalar or component nodata, and JSON-safe source metadata on ordinary scientific TIFF descriptors and SubIFD levels while keeping reads in raster pixel coordinates
- Explicit
rasterToPixels()display conversion with declared per-channel ranges - First-party GeoTIFF model, coordinate conversion, bounding-box, GeoKey, GDAL metadata, and nodata helpers
- OME-TIFF Z/C/T datasets with validated dimension orders, channel metadata, physical pixel sizes, and explicit or implicit
TiffDatamappings - OME reduced-resolution SubIFD plane selection and separate-channel plane assembly
- Explicit bounded scientific plane display mapping with declared or sampled ranges, scales, palettes, and optional scalar-surface relief
- Deterministic TIFF profile registry with detector-failure isolation and equal-priority ambiguity rejection
- First-party standard TIFF, ImageJ, DigitalMicrograph, FEI SFEG/Helios, and Zeiss SEM calibration profiles with normalized origins and steps, per-axis evidence, structured spatial/intensity status, format-default resolution evidence, bounded raw metadata, normalized acquisition fields, strict private-tag detection, and non-fatal validation warnings
- Generic bounded
WholeSlideImagelevels, region reads, associated images, and physical metadata - First-party Aperio SVS pyramid, associated-image, MPP, objective, and JPEG 2000 tile integration
- Explicit Aperio ScientificDocument reader with configurable large-source WSI limits, calibrated format-consistent pyramid levels, lightweight ICC presence/length metadata, distinct associated-image datasets, identities, cancellation, release forwarding, and bounded local or HTTP Range region reads
- Separately compiled Leica SCN single-area example using only published package imports
- Automatic display semantics for arbitrary scientific multiband data
- Multi-area Leica scene composition and additional vendor-specific whole-slide profiles
Pixel formats
- 1-, 2-, 4-, 6-, 8-, 10-, 12-, 14-, 16-, 24-, 32-, and 64-bit grayscale (
WhiteIsZeroandBlackIsZero) - 1-, 2-, 4-, 8-, and 16-bit indexed color with a TIFF color map
- 2-, 4-, 8-, 10-, 12-, 14-, 16-, 24-, 32-, and 64-bit RGB
- 8-bit grayscale plus alpha
- 8-bit RGBA
- Associated and unassociated alpha samples
- 16-bit grayscale-alpha and RGBA
- Unsigned four-component CMYK /
Separated, includingDotRange - Five-sample unsigned CMYK plus associated or unassociated alpha
- Chunky subsampled YCbCr and planar 1x1 YCbCr
- 8-/16-bit RGB, indexed, and JPEG-backed ICC-profile color conversion
- Signed 8- and 16-bit grayscale and RGB with raw numeric preservation
- IEEE float16, float32, and float64 grayscale and RGB with raw numeric preservation
- Signed 8-/16-bit and float16/float32/float64 CMYK with deterministic display-range normalization and direct RGB output
- Unsigned 24-/32-bit samples preserved in
gray32/rgb32blocks and unsigned 64-bit samples preserved ingray64/rgb64blocks - Deterministic display conversion using
SMinSampleValue/SMaxSampleValueor documented full-type defaults without an 8-/16-bit raw intermediate - SGILog luminance reconstructed in native
yf32CIE Y blocks with deterministic gamma-2 display conversion - SGILog24 and SGILog32 color reconstructed in native
xyzf32CIE XYZ blocks with CCIR 709 display conversion - TIFF 6 8-bit CIELab L* and Lab* display conversion from D65 to sRGB with unassociated alpha
- Embedded CMYK lut16 A2B0 ICC-profile conversion with profile precedence over numeric CMYK display
Compression and prediction
- Uncompressed strips
- PackBits
- LZW with standard MSB/early-change and legacy LSB/late-change code packing, including bounded final-strip padding
- Deflate / Adobe Deflate
- CCITT Group 4 (
T6) bilevel fax, including multi-strip andFillOrder=2input - CCITT Modified Huffman and Group 3 (
T4) fax, including mixed 1D/2D rows and legacy 1D rows without EOL markers - Horizontal differencing predictor for uniform 2-, 4-, 6-, 8-, 10-, 12-, 14-, 16-, 24-, 32-, and 64-bit integer or floating-point samples
- Floating-point predictor 3 byte unshuffle and accumulation for float16, float32, and float64 samples
- JPEG-in-TIFF (
Compression=7) complete and abbreviated streams withJPEGTables - Native scientific raster for JPEG-in-TIFF three-band YCbCr-converted RGB and four-band photometric RGB ExtraSamples=0, including JPEGTables, edge tiles, SubIFD overviews, region reads, and explicit unsupported photometric/planar errors
- Old-style JPEG (
Compression=6) complete interchange streams, multi-strip scans, omittedRowsPerStrip, and baseline Q/DC/AC table reconstruction - Aperio JPEG 2000 (
Compression=33003YCbCr and33005MCT) tiles through the reusable codestream decoder - WebP-in-TIFF (
Compression=50001) through explicitcreateTiffCodec({ embeddedCodecs: [webpCodec] })composition - SGILog (
Compression=34676) and SGILog24 (Compression=34677) with bounded row RLE and exact segment sizing - Zstandard (
Compression=50000) through the reusable first-party bounded decompressor - LERC and LERC plus Deflate (
Compression=34887) with bounded first-party LERC2 decoding - Generated compression audit distinguishing fully COG-tested, weakly COG-tested, recognized unsupported, and not-implemented assignments, with display-versus-native-raster boundaries
- Deterministic tiled Classic TIFF, BigTIFF, SubIFD, Deflate, LZW, PackBits, JPEG-in-TIFF three-band and four-band, RGB/RGBA, nodata, and rotated-affine COG fixtures
- ThunderScan and other extension compressions
- Reversed bit fill order (
FillOrder=2) normalized per strip or tile before prediction
Conformance evidence
The 2026-08-10 154-file Imazen TIFF baseline records 148 passes, 2 structured UNSUPPORTED_OPERATION results, and 4 safely rejected robustness inputs, with zero decode failures, invalid outputs, raw exceptions, timeouts, crashes, or out-of-memory results. Each file runs in an isolated worker with a 30-second timeout and 512 MiB heap limit. A pass means metadata inspection, TIFF-to-PNG decode, PNG reopen, and output-dimension validation all completed; it is not by itself an exact-pixel oracle.
Exact pixel behavior is covered separately by pinned focused fixtures and independent ImageMagick/LibTIFF, tifffile/imagecodecs, Esri LERC, libwebp, GeoTIFF.js, and OpenSlide comparisons. The TIFF conformance worker explicitly composes WebP; the default TIFF codec remains independent. Unsupported totals record only the first boundary reached.
The generated docs/tiff-cog-compatibility.md matrix records every audited compression assignment, its display/native-raster boundary, deterministic COG fixtures, structural inspection contract, and simulated-range viewport benchmark.
Remaining priorities
TIFF 6 CIELab converts the format's D65-referenced L*, a*, and b* samples directly to sRGB with explicit clipping and round-to-nearest output. CMYK lut16 A2B0 ICC profiles transform bounded rows and take precedence over numeric CMYK conversion. LogL and LogLuv reconstruct native CIE Y or XYZ float32 blocks. SGILog RLE scratch remains one row; decoded segment state is bounded to the current strip or tile.
Follow-on priorities
- Add alternate TIFF encoder compression and pixel profiles only when their complete contracts exist, prioritizing PackBits/LZW, grayscale, or 16-bit channels from demonstrated demand.
- Keep implicit display conversion for generic multiband data unsupported; use native
RasterBlockoutput or require an explicit channel/range mapping. - Keep ThunderScan unsupported: the current corpus fixture is truncated by three rows and LibTIFF independently rejects it.
Encode
Implemented target
- Classic little-endian TIFF and BigTIFF with automatic or explicit container selection
- Chunky 8-bit RGB and RGBA with unassociated alpha metadata
- Independently Deflate-compressed strips or tiles with horizontal differencing (
Predictor=2) - Automatic roughly 128 KiB strips or explicit
rowsPerStrip; tile dimensions validated as multiples of 16 - Bounded current-segment pixel and predictor scratch without a full uncompressed frame
- Compatible RGB ICC profile writing when explicitly preserved
- Multi-page top-level IFD chains and reduced-resolution SubIFD pyramids
- Strict
compression,predictor,layout,compressionLevel,format, strip, and tile options on the public encode APIs
Planned
- Uncompressed, PackBits, LZW, JPEG, and other compression profiles
- Grayscale and palette encoding
- 16-bit channel encoding
- Associated-alpha encoding
- EXIF, XMP, resolution, and application metadata preservation
Correctness and safety contract
- Validate IFD extents, field types, counts, and offset arithmetic before reading or allocating
- Traverse top-level and SubIFD graphs with bounded directory counts, cycle rejection, and alias-safe shared-directory reuse
- Expose stable IFD offsets and graph lookup, bounded defensive-copy source reads, per-directory immutable tag caching with per-call limits, and typed explicit profile opening through public TIFF APIs
- Validate strip/tile counts, byte ranges, decoded sizes, and predictor boundaries
- Bound decompression output to the declared strip or tile geometry
- Preflight aggregate decoded-segment, output-block, and predictor-scratch peaks before direct TIFF segment reads; stream tiled Aperio regions one native intersection at a time
- Reject unsupported photometric interpretations, sample formats, and compressions explicitly, naming both compression ID and assigned name
- Verify decoded pixels against pinned LibTIFF fixtures
- Verify packed 10-, 12-, and 14-bit output exactly at native 16-bit depth against ImageMagick/LibTIFF
- Verify low packed depths, CMYK-alpha, and lossless WebP-in-TIFF output exactly against ImageMagick/LibTIFF
- Verify explicitly composed lossy WebP-in-TIFF against the independently validated WebP decoder contract
- Verify float16, float32, float64, and floating Predictor 3 display output exactly against ImageMagick/LibTIFF
- Verify signed integer and IEEE floating-point raw values at native precision in both byte orders
- Verify unsigned 24-/32-bit display output exactly against ImageMagick/LibTIFF and unsigned 64-bit raw values exactly above JavaScript's safe-integer range
- Verify CCITT Group 4 output against independently encoded ImageMagick/LibTIFF fixtures
- Verify TIFF 6 CIELab samples exactly against an independent colour-science oracle and CMYK lut16 ICC output within one 8-bit code value of ImageMagick/LittleCMS
- Verify FillOrder 2 packed strip and padded edge-tile output against independently written fixtures decoded by ImageMagick/LibTIFF
- Verify tiled LZW and BigTIFF output against independently encoded ImageMagick/LibTIFF fixtures
- Verify standalone Zstandard raw, RLE, compressed, multi-block, checksum, repeated-table, repeated-offset, and hostile-input behavior against independently generated reference frames
- Verify selected classic TIFF and BigTIFF pyramid levels independently and decode no unselected pixel segments
- Verify 16-bit ColorMap scaling, floating-point CMYK display, and signed CMYK sample reconstruction exactly against independent ImageMagick and tifffile oracles
- Verify canonical Deflate-predicted RGB and RGBA output exactly after ImageMagick/LibTIFF reopen
- Verify first-party LERC and LERC-plus-Deflate pixels against the independent Esri decoder and reject corrupt masks, headers, dimensions, checksums, and missing TIFF metadata
- Verify bounded HTTP range caching, resource validators, failure propagation, and selective tile reads without fetching unrelated segment payloads
- Assert COG overview selection in a simulated-remote viewport benchmark and report requests, fetched bytes, cache hits, first decoded tile-block latency, and decoded pixels without fetching the complete source
- Verify tiled RGB/RGBA, BigTIFF, multi-page, and SubIFD-pyramid writer output through independent GeoTIFF.js reopen
- Compare PureJsImage, GeoTIFF.js, UTIF.js, image-js, and Jimp against sharp with an ImageMagick fallback on the targeted TIFF feature corpus
- Complete the 154-file Imazen TIFF corpus decode-to-PNG baseline with every supported valid file decoded and all remaining inputs classified at structured boundaries