summaryrefslogtreecommitdiff
path: root/doc/multi_page.rst
blob: 9b0776b8936e47490740e29c4c0235198f6f8895 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
Multi Page / Multi Image TIFF
=============================

There may be more than one :ref:`Image File Directory (IFD) <ImageFileDirectory>`
in a TIFF file. Each IFD defines a :ref:`subfile <SubFile>`.

One potential use of *subfiles* is to describe related images,
such as the pages of a facsimile transmission or reduced-resolution images
of the first full-resolution image.
Such files are also named "*multi-page* TIFF" or "*multi-image* TIFF".

There are two mechanisms for storing multiple images in a TIFF file:

1.  A **main-IFD chain**, where the images are stored in linked IFDs (directories).
    This mechanism is widely used.
2.  A **SubIFD chain**, where additional images are stored within the SubIFD tag
    of a main-IFD. Such child images provide extra information for the parent image
    - such as a subsampled version of the parent image. 
    SubIFD chains are rarely supported.
    For SubIFD refer also to
    `Adobe PageMaker® 6.0 TIFF Technical Notes <https://www.awaresystems.be/imaging/tiff/specification/TIFFPM6.pdf>`_

.. _SubIFDAccess:

Sub IFD chains
--------------

In the case of several SubIFDs of a main image, there are two possibilities
that are not even mutually exclusive.

a. The ``SubIFD`` tag contains an array with all offsets of the SubIFDs.
   This is called SubIFD tree and DNG recommends the use of SubIFD trees,
   as described in the TIFF-EP specification. SubIFD chains are not supported.
b. The SubIFDs are concatenated with their ``NextIFD`` parameters
   to a SubIFD chain.

LibTiff does support SubIFD chains partially. When the first
``SubIFD`` tag is activated and read with :c:func:`TIFFSetSubDirectory()`,
the following can be parsed with :c:func:`TIFFReadDirectory()`.
The *tif_curdir* is just incremented from its current value
and thus gets arbitrary values when parsing through SubIFD chains.
When the SubIFDs are not chained, each offset
within the SubIFD array has to be activated and read individually.
:c:func:`TIFFSetDirectory()` only works with main-IFD chains because
allways starts with the first main-IFD and thus is able to reset
the SubIFD reading chain to the main-IFD chain.

Writing Multi Page TIFF
-----------------------

The following example code shows how to write multi-page TIFF
as main-IFD chain and as SubIFD chain.
``libtiff`` writes SubIFDs as an array of IFDs that are not chained
additionally, as Adobe PageMaker® 6.0 TIFF Technical Notes suggests.

.. highlight:: c

