// ========================================================== // Metadata functions implementation // Extended TIFF Directory GEO Tag Support // // Design and implementation by // - HervĂ© Drolon (drolon@infonie.fr) // - Thorsten Radde (support@IdealSoftware.com) // - Berend Engelbrecht (softwarecave@users.sourceforge.net) // - Mihail Naydenov (mnaydenov@users.sourceforge.net) // // Based on the LibTIFF xtiffio sample and on LibGeoTIFF // // This file is part of FreeImage 3 // // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER // THIS DISCLAIMER. // // Use at your own risk! // ========================================================== #ifdef _MSC_VER #pragma warning (disable : 4786) // identifier was truncated to 'number' characters #endif #include "../LibTIFF4/tiffiop.h" #include "FreeImage.h" #include "Utilities.h" #include "FreeImageTag.h" #include "FIRational.h" // ---------------------------------------------------------- // Extended TIFF Directory GEO Tag Support // ---------------------------------------------------------- /** Tiff info structure. Entry format: { TAGNUMBER, ReadCount, WriteCount, DataType, FIELDNUM, OkToChange, PassDirCountOnSet, AsciiName } For ReadCount, WriteCount, -1 = unknown. */ static const TIFFFieldInfo xtiffFieldInfo[] = { { TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoPixelScale" }, { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"Intergraph TransformationMatrix" }, { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTransformationMatrix" }, { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTiePoints" }, { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoKeyDirectory" }, { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoDoubleParams" }, { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, (char*) "GeoASCIIParams" }, { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, (char*)"JPL Carto IFD offset" } /** Don't use this! **/ }; static void _XTIFFLocalDefaultDirectory(TIFF *tif) { int tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); // Install the extended Tag field info TIFFMergeFieldInfo(tif, xtiffFieldInfo, tag_size); } static TIFFExtendProc _ParentExtender; /** This is the callback procedure, and is called by the DefaultDirectory method every time a new TIFF directory is opened. */ static void _XTIFFDefaultDirectory(TIFF *tif) { // set up our own defaults _XTIFFLocalDefaultDirectory(tif); /* Since an XTIFF client module may have overridden the default directory method, we call it now to allow it to set up the rest of its own methods. */ if (_ParentExtender) { (*_ParentExtender)(tif); } } /** XTIFF Initializer -- sets up the callback procedure for the TIFF module. @see PluginTIFF::InitTIFF */ void XTIFFInitialize(void) { static int first_time = 1; if (! first_time) { return; /* Been there. Done that. */ } first_time = 0; // Grab the inherited method and install _ParentExtender = TIFFSetTagExtender(_XTIFFDefaultDirectory); } // ---------------------------------------------------------- // GeoTIFF tag reading / writing // ---------------------------------------------------------- BOOL tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { char defaultKey[16]; // first check for a mandatory tag { short tag_count = 0; void* data = NULL; if(!TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tag_count, &data)) { // no GeoTIFF tag here return TRUE; } } // next, read GeoTIFF tags const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); TagLib& tag_lib = TagLib::instance(); for(size_t i = 0; i < tag_size; i++) { const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i]; if(fieldInfo->field_type == TIFF_ASCII) { char *params = NULL; if(TIFFGetField(tif, fieldInfo->field_tag, ¶ms)) { // create a tag FITAG *tag = FreeImage_CreateTag(); if(!tag) { return FALSE; } WORD tag_id = (WORD)fieldInfo->field_tag; FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)fieldInfo->field_type); FreeImage_SetTagID(tag, tag_id); FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey)); FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id)); FreeImage_SetTagLength(tag, (DWORD)strlen(params) + 1); FreeImage_SetTagCount(tag, FreeImage_GetTagLength(tag)); FreeImage_SetTagValue(tag, params); FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag); // delete the tag FreeImage_DeleteTag(tag); } } else { short tag_count = 0; void* data = NULL; if(TIFFGetField(tif, fieldInfo->field_tag, &tag_count, &data)) { // create a tag FITAG *tag = FreeImage_CreateTag(); if(!tag) { return FALSE; } WORD tag_id = (WORD)fieldInfo->field_tag; FREE_IMAGE_MDTYPE tag_type = (FREE_IMAGE_MDTYPE)fieldInfo->field_type; FreeImage_SetTagType(tag, tag_type); FreeImage_SetTagID(tag, tag_id); FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey)); FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id)); FreeImage_SetTagLength(tag, FreeImage_TagDataWidth(tag_type) * tag_count); FreeImage_SetTagCount(tag, tag_count); FreeImage_SetTagValue(tag, data); FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag); // delete the tag FreeImage_DeleteTag(tag); } } } // for(tag_size) return TRUE; } BOOL tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { char defaultKey[16]; if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) { // no GeoTIFF tag here return TRUE; } const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); TagLib& tag_lib = TagLib::instance(); for(size_t i = 0; i < tag_size; i++) { const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i]; FITAG *tag = NULL; const char *key = tag_lib.getTagFieldName(TagLib::GEOTIFF, (WORD)fieldInfo->field_tag, defaultKey); if(FreeImage_GetMetadata(FIMD_GEOTIFF, dib, key, &tag)) { if(FreeImage_GetTagType(tag) == FIDT_ASCII) { TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagValue(tag)); } else { TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); } } } return TRUE; } // ---------------------------------------------------------- // TIFF EXIF tag reading & writing // ---------------------------------------------------------- /** Read a single Exif tag @param tif TIFF handle @param tag_id TIFF Tag ID @param dib Image being read @param md_model Metadata model where to store the tag @return Returns TRUE if successful, returns FALSE otherwise */ static BOOL tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) { uint32 value_count = 0; int mem_alloc = 0; void *raw_data = NULL; if(tag_id == TIFFTAG_EXIFIFD) { // Exif IFD offset - skip this tag // md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model return TRUE; } if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) { // Exif GPS IFD offset - skip this tag // should be processed in another way ... return TRUE; } TagLib& tagLib = TagLib::instance(); // get the tag key - use NULL to avoid reading GeoTIFF tags const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL); if(key == NULL) { return TRUE; } const TIFFField *fip = TIFFFieldWithTag(tif, tag_id); if(fip == NULL) { return TRUE; } if(TIFFFieldPassCount(fip)) { // a count value is required for 'TIFFGetField' if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) { // a count is required, it will be of type uint16 uint16 value_count16 = 0; if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) { // stop, ignore error return TRUE; } value_count = value_count16; } else { // a count is required, it will be of type uint32 uint32 value_count32 = 0; if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) { // stop, ignore error return TRUE; } value_count = value_count32; } } else { // determine count if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) { value_count = 1; } else if (TIFFFieldReadCount(fip) == TIFF_SPP) { uint16 spp; TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); value_count = spp; } else { value_count = TIFFFieldReadCount(fip); } // access fields as pointers to data // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) if(TIFFFieldTag(fip) == TIFFTAG_TRANSFERFUNCTION) { // reading this tag cause a bug probably located somewhere inside libtiff return TRUE; } if ((TIFFFieldDataType(fip) == TIFF_ASCII || TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2 || TIFFFieldReadCount(fip) == TIFF_SPP || value_count > 1) && TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER && TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS && TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING && TIFFFieldTag(fip) != TIFFTAG_DOTRANGE && TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - && TIFFFieldTag(fip) != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value ) { if(TIFFGetField(tif, tag_id, &raw_data) != 1) { // stop, ignore error return TRUE; } } else { int value_size = 0; // access fields as values // Note: // For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals. { TIFFDataType tag_type = TIFFFieldDataType(fip); switch(tag_type) { case TIFF_RATIONAL: case TIFF_SRATIONAL: value_size = 4; break; default: value_size = TIFFDataWidth(tag_type); break; } } raw_data = _TIFFmalloc(value_size * value_count); mem_alloc = 1; int ok = FALSE; // ### if value_count > 1, tag is PAGENUMBER or HALFTONEHINTS or YCBCRSUBSAMPLING or DOTRANGE, // all off which are value_count == 2 (see tif_dirinfo.c) switch(value_count) { case 1: ok = TIFFGetField(tif, tag_id, raw_data); break; case 2: ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1); break; /* # we might need more in the future: case 3: ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2); break; */ default: FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip)); break; } if(ok != 1) { _TIFFfree(raw_data); return TRUE; } } } // build FreeImage tag from Tiff Tag data we collected FITAG *fitag = FreeImage_CreateTag(); if(!fitag) { if(mem_alloc) { _TIFFfree(raw_data); } return FALSE; } FreeImage_SetTagID(fitag, (WORD)tag_id); FreeImage_SetTagKey(fitag, key); switch(TIFFFieldDataType(fip)) { case TIFF_BYTE: FreeImage_SetTagType(fitag, FIDT_BYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_UNDEFINED: FreeImage_SetTagType(fitag, FIDT_UNDEFINED); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SBYTE: FreeImage_SetTagType(fitag, FIDT_SBYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SHORT: FreeImage_SetTagType(fitag, FIDT_SHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SSHORT: FreeImage_SetTagType(fitag, FIDT_SSHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG: FreeImage_SetTagType(fitag, FIDT_LONG); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD: FreeImage_SetTagType(fitag, FIDT_IFD); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG: FreeImage_SetTagType(fitag, FIDT_SLONG); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_RATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_SRATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_FLOAT: FreeImage_SetTagType(fitag, FIDT_FLOAT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_DOUBLE: FreeImage_SetTagType(fitag, FIDT_DOUBLE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG8: // BigTIFF 64-bit unsigned integer FreeImage_SetTagType(fitag, FIDT_LONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset) FreeImage_SetTagType(fitag, FIDT_IFD8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG8: // BigTIFF 64-bit signed integer FreeImage_SetTagType(fitag, FIDT_SLONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_ASCII: default: { size_t length = 0; if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) { // when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE), // tiff_read_exif_tag function gives length of 1 so all strings are truncated ... // ... try to avoid this by using an explicit calculation for 'length' length = strlen((char*)raw_data) + 1; } else { // remember that raw_data = _TIFFmalloc(value_size * value_count); const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) ); length = value_size * value_count; } FreeImage_SetTagType(fitag, FIDT_ASCII); FreeImage_SetTagLength(fitag, (DWORD)length); FreeImage_SetTagCount(fitag, (DWORD)length); FreeImage_SetTagValue(fitag, raw_data); } break; } const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id); if(description) { FreeImage_SetTagDescription(fitag, description); } // store the tag FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag); // destroy the tag FreeImage_DeleteTag(fitag); if(mem_alloc) { _TIFFfree(raw_data); } return TRUE; } /** Read all known exif tags @param tif TIFF handle @param md_model Metadata model where to store the tags @param dib Image being read @return Returns TRUE if successful, returns FALSE otherwise */ BOOL tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { TagLib& tagLib = TagLib::instance(); const int count = TIFFGetTagListCount(tif); for(int i = 0; i < count; i++) { uint32 tag_id = TIFFGetTagListEntry(tif, i); // read the tag if (!tiff_read_exif_tag(tif, tag_id, dib, md_model)) return FALSE; } // we want to know values of standard tags too!! // loop over all Core Directory Tags // ### uses private data, but there is no other way if(md_model == TagLib::EXIF_MAIN) { const TIFFDirectory *td = &tif->tif_dir; uint32 lastTag = 0; //<- used to prevent reading some tags twice (as stored in tif_fieldinfo) for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFField *fld = tif->tif_fields[fi]; const uint32 tag_id = TIFFFieldTag(fld); if(tag_id == lastTag) { continue; } // test if tag value is set // (lifted directly from LibTiff _TIFFWriteDirectory) if( fld->field_bit == FIELD_CUSTOM ) { int is_set = FALSE; for(int ci = 0; ci < td->td_customValueCount; ci++ ) { is_set |= (td->td_customValues[ci].info == fld); } if( !is_set ) { continue; } } else if(!TIFFFieldSet(tif, fld->field_bit)) { continue; } // process *all* other tags (some will be ignored) tiff_read_exif_tag(tif, tag_id, dib, md_model); lastTag = tag_id; } } return TRUE; } /** Skip tags that are already handled by the LibTIFF writing process */ static BOOL skip_write_field(TIFF* tif, uint32 tag) { switch (tag) { case TIFFTAG_SUBFILETYPE: case TIFFTAG_OSUBFILETYPE: case TIFFTAG_IMAGEWIDTH: case TIFFTAG_IMAGELENGTH: case TIFFTAG_BITSPERSAMPLE: case TIFFTAG_COMPRESSION: case TIFFTAG_PHOTOMETRIC: case TIFFTAG_THRESHHOLDING: case TIFFTAG_CELLWIDTH: case TIFFTAG_CELLLENGTH: case TIFFTAG_FILLORDER: case TIFFTAG_STRIPOFFSETS: case TIFFTAG_ORIENTATION: case TIFFTAG_SAMPLESPERPIXEL: case TIFFTAG_ROWSPERSTRIP: case TIFFTAG_STRIPBYTECOUNTS: case TIFFTAG_MINSAMPLEVALUE: case TIFFTAG_MAXSAMPLEVALUE: case TIFFTAG_XRESOLUTION: case TIFFTAG_YRESOLUTION: case TIFFTAG_PLANARCONFIG: case TIFFTAG_FREEOFFSETS: case TIFFTAG_FREEBYTECOUNTS: case TIFFTAG_GRAYRESPONSEUNIT: case TIFFTAG_GRAYRESPONSECURVE: case TIFFTAG_GROUP3OPTIONS: case TIFFTAG_GROUP4OPTIONS: case TIFFTAG_RESOLUTIONUNIT: case TIFFTAG_PAGENUMBER: case TIFFTAG_COLORRESPONSEUNIT: case TIFFTAG_PREDICTOR: case TIFFTAG_COLORMAP: case TIFFTAG_HALFTONEHINTS: case TIFFTAG_TILEWIDTH: case TIFFTAG_TILELENGTH: case TIFFTAG_TILEOFFSETS: case TIFFTAG_TILEBYTECOUNTS: case TIFFTAG_EXTRASAMPLES: case TIFFTAG_SAMPLEFORMAT: case TIFFTAG_SMINSAMPLEVALUE: case TIFFTAG_SMAXSAMPLEVALUE: // skip always, values have been set in SaveOneTIFF() return TRUE; break; case TIFFTAG_RICHTIFFIPTC: // skip always, IPTC metadata model is set in tiff_write_iptc_profile() return TRUE; break; case TIFFTAG_YCBCRCOEFFICIENTS: case TIFFTAG_REFERENCEBLACKWHITE: case TIFFTAG_YCBCRSUBSAMPLING: // skip as they cannot be filled yet return TRUE; break; case TIFFTAG_PAGENAME: { char *value = NULL; TIFFGetField(tif, TIFFTAG_PAGENAME, &value); // only skip if no value has been set if(value == NULL) { return FALSE; } else { return TRUE; } } default: return FALSE; break; } } /** Write all known exif tags @param tif TIFF handle @param md_model Metadata model from where to load the tags @param dib Image being written @return Returns TRUE if successful, returns FALSE otherwise */ BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { char defaultKey[16]; // only EXIF_MAIN so far if(md_model != TagLib::EXIF_MAIN) { return FALSE; } if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib) == 0) { return FALSE; } TagLib& tag_lib = TagLib::instance(); for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFField *fld = tif->tif_fields[fi]; const uint32 tag_id = TIFFFieldTag(fld); if(skip_write_field(tif, tag_id)) { // skip tags that are already handled by the LibTIFF writing process continue; } FITAG *tag = NULL; // get the tag key const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey); if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); TIFFDataType tif_tag_type = TIFFFieldDataType(fld); // check for identical formats // (enum value are the sames between FREE_IMAGE_MDTYPE and TIFFDataType types) if((int)tif_tag_type != (int)tag_type) { // skip tag or _TIFFmemcpy will fail continue; } // type of storage may differ (e.g. rationnal array vs float array type) if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { // skip tag or _TIFFmemcpy will fail continue; } if(tag_type == FIDT_ASCII) { TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag)); } else { TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); } } } return TRUE; }