This is a relatively short tutorial on how to read TIFF (Tagged Image File Format).
In order to follow this specific example, you'll need my original tiff file, which is here (78kb) and a hex editor. (I recommend UltraEdit).
In order to follow the explanation any tiff file will do however.
You might also be interested in the official specification from Adobe.
From the TIFF file specification
largest possible file size: 2**32 bytes
A TIFF file begins with an 8-byte image file header that points to an image file
directory (IFD). An image file directory contains information about the image, as
well as pointers to the actual image data.
Bytes 0-1: The byte order used within the file.
Legal values are:
II (4949.H) (Intel)
MM (4D4D.H) (Motorola)
In the II format, byte order is always from the least significant byte to the most
significant byte, for both 16-bit and 32-bit integers This is called little-endian byte
order. In the MM format, byte order is always from most significant to least
significant, for both 16-bit and 32-bit integers. This is called big-endian byte
order.
Bytes 2-3: An arbitrary but carefully chosen number (42) that further identifies the file as a
TIFF file.
The byte order depends on the value of Bytes 0-1.
Bytes 4-7: The offset (in bytes) of the first IFD.
The directory may be at any location in the
file after the header but must begin on a word boundary. In particular, an Image
File Directory may follow the image data it describes. Readers must follow the
pointers wherever they may lead.
The term byte offset is always used in this document to refer to a location with
respect to the beginning of the TIFF file. The first byte of the file has an offset of
0.
Types
The field types and their sizes are:
- 1 = BYTE 8-bit unsigned integer.
- 2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero).
- 3 = SHORT 16-bit (2-byte) unsigned integer.
- 4 = LONG 32-bit (4-byte) unsigned integer.
- 5 = RATIONAL Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
- 6 = SBYTE An 8-bit signed (twos-complement) integer.
- 7 = UNDEFINED An 8-bit byte that may contain anything, depending on the definition of the field.
- 8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
- 9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
- 10 = SRATIONAL Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
- 11 = FLOAT Single precision (4-byte) IEEE format.
- 12 = DOUBLE
An Image File Directory (IFD) consists of a 2-byte count of the number of directory
entries (i.e., the number of fields), followed by a sequence of 12-byte field
entries, followed by a 4-byte offset of the next IFD (or 0 if none). (Do not forget to
write the 4 bytes of 0 after the last IFD.)
There must be at least 1 IFD in a TIFF file and each IFD must have at least one
entry.
IFD Entry
Each 12-byte IFD entry has the following format:
Bytes 0-1 The Tag that identifies the field.
Bytes 2-3 The field Type.
Bytes 4-7 The number of values, Count of the indicated Type.
Bytes 8-11 The Value Offset, the file offset (in bytes) of the Value for the field.
The Value is expected to begin on a word boundary; the correspond-ing
Value Offset will thus be an even number. This file offset may
point anywhere in the file, even after the image data.
Taking apart file 000005d.tif
This means that:
49 49 2A 00 08 00 00 00
Means:
49 49 - Intel (Little Endian Encoding)
2A 00 - (decimal: 42 00) magic number, indicating tiff-ness (2x16 + 10 = 42 )
00 08 00 00 - Offset from begin of file to first IFD (in this case, 8 (The next byte))
All the tags in my test file
Start IFD (2 bytes indicating number of fields)
11 00 - number of fields (17)
Start 12 byte IFD Field Entry (FE 00 04 00 01 00 00 00 02 00 00 00) 1
FE 00 - Tag NewSubFileType
04 00 - Type LONG
01 00 00 00 - 1 value
02 00 00 00 - 00000000000000000000000000000010b
NewSubfileType
A general indication of the kind of data contained in this subfile.
Tag = 254 (FE.H)
Type = LONG
N = 1
Replaces the old SubfileType field, due to limitations in the definition of that field.
NewSubfileType is mainly useful when there are multiple subfiles in a single
TIFF file.
This field is made up of a set of 32 flag bits. Unused bits are expected to be 0. Bit 0
is the low-order bit.
Currently defined values are:
Bit 0 is 1 if the image is a reduced-resolution version of another image in this TIFF file;
else the bit is 0.
Bit 1 is 1 if the image is a single page of a multi-page image (see the PageNumber field
description); else the bit is 0.
Bit 2 is 1 if the image defines a transparency mask for another image in this TIFF file.
The PhotometricInterpretation value must be 4, designating a transparency mask.
These values are defined as bit flags because they are independent of each other.
Default is 0.
This thing is a single page or more.
Start 12 byte IFD Field Entry (01 00 04 00 01 00 00 00 F0 09 00 00) 2
00 01 - Tag (this case: ImageWidth
04 00 - Type of tag (in this case: 4 = LONG 32-bit (4-byte) unsigned integer.)
01 00 00 00 - number of values (in this case: 1)
F0 09 00 00 - Value offset (in this case, direct value, since it fits in one 4 byte sequence)
- Also, since it's little endian, it means F0 09 => 09 F0 = 2544 pixels wide
Start 12 byte IFD Field Entry (01 01 04 00 01 00 00 00 E4 0C 00 00) 3
01 01 - Tag ImageHeight
04 00 - Type LONG
01 00 00 00 - 1 value
E4 0C 00 00 - Direct Value: 3300 (0x0CE4 = 3300)
Start 12 byte IFD Field Entry (02 01 03 00 01 00 00 00 01 00 00 00) 4
02 01 - tag BitsPerSample
03 00 - SHORT
01 00 00 00 - 1 value
01 00 - 1 bit per sample
00 00 - padding
Start 12 byte IFD Field Entry (03 01 03 00 01 00 00 00 04 00 00 00) 5
03 01 - tag Compression
03 00 - SHORT
01 00 00 00 - 1 value
04 00 - Group 4 Fax
00 00 padding
Compression values:
1 Uncompressed
2 CCITT 1D
3 Group 3 Fax
4 Group 4 Fax
5 LZW
6 JPEG
32773 PackBits
Start 12 byte IFD Field Entry (06 01 03 00 01 00 00 00 00 00 00 00) 6
06 01 - tag PhotometricInterpretation
03 00 - SHORT
01 00 00 00 - 1 val
00 00 - WhiteIsZero
00 00 - padding
PhotometricInterpretation values:
0 WhiteIsZero
1 BlackIsZero
2 RGB
3 RGB Palette
4 Transparency mask
5 CMYK
6 YCbCr
7 CIELab
Start 12 byte IFD Field Entry (07 01 03 00 01 00 00 00 01 00 00 00) 7
07 01 - tag Threshholding
03 00 - SHORT
01 00 00 00 - 1 val
01 00 - value of 1
00 00 - padding
Threshholding
For black and white TIFF files that represent shades of gray, the technique used to
convert from gray to black and white pixels.
Tag = 263 (107.H)
Type = SHORT
N = 1
1 = No dithering or halftoning has been applied to the image data.
2 = An ordered dither or halftone technique has been applied to the image data.
3 = A randomized process such as error diffusion has been applied to the image data.
Default is Threshholding = 1. See also CellWidth, CellLength.
Start 12 byte IFD Field Entry (11 01 04 00 01 00 00 00 00 03 00 00) 8
11 01 - tag StripOffsets
04 00 - LONG
01 00 00 00 - 1 val
00 03 00 00 - 0x300 = 768
StripsPerImage = floor ((ImageLength + RowsPerStrip - 1) / RowsPerStrip).
StripsPerImage is not a field. It is merely a value that a TIFF reader will want to
compute because it specifies the number of StripOffsets and StripByteCounts for the
image.
Note that either SHORT or LONG values can be used to specify RowsPerStrip.
SHORT values may be used for small TIFF files. It should be noted, however, that
earlier TIFF specification revisions required LONG values and that some software
may not accept SHORT values.
The default is 2**32 - 1, which is effectively infinity. That is, the entire image is
one strip.
Use of a single strip is not recommended. Choose RowsPerStrip such that each strip is
about 8K bytes, even if the data is not compressed, since it makes buffering simpler
for readers. The 8K value is fairly arbitrary, but seems to work well.
Start 12 byte IFD Field Entry (12 01 03 00 01 00 00 00 01 00 00 00) 9
12 01 - tag Orientation
03 00 - SHORT
01 00 00 00 - 1 val
01 00 - 1
00 00 - padding
Orientation Values
The orientation of the image with respect to the rows and columns.
Tag = 274 (112.H)
Type = SHORT
N = 1
1 = The 0th row represents the visual top of the image, and the 0th column represents
the visual left-hand side.
2 = The 0th row represents the visual top of the image, and the 0th column represents
the visual right-hand side.
3 = The 0th row represents the visual bottom of the image, and the 0th column represents
the visual right-hand side.
4 = The 0th row represents the visual bottom of the image, and the 0th column represents
the visual left-hand side.
5 = The 0th row represents the visual left-hand side of the image, and the 0th column
represents the visual top.
6 = The 0th row represents the visual right-hand side of the image, and the 0th column
represents the visual top.
7 = The 0th row represents the visual right-hand side of the image, and the 0th column
represents the visual bottom.
8 = The 0th row represents the visual left-hand side of the image, and the 0th column
represents the visual bottom.
Default is 1.
Start 12 byte IFD Field Entry (15 01 03 00 01 00 00 00 01 00 00 00) 10
15 01 - tag SamplesPerPixel
03 00 - SHORT
01 00 00 00 - 1 val
01 00 - 1
00 00 - padding
SamplesPerPixel values
The number of components per pixel.
Tag = 277 (115.H)
Type = SHORT
N = 1
SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images.
SamplesPerPixel is usually 3 for RGB images.
Start 12 byte IFD Field Entry (16 01 04 00 01 00 00 00 E4 0C 00 00) 11
16 01 - tag RowsPerStrip
04 00 - LONG
01 00 00 00 - 1 val
E4 0C 00 00 - 0x0CE4 = 3300 (this is equal to the ImageHeight, could be coincidence)
RowsPerStrip
Tag = 278 (116.H)
Type = SHORT or LONG
The number of rows in each strip (except possibly the last strip.)
For example, if ImageLength is 24, and RowsPerStrip is 10, then there are 3
strips, with 10 rows in the first strip, 10 rows in the second strip, and 4 rows in the
third strip. (The data in the last strip is not padded with 6 extra rows of dummy
data.)
Start 12 byte IFD Field Entry (17 01 04 00 01 00 00 00 28 69 00 00) 12
17 01 - tag StripByteCount
04 00 - LONG
01 00 00 00 - 1 val
28 69 00 00 - 0x6928 = 26920 (= ~26K)
(Since ImageHeight = 3300, RowsPerStrip = 3300, StripOffSets= single value, there is 1 strip,\
with all the imagedata)
Start 12 byte IFD Field Entry (1A 01 05 00 01 00 00 00 F8 02 00 00) 13
1A 01 - tag XResolution
05 00 - RATIONAL (2 LONG)
01 00 00 00 - 1 val (must be an offset, 2 LONG = 8 bytes and does not fit)
F8 02 00 00 - offset = 0x02F8 = 760 from BOF (we're at 218 now (give or take a few bytes)
Start 12 byte IFD Field Entry (1B 01 05 00 01 00 00 00 F0 02 00 00) 14
1B 01 - tag YResolution
05 00 - RATIONAL
01 00 00 00 - 1 val (will not fit, this is an offset)
F0 02 00 00 - offset = 0x02F0 = 752 from BOF
This means that the 2 LONGs for the Xres start 8 bytes after the 2 LONGs for the Yres, which is ok
Start 12 byte IFD Field Entry (28 01 03 00 01 00 00 00 02 00 00 00) 15
28 01 - tag ResolutionUnit
03 00 - SHORT
01 00 00 00 - 1 val
02 00 - 2 (Inch)
00 00 - pading
ResolutionUnit
Tag = 296 (128.H)
Type = SHORT
Values:
1 = No absolute unit of measurement. Used for images that may have a non-square
aspect ratio but no meaningful absolute dimensions.
2 = Inch.
3 = Centimeter.
Default = 2 (inch).
Start 12 byte IFD Field Entry (31 01 02 00 38 00 00 00 B8 02 00 00) 16
31 01 - tag Software
02 00 - ASCII (bytes)
38 00 00 00 - 0x38 = 56 values of byte (must be an offset)
B8 02 00 00 - offset = 0x02B8 = 696
This means the Software name is from 696 to 752, which is where the YRes starts, this is Good.
Start 12 byte IFD Field Entry (32 01 02 00 14 00 00 00 A4 02 00 00) 17
32 01 - tag DateTime
02 00 - ASCII (bytes)
14 00 00 00 - 0x14 = 20 values of byte ( must be an offset)
A2 02 00 00 - offset = 0x02A2 = 674
This means the DateTim data are from 674 to 694, which means were missing 2 bytes.
(offsets must align on word boundaries (4 bytes)
End of field entries.
4 byte offset for next IFD (or 00 00 00 00 for this-was-the-last)
28 6C 00 00 (0x6C28 = 27688 from the start of file)
We're now at 8header +2IFDheader + (17x12)=204 fields + 4offset = 218 bytes in the file.
Offsets for extra data:
bytes: 20 2 56 8 8
start= 674 |DateTime|padding|Software|YResolution|XResolution| += 94 bytes = 768
And 768, surprise, surprise, is the StripOffsets :)
I'm not sure why those (674-218)= 456 extra bytes are there... (they're all empty anyway)
The actual data starts at 768, and is StripByteCount*Number_of_strips long
Number_of_Strips = (ImageHeight/RowsPerStrip) (=1 in this case)
so the data ends at: 768+26920 = 27688, This his where the next IFD-block starts. This is Good.
In order to break this mulipage tiff into its constituent parts we have to do the following:
- open the tiff
- read the header for little/big endian format
- read the first IFD
- read all fields, and remember them.
- dump all the offset fields. (except the datapart)
- read all fields, and remember them.
- create new file
- write tiff header
- determine header+fields length
I used to have some working code for this, but I seem to have misplaced it...