1993.09.02 17:17 "Private Tags, Private IFDs & libtiff", by Niles Ritter
Summary: Difficulties with incorporating new releases of libtiff is encountered when implementation requires adding private tags and private IFD's. A number of suggestions are made to modify the design of libtiff to make the incorporation process (less) difficult, by making libtiff "extensible", without the necessity of modifying released code.
We have been using the libtiff software for several years now, and I am convinced that others may have begun encountering one of the difficulties we have found when working with the current design. Fortunately, most of our difficulties may be relieved by a simple modification to a few choice modules. Here we go...
The basic problem is this: we have taken the libtiff code and added some currently private tags, needed for georeferencing and annotating scanned maps. As noted in the "HOW_TO_CONFIGURE" document, this requires six steps be performed, involving changes to the files tiff.h, tiffiop.h, tif_dirinfo.c, tif_dir.c, tif_print.c, and for some complex tags, possibly even tif_dirread.c and tif_dirwrite.c. (Side Note: in the current implementation, if a very large number of tags are added, the dimension of td_fieldsset will need to be increased, or you will corrupt the directory structure; increment by 1 for every 32 tags you have added).
Given the otherwise elegant object-oriented methodology used in the compression schemes and in file/error/warning handling, it would help enormously in accomodating new releases of libtiff, if the modules mentioned above changed in a few places. The additions/mods run at about 15 lines of code, with no measurable change in performance, and fully backward-compatible implementation.
Instead of hard-coding a TIFFError call in the "default" switch case, the routine calls a "TIFFXGetField" handler. In the delivered package, this would mean simply defining a set of stub routines which call TIFFError ,reporting that the tag is not recognized. In addition, this would allow a client program to override the extended tag handlers in the same way that the TIFFErrorHanders can be done currently.
The complete set of handlers which would need to be defined and installed into the libtiff releases are:
(routine names are only suggested for sake of argument).
These routines could be overriden/accessed at runtime by the functions:
Each TIFFXnnn routine is called by its parent TIFFnnn (except for TIFFXNumFields, which is new, and will be called in several places).
TIFFXS/GetField would be called in the switch statement of its parent; the default action is to return a TIFFError for invalid tags, TIFFXReadDirectory just before its parent's return, it's default is NULL.
TIFFXNumFields would be called in TIFFWriteDirectory, when determining the number of TIFF tags in the current TIFF IFD (for sizing the IFD in the file); if "tif" is 0 it returns the total # of new tags, which will be used by TIFFClientOpen in dynamically allocating enough memory for the td_fieldsset array (which would now be a pointer).Its default is NULL.
TIFFXWriteDirectory would be called immediately after the switch statement in TIFFWriteDirectory, and prior to writing the directory structure "dir" to the file (dir must be passed in to the routine to fill in the missing fields). NULL Default
TIFFXFindFieldInfo provides the pointers to the extension's equivalent to "tiffFieldInfo" elements. NULL Default.
The extended handlers themselves would look very much like the routines that called them, except their switch() statements would have the private tag implementations defined. For TIFFXWriteDirectory, this means that the static code for writing rationals, etc needs to be replicated in TIFFXWriteDirectory (but only if one of the extended tags is rational, LONG array, etc). The price of modularity.
The whole tag installation process could in this way be done without having to change any of the delivered modules. Even in the case of tiffiop.h, where the internal representation of the tags is stored in a struct, if a "td_xdir" pointer is defined, the client-provided module would be able to create and attach its own structure here. In addition, the td_fieldsset entry will need to be dynamically allocated.
As with many who have formally requested registered private tags from Aldus, if many (>5) tags are needed, Aldus developers reccomends defining one tag to be a LONG value, which is an offset to a "private IFD", in which one can define up to 64K tags. In particular our implemenation takes this approach, and it may be noted that the private IFD handlers may be set up simply by overriding the same modules listed above. As an exercise, a description of the overrides in this case is shown below:
Write the TIFFXG/Set routines so they will know which tags are extended and which are public, and just store everything in the same "td_xdir" structure.
Write the TIFFXNumFields to return the total number of extended fields, public and private.
For the TIFFXReadDirectory method, just read in all of the extended tags you have defined in the standard TIFF IFD, and if one of them is one your code recognizes as an offset to a private IFD, TIFFSeek to that point and call a new routine analogous to TIFFReadDirectory to read in all of those tags.
For the TIFFXWriteDirectory method, work with the private IFD first, allocating space in the file for the IFD, then writing out the data associated with the private tags, finally flushing the private tags/file offsets to the private IFD. Next, go to the extended tags which are part of the public IFD and write out their associated data values, filling the "dir" entries as you go. Ironically, the directory itself doesn't get written out here -- that is done by TIFFWriteDirectory immediately after this routine returns.
NOTE AT THE END
My current version of libtiff is ~3.0, but I have updated the discussion for the latest (3.3) delivery, though I'm not quite as sure about the implementation details as with 3.0.
I would be very much interested in other's comments on this (modest) proposal, and if they have had similar experiences with implementing extended public or private tags.