libtiff: manpage: Add multi page TIFF and SubIFDs description and read / write example.

From dc3369a7947b949a283d267b68524e4f931b3b49 Mon Sep 17 00:00:00 2001
From: Su Laus <[EMAIL REDACTED]>
Date: Mon, 19 Dec 2022 21:30:42 +0000
Subject: [PATCH] manpage: Add multi page TIFF and SubIFDs description and read
 / write example.

---
 doc/functions/TIFFOpen.rst           |  24 +--
 doc/functions/TIFFReadDirectory.rst  |   1 +
 doc/functions/TIFFSetDirectory.rst   |  24 +--
 doc/functions/TIFFWriteDirectory.rst |  10 +-
 doc/index.rst                        |   1 +
 doc/multi_page.rst                   | 211 +++++++++++++++++++++++++++
 doc/terms.rst                        |  25 ++++
 7 files changed, 265 insertions(+), 31 deletions(-)
 create mode 100644 doc/multi_page.rst

diff --git a/doc/functions/TIFFOpen.rst b/doc/functions/TIFFOpen.rst
index 23eb12a2..db79d7bc 100644
--- a/doc/functions/TIFFOpen.rst
+++ b/doc/functions/TIFFOpen.rst
@@ -50,17 +50,19 @@ Description
 and returns a handle to be used in subsequent calls to routines in
 :program:`libtiff`.  If the open operation fails, then
 :c:macro:`NULL` (0) is returned.  The *mode* parameter specifies if
-the file is to be opened for reading (``r``), writing (``w``), or
+the file is to be opened for reading (``r``) or (``r+``), writing (``w``), or
 appending (``a``) and, optionally, whether to override certain
-default aspects of library operation (see below).
+default aspects of library operation (see below Options_).
+
+The *mode* (``r``) opens only an **existing** file for reading and (``r+``)
+for reading and writing.
 When a file is opened for appending, existing data will not
 be touched; instead new data will be written as additional subfiles.
 If an existing file is opened for writing, all previous data is
 overwritten.
 
 If a file is opened for reading, the first TIFF directory in the file
-is automatically read (also see :c:func:`TIFFSetDirectory` for reading
-directories other than the first). 
+is automatically read. 
 If a file is opened for writing or appending, a default directory
 is automatically created for writing subsequent data.
 This directory has all the default values specified in TIFF Revision 6.0:
@@ -78,16 +80,13 @@ This directory has all the default values specified in TIFF Revision 6.0:
 To alter these values, or to define values for additional fields,
 :c:func:`TIFFSetField` must be used.
 
-A file can also be opened for reading and writing with *mode* (``r+``).
-In this case, the first TIFF directory in the file is automatically read,
-but calls to :c:func:`TIFFSetField` are put into a fresh directory, which
-will be appended when the file is closed.
-
 :c:func:`TIFFOpenW` opens a TIFF file with a Unicode filename, for read/writing.
 
 :c:func:`TIFFFdOpen` is like :c:func:`TIFFOpen` except that it opens a
 TIFF file given an open file descriptor *fd*.
 The file's name and mode must reflect that of the open descriptor.
+Even for write-only mode, ``libtiff`` needs read permissions because
+some of its functions need to read back the partially written TIFF file.
 The object associated with the file descriptor **must support random access**.
 In order to close a TIFF file opened with :c:func:`TIFFFdOpen`
 first :c:func:`TIFFCleanup` should be called to free the internal
@@ -154,10 +153,15 @@ Options
 -------
 
 The open mode parameter can include the following flags in
-addition to the ``r``, ``w``, and ``a`` flags.
+addition to the ``r``, ``r+``, ``w``, and ``a`` flags.
 Note however that option flags must follow the read-write-append
 specification.
 
+Note 2: Also for ``w`` the file will be opened with *read access* rights
+because ``libtiff`` needs to read back the partially written TIFF file
+for some of its functions.
+
+
 ``l``:
 
   When creating a new file force information be written with
diff --git a/doc/functions/TIFFReadDirectory.rst b/doc/functions/TIFFReadDirectory.rst
index 3ffaf7b8..7a1fc038 100644
--- a/doc/functions/TIFFReadDirectory.rst
+++ b/doc/functions/TIFFReadDirectory.rst
@@ -142,4 +142,5 @@ See also
 :doc:`TIFFquery` (3tiff),
 :doc:`TIFFWriteDirectory` (3tiff),
 :doc:`TIFFSetDirectory` (3tiff),
+:doc:`/multi_page`,
 :doc:`libtiff` (3tiff)
diff --git a/doc/functions/TIFFSetDirectory.rst b/doc/functions/TIFFSetDirectory.rst
index 61aea5c4..5d1212f7 100644
--- a/doc/functions/TIFFSetDirectory.rst
+++ b/doc/functions/TIFFSetDirectory.rst
@@ -20,28 +20,15 @@ Description
 :c:func:`TIFFSetDirectory` changes the current directory and reads its
 contents with :c:func:`TIFFReadDirectory`.  The parameter *dirnum*
 specifies the subfile/directory as an integer number, with the first
