From dc3369a7947b949a283d267b68524e4f931b3b49 Mon Sep 17 00:00:00 2001 From: Su Laus Date: Mon, 19 Dec 2022 21:30:42 +0000 Subject: 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 (limited to 'doc') 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 `) 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) ` +in a TIFF file. Each IFD defines a :ref:`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 `_ + +.. _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 + + 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 `_, +`Example from StackOverflow `_ \ 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 -- cgit v1.2.1