TIFF and LibTiff Mail List Archive


2004.01.16 17:32 "[Tiff] libtiff and CALS files...", by Bill Hess
2004.01.19 15:03 "Re: [Tiff] libtiff and CALS files...", by Bob Friesenhahn
2004.01.20 12:58 "Re: [Tiff] libtiff and CALS files...", by Bill Hess
2004.01.19 19:15 "Re: [Tiff] libtiff and CALS files...", by Phillip Crews
2004.01.20 13:44 "[Tiff] libtiff and CALS files...", by Finlayson, Ross

2004.01.20 13:44 "[Tiff] libtiff and CALS files...", by Finlayson, Ross


CALS is a DOD, Department of Defense, standard for acquisition and life cycle support, it is said. Anyways part of the CALS standards are CALS raster and document file formats.

CALS 1, or CALS raster or C1, .img, or perhaps .mil images, are as described, a fixed length header of maybe 2048 bytes, I forget, then a block of Group 4 facsimile encoded data.

CALS2 has two varieties, one form is a raster image, and the other uses an SGML profile of some form, perhaps similar to DSSSL, some form of ODAP. Anyways the raster image is similar to the CALS Type 1 image, but different. I think type 2 raster files might also be called TRIF files.

The CALS3 format is a raster format and is very similar to TIFF. Also called NIFF, for Navy Image File Format, or NIRS/NIFF, it is almost identical in structure to a TIFF. Where TIFF has the header ir signature of of "42", NIFF uses "N1". All NIFF images, at least for the specifications I have read, are black and white images. If the image is less than or equal to letter size, it is an untiled images, else it is a tiled image.

CALS4 is also known as EDMICs, and JEDMICS, EDMICS/JEDMICS, ImageCenter, tg4, or sometimes C4, or IMG files. These are files with a fixed header and tiles of Group 4 compressed fax data to comprise basically engineering drawings.

I wrote a short synopsis how to read NIRS/NIFF images with libtiff, although I'm still looking for NIRS/NIFF images to test it. If you have any NIRS/NIFF samples that you can share I'm interested in looking at them. The way I read NIFF files using libtiff is to add support for reading the NIFF private fields and the NIFF header instead of the TIFF header.

Programs to convert CALS type 1 or 4 into TIFF could be similar to the fax2tiff program.

Hopefully if you actually have a NIFF file this might be useful. If so, please e-mail to me sample NIFF files of small size, standard tiled and untiled, thank you.


Reading NIRS/NIFF files with libtiff and these patches.

NIFF, the "Navy Image File Format", also known as "CALS Raster Type 3", according to "Navy

Specification, Advanced Technical Information Support (ATIS), Raster Technical Manuals,

Preparation of" is an image file format for the storage of bilevel/black and white data

in tiled and untiled forms that is very similar to little-endian TIFF.

The difference between a NIFF and a valid TIFF file is that the NIFF file has as its header "N1", 0x4e31 instead of 42, 0x0042. As

the file is defined to use little-endian byte order the actual value of zero-based bytes 2

and 3 are hexadecimal 31 and 4e instead of 42 and 00.

NIFF defines several private TIFF tag definitions and as well overloads for its use a few

of the TIFF tags. Here are some definitions for the NIFF tags and specified values:

#define NIFFTAG_SUBFILETYPE             254     /* Same tag as 
TIFFTAG_SUBFILETYPE, different interpretation */
#define NIFF_SUBFILE_TILED              0x02
#define NIFFTAG_PELPATHLENGTH           256     /* Same as TIFFTAG_IMAGEWIDTH */
#define NIFFTAG_BITSPERSAMPLE           258     /* Same as TIFFTAG_BITSPERSAMPLE */
#define NIFFTAG_PHOTOMETRIC             262     /* Same as TIFFTAG_PHOTOMETRIC */
#define NIFFTAG_DATAOFFSET              273     /* Either strip offset or tile 
offsets */
#define NIFFTAG_SAMPLESPERPIXEL         277     /* Same as 
#define NIFFTAG_DATABYTECOUNTS          279     /* Either strip byte counts or 
tile byte counts */
#define NIFFTAG_RESOLUTIONUNIT          296     /* Same as 
#define NIFFTAG_ROWSPERLINEPATH         323     /* Same as TIFFTAG_TILELENGTH */
#define NIFFTAG_ROTATION                33465   /* Private tag, 1 x uint16 */
#define NIFF_ROTATION_0                 0
#define NIFF_ROTATION_90                1
#define NIFF_ROTATION_180               2
#define NIFF_ROTATION_270               3
#define NIFFTAG_NAVYCOMPRESSION         33466   /* Private tag, 1 x 
uint16 or tile count x uint16 */
#define NIFFTAG_TILEINDEX               33467   /* Private tag, tile count * uint16 */

If the image is less than the size of one 8 1/2" by 11" page, then it may be stored as a single-strip image, otherwise it is stored as a tiled image, with tiles of dimensions