::

  #include <tiffio.h>

  int main(int argc, const char **argv)
  {
      TIFF *tiff;

      /* Define the number of pages/images (main-IFDs) you are going to write */
      int number_of_images = 3;

  /* Define the number of sub - IFDs you are going to write */
  #define NUMBER_OF_SUBIFDs 2
      int number_of_sub_IFDs = NUMBER_OF_SUBIFDs;
      toff_t sub_IFDs_offsets[NUMBER_OF_SUBIFDs] = {
          0UL}; /* array for SubIFD tag */
      int blnWriteSubIFD = 0;

      if (!(tiff = TIFFOpen("multiPageTiff2.tif", "w")))
          return 1;

      for (int i = 0; i < number_of_images; i++)
      {
          char pixel[1] = {128};

          TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, 1);
          TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, 1);
          TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
          TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
          TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);

          /* For the last but one multi-page image, add a SubIFD e.g. for a
          * thumbnail */
          if (number_of_images - 2 == i)
              blnWriteSubIFD = 1;

          if (blnWriteSubIFD)
          {
             /* Now here is the trick: the next n directories written
              * will be sub-IFDs of the main-IFD (where n is number_of_sub_IFDs
              * specified when you set the TIFFTAG_SUBIFD field.
              * The SubIFD offset array sub_IFDs_offsets is filled automatically
              * with the proper offset values by the following number_of_sub_IFDs
              * TIFFWriteDirectory() calls and updated in the related main-IFD
              * with the last call.
              */
              if (!TIFFSetField(tiff, TIFFTAG_SUBIFD, number_of_sub_IFDs,
                                sub_IFDs_offsets))
                  return 1;
          }

          /* Write dummy pixel to image */
          if (TIFFWriteScanline(tiff, pixel, 0, 0) < 0)
              return 1;
          /* Write image / directory to file */
          if (!TIFFWriteDirectory(tiff))
              return 1;

          if (blnWriteSubIFD)
          {
             /* A SubIFD tag has been written for that main-IFD and this
              * triggers that pervious TIFFWriteDirectory() to switch to the
              * SubIFD-chain for the next number_of_sub_IFDs writings.
              * Thus, only the thumbnail images need to be
              * set up and written to file using TIFFWriteDirectory().
              * The last of this TIFFWriteDirectory() calls will setup
              * the next fresh main-IFD.
              */
              for (int i = 0; i < number_of_sub_IFDs; i++)
              {
                  TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, 1);
                  TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, 1);
                  TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
                  TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
                  TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
                  /* SUBFILETYPE tag is not mandatory for SubIFD writing, but a
                  * good idea to indicate thumbnails */
                  if (!TIFFSetField(tiff, TIFFTAG_SUBFILETYPE,
                                    FILETYPE_REDUCEDIMAGE))
                      return 1;

                  /* Write dummy pixel to thumbnail image */
                  pixel[0] = 64;
                  if (TIFFWriteScanline(tiff, pixel, 0, 0) < 0)
                      return 1;
                  /* Writes now in the SubIFD chain */
                  if (!TIFFWriteDirectory(tiff))
                      return 1;

                  blnWriteSubIFD = 0;
              }
          }
      }
      TIFFClose(tiff);
      return 0;
    }

Reading Multi Page TIFF
-----------------------

For a reading example see code of `tools/tiffinfo.c` or below:

.. highlight:: c

::

    /* Reading of multi-page and SubIFD images (subfiles) */
    if (!(tiff = TIFFOpen(filename, "r")))
        return 1;

    tdir_t currentDirNumber = TIFFCurrentDirectory(tiff);

    /* The first directory is already read through TIFFOpen() */
    int blnRead = 0;
    do
    {
        /*Check if there are SubIFD subfiles */
        void *ptr;
        if (TIFFGetField(tiff, TIFFTAG_SUBIFD, &number_of_sub_IFDs, &ptr))
        {
            /* Copy SubIFD array from pointer */
            memcpy(sub_IFDs_offsets, ptr,
                   number_of_sub_IFDs * sizeof(sub_IFDs_offsets[0]));

            for (int i = 0; i < number_of_sub_IFDs; i++)
            {
                /* Read first SubIFD directory */
                if (!TIFFSetSubDirectory(tiff, sub_IFDs_offsets[i]))
                    return 1;
                /* Check if there is a SubIFD chain behind the first one from
                 * the array, as specified by Adobe */
                while (TIFFReadDirectory(tiff))
                    /* analyse subfile */
                    ;
            }
            /* Go back to main-IFD chain and re-read that main-IFD directory */
            if (!TIFFSetDirectory(tiff, currentDirNumber))
                return 1;
        }
        /* Read next main-IFD directory (subfile) */
        blnRead = TIFFReadDirectory(tiff);
        currentDirNumber = TIFFCurrentDirectory(tiff);
    } while (blnRead);
    TIFFClose(tiff);




See also
--------

:doc:`terms`,
:doc:`/functions/TIFFSetDirectory` (3tiff),
:doc:`/functions/TIFFWriteDirectory` (3tiff),
`Adobe PageMaker® 6.0 TIFF Technical Notes <https://www.awaresystems.be/imaging/tiff/specification/TIFFPM6.pdf>`_,
`Example from StackOverflow <https://stackoverflow.com/questions/11959617/in-a-tiff-create-a-sub-ifd-with-thumbnail-libtiff>`_