-directory numbered zero.
+directory numbered zero. 
+: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.
 
 :c:func:`TIFFSetSubDirectory` acts like :c:func:`TIFFSetDirectory`,
 except the directory is specified as a file offset instead of an index;
 this is required for accessing subdirectories linked through a
-``SubIFD`` tag.
-
-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.
-b. The ``SubIFDs`` are concatenated with their ``NextIFD`` parameters
-   to a SubIFD chain.
-
-LibTiff does support SubIFD chains partially. When a ``SubIFD`` tag is
-activated with :c:func:`TIFFSetSubDirectory()`, :c:func:`TIFFReadDirectory()`
-is able to parse through the SubIFD chain. The *tif_curdir* is just
-incremented from its current value and thus gets arbitrary values
-when parsing through SubIFD chains.
-: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.
+``SubIFD`` tag.  (see :ref:`MultiPage SubIFD <SubIFDAccess>`)
 
 Directory query functions :c:func:`TIFFCurrentDirectory`,
 :c:func:`TIFFCurrentDirOffset`, :c:func:`TIFFLastDirectory` and
@@ -80,4 +67,5 @@ See also
 :doc:`TIFFCustomDirectory` (3tiff),
 :doc:`TIFFWriteDirectory` (3tiff),
 :doc:`TIFFReadDirectory` (3tiff),
+:doc:`/multi_page`,
 :doc:`libtiff` (3tiff)
diff --git a/doc/functions/TIFFWriteDirectory.rst b/doc/functions/TIFFWriteDirectory.rst
index 8682d5f0..00bac0b1 100644
--- a/doc/functions/TIFFWriteDirectory.rst
+++ b/doc/functions/TIFFWriteDirectory.rst
@@ -24,9 +24,12 @@ Description
 -----------
 
 :c:func:`TIFFWriteDirectory` will write the contents of the current
-directory to the file and setup to create a new subfile in the same
-file. Applications only need to call :c:func:`TIFFWriteDirectory`
-when writing multiple subfiles to a single TIFF file.
+directory (IFD) to the file and setup to create a new directory (IFD)
+using :c:func:`TIFFCreateDirectory`.
+Applications only need to call :c:func:`TIFFWriteDirectory`
+when writing multiple subfiles (images) to a single TIFF file.
+This is called "multi-page TIFF" or "multi-image TIFF"
+(see :doc:`/multi_page`).
 :c:func:`TIFFWriteDirectory` is automatically called by
 :c:func:`TIFFClose` and :c:func:`TIFFFlush` to write a modified
 directory if the file is open for writing.
@@ -133,4 +136,5 @@ See also
 :doc:`TIFFSetDirectory` (3tiff),
 :doc:`TIFFReadDirectory` (3tiff),
 :doc:`TIFFError` (3tiff),
+:doc:`/multi_page`,
 :doc:`libtiff` (3tiff)
diff --git a/doc/index.rst b/doc/index.rst
index b33cdd1f..abec6622 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -46,6 +46,7 @@ The following sections are included in this documentation:
     build
     terms
     libtiff
+    multi_page
     functions
     internals
     addingtags
diff --git a/doc/multi_page.rst b/doc/multi_page.rst
new file mode 100644
index 00000000..d4e5dcd5
--- /dev/null
+++ b/doc/multi_page.rst
@@ -0,0 +1,211 @@
+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.
+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>`_
\ No newline at end of file
diff --git a/doc/terms.rst b/doc/terms.rst
index d20d80ed..3d4012a6 100644
--- a/doc/terms.rst
+++ b/doc/terms.rst
@@ -35,6 +35,31 @@ Codec:
     Software that implements the decoding and encoding algorithms
     of a compression scheme.
 
+.. _ImageFileDirectory:
+
+Image File Directory (IFD):
+    An Image File Directory - in short also *directory* -
+    contains information about the image,
+    as well as pointers (offsets) to the actual image data
+    within the on-disk file.
+    An IFD points either to the next IFD or shows with a ''0''
+    that it is the last IFD in the IFD-chain.
+
+Multi Images per TIFF file:
+    There may be more than one IFD in a TIFF file.
+    Each IFD defines a *subfile*.
+    One potential use of *subfiles* is to describe related images,
+    such as the pages of a facsimile transmission.
+    Such files are also named "*multi-page* TIFF" or "*multi-image* TIFF".
+    Refer also to :doc:`/multi_page`.
+
+.. _SubFile:
+
+Subfile:
+    *Subfile* is a term in the TIFF 6.0 specification for
+    an image and its associated *Image File Directory (IFD)*
+    in a TIFF file containing one or more images.
+
 In order to better understand how TIFF works (and consequently this
 software) it is important to recognize the distinction between the
 physical organization of image data as it is stored in a TIFF and how