512 x 512.

The tile index field stores for each tile of the image, in order of the tiles stored in the

file as indicated by the tile offsets and tile byte counts, the tile's location in the sequence

of tiles of the image. For example, for a 1024 x 2048 image, with two tiles in the x

dimension and four tiles in the y dimension, the tile with tile index 0 is the upper left

tile, and the tile with tile index 7 is the lower right tile, and they may otherwise any

of the tiles stored physically in the image.

NIFF uses only CCITT Fax Group 4 compression, (Modifed Modified READ, MMR), and otherwise

stores uncompressed data. The data is to be compressed with Group 4 facsimile encoding

unless the compressed data would be larger than the uncompressed data, in which case it is

to be stored uncompressed.

The Navy Compression field contains either one entry or entry for each tile. If it contains

only one entry then its value applies to each tile of the image, else for each tile its

compression method is indicated. The specified allowable values are 1 for uncompressed data

and 4 for Group 4 facsimile compressed data.

The NIFF rotation tag is used instead of the TIFF orientation tag, values represent

degrees counterclockwise rotation, 0, 1, 2, and 3 correspond to TIFF orientations 0, 6,

3, and 8.

In reading the NIFF contents, it is either a tiled or untiled image. IF it is a tiled

image, bit 1 of the Navy Subfiletype uint16 field is set. If it is an untiled image it

has exactly one strip, which could be considered a tile, the only difference between an

untiled image and a tiled image is that the single strip or tile of the

untiled image may

have dimensions that are not 512 x 512. The TIFFIsTiled() function will return a positive value as the TIFFTAG_TILEWIDTH and TIFFTAG_TILELENGTH tags are set. As well, bit 1 of the NIFF subfiletype will be set if

the image is tiled. Otherwise the image is untiled and consists of a single strip image

that is compressed with Group 4 fax encoding unless the NIFFTAG_NAVYCOMPRESSION field has the value NIFF_NAVYCOMPRESSION_UNCOMPRESSED.

It is necessary to read the contents of the tile index field, when the image is tiled, to

determine which tile in the image file represents a particular tile in the image. One way

to do that is to store an array of structures with the tile index and the ttile_t libtiff

tile number type and sort it on the tile index, then when the tile at that index is to be read,

to read the tile from the file of the ttile_t.

Define a structure to contain the reference to the tile from the tile index.

typedef struct {
        uint16 tile_index;
        ttile_t tile_number;
        uint16 tile_uncompressed;
} tile;

Before reading the tiles, sort an array of (or rather pointer to) the tile indexes/indices.

tile* tiles;
uint16* tileindex;
uint16* tilecompression;

ttile_t tile_count = TIFFNumberOfTiles(tiff);

tiles = _TIFFmalloc(tile_count * sizeof(tile));

_TIFFmemset(tiles, 0x00, tile_count * sizeof(tile));

TIFFGetField(tiff, NIFFTAG_TILEINDEX, &tileindex);

for(i=0; i<tile_count; i++){
        tiles[i].tile_index = tileindex[i];
        tiles[i].tile_number = i;

if(TIFFGetField(tiff, NIFFTAG_NAVYCOMPRESSION, &tilecompression)==tile_count){

        for(i=0; i<tile_count;i++){
                for(i=0; i<tile_count;i++){

qsort(tiles, tile_count, sizeof(tile), qsort_cmp_tile_index);

The qsort_cmp_tile_index function compares the tile indexes.

int qsort_cmp_tile_index(const void* e1, const void* e2){
        return( ((tile*)e1)->tile_index - ((tile*)e2)->tileindex );

Then, you have the tile array. When you go to read the tiles, for ttile_t

i from 0 to tile_count in order, instead of calling TIFFReadEncodedTile on

i, call it on tiles[i].tile_number. If tiles[i].tile_uncompressed is equal

to 1, call TIFFReadRawTile instead of TIFFReadEncodedTile.

These patches here don't really handle writing NIFF files. In writing NIFF files

it is necessary to determine for each tile whether or not "compression" would

increase the size of the data. In Group 4 compression, data that is likely to be

increased in size by encoding is stippled with few long runs of black or white pixels.

Normally the method to determine if data would be increased in size by compression is

to compress it and compare the size to the uncompressed data, which shall be a 512 x 512

block for tiles of 512*512/8 bytes, or the complete image for the single strip image

representing less than or equal to a letter size piece of paper. A way to do that

would be to generate a regular compressed tiled TIFF. Then, go through the tiles and

compare their size against the size of the uncompressed tile to form the arrays of

short 16-bit integers representing for each tile whether it is to be stored compressed

or uncompressed. Then, generate a NIFF file, with storing for each file either the

raw data of the image to be stored for the tile, if and only if the compressed size is

larger than the Group 4 fax compressed size, otherwise copying over the raw tile from

the fully compressed image. The writing of the tiles to the output NIFF would be done