Beispiel #1
0
void
tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
	char defaultKey[16];

	if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) {
		return;
	}

	size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);

	TagLib& tag_lib = TagLib::instance();

	for(unsigned 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));
			}
		}
	}
}
Beispiel #2
0
BOOL DLL_CALLCONV 
FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
	if(!dib) 
		return FALSE;

	TAGMAP *tagmap = NULL;

	// get the metadata model
	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
	tagmap = (*metadata)[model];

	if(key != NULL) {

		if(!tagmap) {
			// this model, doesn't exist: create it 
			tagmap = new TAGMAP();
			(*metadata)[model] = tagmap;
		}

		// first check the tag
		if(tag) {
			if(FreeImage_GetTagKey(tag) == NULL) {
				FreeImage_SetTagKey(tag, key);
			} else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
				// set the tag key
				FreeImage_SetTagKey(tag, key);
			}
			if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
				// invalid data count ?
				return FALSE;
			}
		}

		// delete existing tag
		FITAG *old_tag = (*tagmap)[key];
		if(old_tag) {
			FreeImage_DeleteTag(old_tag);
		}

		// create a new tag
		(*tagmap)[key] = FreeImage_CloneTag(tag);
	}
	else {
		// destroy the metadata model
		if(tagmap) {
			for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
				FITAG *tag = (*i).second;
				FreeImage_DeleteTag(tag);
			}

			delete tagmap;
			(*metadata)[model] = NULL;
		}
	}

	return TRUE;
}
Beispiel #3
0
/**
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;
}
Beispiel #4
0
static BOOL 
FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag)
{
	if( FreeImage_GetMetadata(model, dib, key, tag) ) {
		if( FreeImage_GetTagType(*tag) == type ) {
			return TRUE;
		}
	}
	return FALSE;
}
Beispiel #5
0
/// Constructor with FITAG
FIRational::FIRational(const FITAG *tag) {
	switch(FreeImage_GetTagType((FITAG*)tag)) {
		case FIDT_RATIONAL:		// 64-bit unsigned fraction 
		{
			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue((FITAG*)tag);
			initialize((LONG)pvalue[0], (LONG)pvalue[1]);
			break;
		}

		case FIDT_SRATIONAL:	// 64-bit signed fraction 
		{
			LONG *pvalue = (LONG*)FreeImage_GetTagValue((FITAG*)tag);
			initialize((LONG)pvalue[0], (LONG)pvalue[1]);
			break;
		}
	}
}
Beispiel #6
0
/**
Convert a FITAG (coming from FIMD_EXIF_MAIN) to a DPKPROPVARIANT.
No allocation is needed here, the function just copy pointers when needed. 
@see WriteDescriptiveMetadata
*/
static BOOL
WritePropVariant(FIBITMAP *dib, WORD tag_id, DPKPROPVARIANT & varDst) {
	FITAG *tag = NULL;

	TagLib& s = TagLib::instance();
	
	// clear output DPKPROPVARIANT
	varDst.vt = DPKVT_EMPTY;

	// given the tag id, get the tag key
	const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL);
	// then, get the tag info
	if(!FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
		return FALSE;
	}

	// set the tag value
	switch(FreeImage_GetTagType(tag)) {
		case FIDT_ASCII:
			varDst.vt = DPKVT_LPSTR;
			varDst.VT.pszVal = (char*)FreeImage_GetTagValue(tag);
			break;
		case FIDT_BYTE:
		case FIDT_UNDEFINED:
			varDst.vt = DPKVT_LPWSTR;
			varDst.VT.pwszVal = (U16*)FreeImage_GetTagValue(tag);
			break;
		case FIDT_SHORT:
			varDst.vt = DPKVT_UI2;
			varDst.VT.uiVal = *((U16*)FreeImage_GetTagValue(tag));
			break;
		case FIDT_LONG:
			varDst.vt = DPKVT_UI4;
			varDst.VT.ulVal = *((U32*)FreeImage_GetTagValue(tag));
			break;
		default:
			break;
	}
	
	return TRUE;
}
Beispiel #7
0
/**
Encode IPTC metadata into a binary buffer. 
The buffer is allocated by the function and must be freed by the caller. 
*/
BOOL 
write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) {
	FITAG *tag = NULL;
	FIMETADATA *mdhandle = NULL;

	BYTE *buffer = NULL;
	unsigned buffer_size = 0;

	// parse all IPTC tags and rebuild a IPTC profile
	mdhandle = FreeImage_FindFirstMetadata(FIMD_IPTC, dib, &tag);

	if(mdhandle) {
		do {
			WORD tag_id	= FreeImage_GetTagID(tag);

			// append the tag to the profile

			switch(tag_id) {
				case TAG_RECORD_VERSION:
					// ignore (already handled)
					break;

				case TAG_SUPPLEMENTAL_CATEGORIES:
				case TAG_KEYWORDS:
					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
						std::string value = (const char*)FreeImage_GetTagValue(tag);

						// split the tag value
						std::vector<std::string> output;
						std::string delimiter = IPTC_DELIMITER;		
						
						size_t offset = 0;
						size_t delimiterIndex = 0;

						delimiterIndex = value.find(delimiter, offset);
						while (delimiterIndex != std::string::npos) {
							output.push_back(value.substr(offset, delimiterIndex - offset));
							offset += delimiterIndex - offset + delimiter.length();
							delimiterIndex = value.find(delimiter, offset);
						}
						output.push_back(value.substr(offset));

						// add as many tags as there are comma separated strings
						for(int i = 0; i < (int)output.size(); i++) {
							std::string& tag_value = output[i];
							buffer = append_iptc_tag(buffer, &buffer_size, tag_id, (DWORD)tag_value.length(), tag_value.c_str());
						}

					}
					break;

				case TAG_URGENCY:
					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
						DWORD length = 1;	// keep the first octet only
						buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
					}
					break;

				default:
					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
						DWORD length = FreeImage_GetTagLength(tag);	
						buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
					}					
					break;
			}

		} while(FreeImage_FindNextMetadata(mdhandle, &tag));
		
		FreeImage_FindCloseMetadata(mdhandle);

		// add the DirectoryVersion tag
		const short version = 0x0200;
		buffer = append_iptc_tag(buffer, &buffer_size, TAG_RECORD_VERSION, sizeof(version), &version);
		
		*profile = buffer;
		*profile_size = buffer_size;

		return TRUE;
	}

	return FALSE;
}
Beispiel #8
0
/**
Convert a tag to a C string
*/
static const char* 
ConvertAnyTag(FITAG *tag) {
	char format[MAX_TEXT_EXTENT];
	static std::string buffer;
	DWORD i;

	if(!tag)
		return NULL;

	buffer.erase();
	
	// convert the tag value to a string buffer

	FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
	DWORD tag_count = FreeImage_GetTagCount(tag);

	switch(tag_type) {
		case FIDT_BYTE:		// N x 8-bit unsigned integer 
		{
			BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld",	(LONG) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	(LONG) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SHORT:	// N x 16-bit unsigned integer 
		{
			unsigned short *pvalue = (unsigned short *)FreeImage_GetTagValue(tag);

			sprintf(format, "%hu", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %hu",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_LONG:		// N x 32-bit unsigned integer 
		{
			DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag);

			sprintf(format, "%lu", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %lu",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_RATIONAL: // N x 64-bit unsigned fraction 
		{
			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
				buffer += format;
			}
			break;
		}
		case FIDT_SBYTE:	// N x 8-bit signed integer 
		{
			char *pvalue = (char*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld",	(LONG) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	(LONG) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SSHORT:	// N x 16-bit signed integer 
		{
			short *pvalue = (short *)FreeImage_GetTagValue(tag);

			sprintf(format, "%hd", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %hd",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SLONG:	// N x 32-bit signed integer 
		{
			LONG *pvalue = (LONG *)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SRATIONAL:// N x 64-bit signed fraction 
		{
			LONG *pvalue = (LONG*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
				buffer += format;
			}
			break;
		}
		case FIDT_FLOAT:	// N x 32-bit IEEE floating point 
		{
			float *pvalue = (float *)FreeImage_GetTagValue(tag);

			sprintf(format, "%f", (double) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%f", (double) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_DOUBLE:	// N x 64-bit IEEE floating point 
		{
			double *pvalue = (double *)FreeImage_GetTagValue(tag);

			sprintf(format, "%f", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%f", pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_IFD:		// N x 32-bit unsigned integer (offset) 
		{
			DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag);

			sprintf(format, "%X", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %X",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_PALETTE:	// N x 32-bit RGBQUAD 
		{
			RGBQUAD *pvalue = (RGBQUAD *)FreeImage_GetTagValue(tag);

			sprintf(format, "(%d,%d,%d,%d)", pvalue[0].rgbRed, pvalue[0].rgbGreen, pvalue[0].rgbBlue, pvalue[0].rgbReserved);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " (%d,%d,%d,%d)", pvalue[i].rgbRed, pvalue[i].rgbGreen, pvalue[i].rgbBlue, pvalue[i].rgbReserved);
				buffer += format;
			}
			break;
		}
		
		case FIDT_LONG8:	// N x 64-bit unsigned integer 
		{
			FIUINT64 *pvalue = (FIUINT64 *)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%ld", pvalue[i]);
				buffer += format;
			}
			break;
		}

		case FIDT_IFD8:		// N x 64-bit unsigned integer (offset)
		{
			FIUINT64 *pvalue = (FIUINT64 *)FreeImage_GetTagValue(tag);

			sprintf(format, "%X", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%X", pvalue[i]);
				buffer += format;
			}
			break;
		}

		case FIDT_SLONG8:	// N x 64-bit signed integer
		{
			FIINT64 *pvalue = (FIINT64 *)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%ld", pvalue[i]);
				buffer += format;
			}
			break;
		}

		case FIDT_ASCII:	// 8-bit bytes w/ last byte null 
		case FIDT_UNDEFINED:// 8-bit untyped data 
		default:
		{
			int max_size = MIN((int)FreeImage_GetTagLength(tag), (int)MAX_TEXT_EXTENT);
			if(max_size == MAX_TEXT_EXTENT)
				max_size--;
			memcpy(format, (char*)FreeImage_GetTagValue(tag), max_size);
			format[max_size] = '\0';
			buffer += format;
			break;
		}
	}

	return buffer.c_str();
}
Beispiel #9
0
/**
	Process Exif directory

	@param dib Input FIBITMAP
	@param tiffp Pointer to the TIFF header
	@param offset 0th IFD offset
	@param length Length of the datafile
	@param msb_order Endianess order of the datafile
	@return 
*/
static BOOL 
jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) {
	WORD de, nde;

	std::stack<WORD>			destack;	// directory entries stack
	std::stack<BYTE*>			ifdstack;	// IFD stack
	std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack

	// Keep a list of already visited IFD to avoid stack overflows 
	// when recursive/cyclic directory structures exist. 
	// This kind of recursive Exif file was encountered with Kodak images coming from 
	// KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
	std::map<DWORD, int> visitedIFD;

    #define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))

	// set the metadata model to Exif

	TagLib::MDMODEL md_model = TagLib::EXIF_MAIN;

	// set the pointer to the first IFD and follow it were it leads.

	BYTE *ifdp = (BYTE*)tiffp + offset;

	de = 0;

	do {
		// if there is anything on the stack then pop it off
		if(!destack.empty()) {
			ifdp		= ifdstack.top();	ifdstack.pop();
			de			= destack.top();	destack.pop();
			md_model	= modelstack.top();	modelstack.pop();
		}

		// remember that we've visited this directory so that we don't visit it again later
		DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
		if(visitedIFD.find(visited) != visitedIFD.end()) {
			continue;
		} else {
			visitedIFD[visited] = 1;	// processed
		}

		// determine how many entries there are in the current IFD
		nde = ReadUint16(msb_order, ifdp);

		for(; de < nde; de++) {
			char *pde = NULL;	// pointer to the directory entry
			char *pval = NULL;	// pointer to the tag value
			
			// create a tag
			FITAG *tag = FreeImage_CreateTag();
			if(!tag) return FALSE;

			// point to the directory entry
			pde = (char*) DIR_ENTRY_ADDR(ifdp, de);

			// get the tag ID
			FreeImage_SetTagID(tag, ReadUint16(msb_order, pde));
			// get the tag format
			WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
                // a problem occured : delete the tag (not free'd after)
			    FreeImage_DeleteTag(tag);
				// break out of the for loop
				break;
            }
			FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);

			// get number of components
			FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4));
			// get the size of the tag value in bytes
			FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth((WORD)FreeImage_GetTagType(tag)));

			if(FreeImage_GetTagLength(tag) <= 4) {
				// 4 bytes or less and value is in the dir entry itself
				pval = pde + 8;
			} else {
				// if its bigger than 4 bytes, the directory entry contains an offset
				// first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
				DWORD offset_value = ReadUint32(msb_order, pde + 8);
				if(offset_value > length) {
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				// now check if offset + tag length exceeds buffer
				if(offset_value > length - FreeImage_GetTagLength(tag)) {
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				pval = (char*)(tiffp + offset_value);
			}

			// check for a IFD offset
			BOOL isIFDOffset = FALSE;
			switch(FreeImage_GetTagID(tag)) {
				case TAG_EXIF_OFFSET:
				case TAG_GPS_OFFSET:
				case TAG_INTEROP_OFFSET:
				case TAG_MAKER_NOTE:
					isIFDOffset = TRUE;
					break;
			}
			if(isIFDOffset)	{
				DWORD sub_offset = 0;
				TagLib::MDMODEL next_mdmodel = md_model;
				BYTE *next_ifd = ifdp;
				
				// get offset and metadata model
				if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
					processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)pval + sub_offset;
				} else {
					processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)tiffp + sub_offset;
				}

				if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) {
					// push our current directory state onto the stack
					ifdstack.push(ifdp);
					// bump to the next entry
					de++;
					destack.push(de);

					// push our current metadata model
					modelstack.push(md_model);

					// push new state onto of stack to cause a jump
					ifdstack.push(next_ifd);
					destack.push(0);

					// select a new metadata model
					modelstack.push(next_mdmodel);
					
					// delete the tag as it won't be stored nor deleted in the for() loop
					FreeImage_DeleteTag(tag);
					
					break; // break out of the for loop
				}
				else {
					// unsupported camera model, canon maker tag or or something unknown
					// process as a standard tag
					processExifTag(dib, tag, pval, msb_order, md_model);
				}			

			} else {
				// process as a standard tag
				processExifTag(dib, tag, pval, msb_order, md_model);
			}
			
			// delete the tag
			FreeImage_DeleteTag(tag);

        } // for(nde)

		// additional thumbnail data is skipped

    } while (!destack.empty()); 


	return TRUE;
}
Beispiel #10
0
/**
Process a standard Exif tag
*/
static void 
processExifTag(FIBITMAP *dib, FITAG *tag, char *pval, BOOL msb_order, TagLib::MDMODEL md_model) {
	char defaultKey[16];
	int n;
	DWORD i;

	// allocate a buffer to store the tag value
	BYTE *exif_value = (BYTE*)malloc(FreeImage_GetTagLength(tag) * sizeof(BYTE));
	memset(exif_value, 0, FreeImage_GetTagLength(tag) * sizeof(BYTE));

	// get the tag value
	switch(FreeImage_GetTagType(tag)) {

		case FIDT_SHORT:
		{
			WORD *value = (WORD*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadUint16(msb_order, pval + i * sizeof(WORD));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SSHORT:
		{
			short *value = (short*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadInt16(msb_order, pval + i * sizeof(short));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_LONG:
		{
			DWORD *value = (DWORD*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadUint32(msb_order, pval + i * sizeof(DWORD));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SLONG:
		{
			LONG *value = (LONG*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadInt32(msb_order, pval + i * sizeof(LONG));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_RATIONAL:
		{
			n = sizeof(DWORD);

			DWORD *value = (DWORD*)&exif_value[0];						
			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
				// read a sequence of (numerator, denominator)
				value[i] = ReadUint32(msb_order, n*i + (char*)pval);
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SRATIONAL:
		{
			n = sizeof(LONG);

			LONG *value = (LONG*)&exif_value[0];
			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
				// read a sequence of (numerator, denominator)
				value[i] = ReadInt32(msb_order, n*i + (char*)pval);
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_BYTE:
		case FIDT_ASCII:
		case FIDT_SBYTE:
		case FIDT_UNDEFINED:
		case FIDT_FLOAT:
		case FIDT_DOUBLE:
		default:
			FreeImage_SetTagValue(tag, pval);
			break;
	}

	if(md_model == TagLib::EXIF_MAKERNOTE_CANON) {
		// A single Canon tag can have multiple values within
		processCanonMakerNoteTag(dib, tag);
	}
	else {
		TagLib& s = TagLib::instance();

		WORD tag_id = FreeImage_GetTagID(tag);

		// get the tag key and description
		const char *key = s.getTagFieldName(md_model, tag_id, defaultKey);
		FreeImage_SetTagKey(tag, key);
		const char *description = s.getTagDescription(md_model, tag_id);
		FreeImage_SetTagDescription(tag, description);

		// store the tag
		if(key) {
			FreeImage_SetMetadata(s.getFreeImageModel(md_model), dib, key, tag);
		}
	}
	

	// free the temporary buffer
	free(exif_value);

}
Beispiel #11
0
BOOL DLL_CALLCONV 
FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
	if(!dib) 
		return FALSE;

	TAGMAP *tagmap = NULL;

	// get the metadata model
	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
	METADATAMAP::iterator model_iterator = metadata->find(model);
	if (model_iterator != metadata->end()) {
		tagmap = model_iterator->second;
	}

	if(key != NULL) {

		if(!tagmap) {
			// this model, doesn't exist: create it 
			tagmap = new(std::nothrow) TAGMAP();
			(*metadata)[model] = tagmap;
		}
		
		if(tag) {
			// first check the tag
			if(FreeImage_GetTagKey(tag) == NULL) {
				FreeImage_SetTagKey(tag, key);
			} else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
				// set the tag key
				FreeImage_SetTagKey(tag, key);
			}
			if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
				FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key);
				return FALSE;
			}

			// fill the tag ID if possible and if it's needed
			TagLib& tag_lib = TagLib::instance();
			switch(model) {
				case FIMD_IPTC:
				{
					int id = tag_lib.getTagID(TagLib::IPTC, key);
					/*
					if(id == -1) {
						FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key);
					}
					*/
					FreeImage_SetTagID(tag, (WORD)id);
				}
				break;

				default:
					break;
			}

			// delete existing tag
			FITAG *old_tag = (*tagmap)[key];
			if(old_tag) {
				FreeImage_DeleteTag(old_tag);
			}

			// create a new tag
			(*tagmap)[key] = FreeImage_CloneTag(tag);
		}
		else {
			// delete existing tag
			TAGMAP::iterator i = tagmap->find(key);
			if(i != tagmap->end()) {
				FITAG *old_tag = (*i).second;
				FreeImage_DeleteTag(old_tag);
				tagmap->erase(key);
			}
		}
	}
	else {
		// destroy the metadata model
		if(tagmap) {
			for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
				FITAG *tag = (*i).second;
				FreeImage_DeleteTag(tag);
			}

			delete tagmap;
			metadata->erase(model_iterator);
		}
	}

	return TRUE;
}
Beispiel #12
0
/**
	Process Exif directory

	@param dib Input FIBITMAP
	@param tiffp Pointer to the TIFF header
	@param offset 0th IFD offset
	@param length Length of the datafile
	@param msb_order Endianess order of the datafile
	@return
*/
static BOOL
jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) {
    WORD de, nde;

    std::stack<WORD>			destack;	// directory entries stack
    std::stack<const BYTE*>		ifdstack;	// IFD stack
    std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack

    // Keep a list of already visited IFD to avoid stack overflows
    // when recursive/cyclic directory structures exist.
    // This kind of recursive Exif file was encountered with Kodak images coming from
    // KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
    std::map<DWORD, int> visitedIFD;

    /*
    "An Image File Directory (IFD) consists of a 2-byte count of the number of directory
    entries (i.e. the number of fields), followed by a sequence of 12-byte field
    entries, followed by a 4-byte offset of the next IFD (or 0 if none)."
    The "next IFD" (1st IFD) is the thumbnail.
    */
#define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))

    // set the metadata model to Exif

    TagLib::MDMODEL md_model = TagLib::EXIF_MAIN;

    // set the pointer to the first IFD (0th IFD) and follow it were it leads.

    const BYTE *ifd0th = (BYTE*)tiffp + offset;

    const BYTE *ifdp = ifd0th;

    de = 0;

    do {
        // if there is anything on the stack then pop it off
        if(!destack.empty()) {
            ifdp		= ifdstack.top();
            ifdstack.pop();
            de			= destack.top();
            destack.pop();
            md_model	= modelstack.top();
            modelstack.pop();
        }

        // remember that we've visited this directory and entry so that we don't visit it again later
        DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
        if(visitedIFD.find(visited) != visitedIFD.end()) {
            continue;
        } else {
            visitedIFD[visited] = 1;	// processed
        }

        // determine how many entries there are in the current IFD
        nde = ReadUint16(msb_order, ifdp);

        for(; de < nde; de++) {
            char *pde = NULL;	// pointer to the directory entry
            char *pval = NULL;	// pointer to the tag value

            // create a tag
            FITAG *tag = FreeImage_CreateTag();
            if(!tag) return FALSE;

            // point to the directory entry
            pde = (char*) DIR_ENTRY_ADDR(ifdp, de);

            // get the tag ID
            FreeImage_SetTagID(tag, ReadUint16(msb_order, pde));
            // get the tag type
            WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
                // a problem occured : delete the tag (not free'd after)
                FreeImage_DeleteTag(tag);
                // break out of the for loop
                break;
            }
            FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);

            // get number of components
            FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4));
            // check that tag length (size of the tag value in bytes) will fit in a DWORD
            unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag));
            if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) {
                FreeImage_DeleteTag(tag);
                // jump to next entry
                continue;
            }
            FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * tag_data_width);

            if(FreeImage_GetTagLength(tag) <= 4) {
                // 4 bytes or less and value is in the dir entry itself
                pval = pde + 8;
            } else {
                // if its bigger than 4 bytes, the directory entry contains an offset
                // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
                DWORD offset_value = ReadUint32(msb_order, pde + 8);
                if(offset_value > length) {
                    // a problem occured : delete the tag (not free'd after)
                    FreeImage_DeleteTag(tag);
                    // jump to next entry
                    continue;
                }
                // now check that length does not exceed the buffer size
                if(FreeImage_GetTagLength(tag) > length - offset_value) {
                    // a problem occured : delete the tag (not free'd after)
                    FreeImage_DeleteTag(tag);
                    // jump to next entry
                    continue;
                }
                pval = (char*)(tiffp + offset_value);
            }

            // check for a IFD offset
            BOOL isIFDOffset = FALSE;
            switch(FreeImage_GetTagID(tag)) {
            case TAG_EXIF_OFFSET:
            case TAG_GPS_OFFSET:
            case TAG_INTEROP_OFFSET:
            case TAG_MAKER_NOTE:
                isIFDOffset = TRUE;
                break;
            }
            if(isIFDOffset)	{
                DWORD sub_offset = 0;
                TagLib::MDMODEL next_mdmodel = md_model;
                const BYTE *next_ifd = ifdp;

                // get offset and metadata model
                if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
                    processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
                    next_ifd = (BYTE*)pval + sub_offset;
                } else {
                    processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
                    next_ifd = (BYTE*)tiffp + sub_offset;
                }

                if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) {
                    // push our current directory state onto the stack
                    ifdstack.push(ifdp);
                    // bump to the next entry
                    de++;
                    destack.push(de);

                    // push our current metadata model
                    modelstack.push(md_model);

                    // push new state onto of stack to cause a jump
                    ifdstack.push(next_ifd);
                    destack.push(0);

                    // select a new metadata model
                    modelstack.push(next_mdmodel);

                    // delete the tag as it won't be stored nor deleted in the for() loop
                    FreeImage_DeleteTag(tag);

                    break; // break out of the for loop
                }
                else {
                    // unsupported camera model, canon maker tag or something unknown
                    // process as a standard tag
                    processExifTag(dib, tag, pval, msb_order, md_model);
                }

            } else {
                // process as a standard tag
                processExifTag(dib, tag, pval, msb_order, md_model);
            }

            // delete the tag
            FreeImage_DeleteTag(tag);

        } // for(nde)

        // additional thumbnail data is skipped

    } while (!destack.empty());

    //
    // --- handle thumbnail data ---
    //

    const WORD entriesCount0th = ReadUint16(msb_order, ifd0th);

    DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th));
    if((next_offset == 0) || (next_offset >= length)) {
        return TRUE; //< no thumbnail
    }

    const BYTE* const ifd1st = (BYTE*)tiffp + next_offset;
    const WORD entriesCount1st = ReadUint16(msb_order, ifd1st);

    unsigned thCompression = 0;
    unsigned thOffset = 0;
    unsigned thSize = 0;

    for(int e = 0; e < entriesCount1st; e++) {

        // point to the directory entry
        const BYTE* base = DIR_ENTRY_ADDR(ifd1st, e);

        // check for buffer overflow
        const size_t remaining = (size_t)base + 12 - (size_t)tiffp;
        if(remaining >= length) {
            // bad IFD1 directory, ignore it
            return FALSE;
        }

        // get the tag ID
        WORD tag = ReadUint16(msb_order, base);
        // get the tag type
        WORD type = ReadUint16(msb_order, base + sizeof(WORD));
        // get number of components
        DWORD count = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
        // get the tag value
        DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD));

        switch(tag) {
        case TAG_COMPRESSION:
            // Tiff Compression Tag (should be COMPRESSION_OJPEG (6), but is not always respected)
            thCompression = offset;
            break;
        case TAG_JPEG_INTERCHANGE_FORMAT:
            // Tiff JPEGInterchangeFormat Tag
            thOffset = offset;
            break;
        case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
            // Tiff JPEGInterchangeFormatLength Tag
            thSize = offset;
            break;
        // ### X and Y Resolution ignored, orientation ignored
        case TAG_X_RESOLUTION:		// XResolution
        case TAG_Y_RESOLUTION:		// YResolution
        case TAG_RESOLUTION_UNIT:	// ResolutionUnit
        case TAG_ORIENTATION:		// Orientation
            break;
        default:
            break;
        }
    }

    if(/*thCompression != 6 ||*/ thOffset == 0 || thSize == 0) {
        return TRUE;
    }

    if(thOffset + thSize > length) {
        return TRUE;
    }

    // load the thumbnail

    const BYTE *thLocation = tiffp + thOffset;

    FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(thLocation), thSize);
    FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
    FreeImage_CloseMemory(hmem);

    // store the thumbnail
    FreeImage_SetThumbnail(dib, thumbnail);
    // then delete it
    FreeImage_Unload(thumbnail);

    return TRUE;
}
Beispiel #13
0
/**
Write a metadata model as a TIF IFD to a FIMEMORY handle.
The entries in the TIF IFD are sorted in ascending order by tag id.	
The last entry is written as 0 (4 bytes) which means no more IFD to follow. 
Supported metadata models are
<ul>
<li>FIMD_EXIF_MAIN
<li>FIMD_EXIF_EXIF
<li>FIMD_EXIF_GPS
<li>FIMD_EXIF_INTEROP
</ul>
The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset)

@param dib Input FIBITMAP
@param md_model Metadata model to write
@param hmem Memory handle
@return Returns TRUE if successful, FALSE otherwise
@see tiff_get_ifd_profile
*/
static BOOL
tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) {
	FITAG *tag = NULL;
	FIMETADATA *mdhandle = NULL;
	std::vector<FITAG*> vTagList;
	TagLib::MDMODEL internal_md_model;

	DWORD ifd_offset = 0;	// WORD-aligned IFD value offset

	const BYTE empty_byte = 0;

	// start of the file
	const long start_of_file = FreeImage_TellMemory(hmem);

	// get the metadata count
	unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib);
	if(metadata_count == 0) {
		return FALSE;
	}

	TagLib& s = TagLib::instance();

	// check for supported metadata models
	switch(md_model) {
		case FIMD_EXIF_MAIN:
			internal_md_model = TagLib::EXIF_MAIN;
			break;
		case FIMD_EXIF_EXIF:
			internal_md_model = TagLib::EXIF_EXIF;
			break;
		case FIMD_EXIF_GPS:
			internal_md_model = TagLib::EXIF_GPS;
			break;
		case FIMD_EXIF_INTEROP:
			internal_md_model = TagLib::EXIF_INTEROP;
			break;
		default:
			return FALSE;
	}

	try {
		// 1) according to the TIFF specifications, 
		// the entries in a TIF IFD must be sorted in ascending order by tag id

		// store the tags into a vector
		vTagList.reserve(metadata_count);
		mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag);
		if(mdhandle) {
			// parse the tags and store them inside vTagList
			do {
				// rewrite the tag id using FreeImage internal database
				// (in case the tag id is wrong or missing)
				const char *key = FreeImage_GetTagKey(tag);
				int tag_id = s.getTagID(internal_md_model, key);
				if(tag_id != -1) {
					// this is a known tag, set the tag ID
					FreeImage_SetTagID(tag, (WORD)tag_id);
					// record the tag
					vTagList.push_back(tag);
				}
				// else ignore this tag
			} while(FreeImage_FindNextMetadata(mdhandle, &tag));

			FreeImage_FindCloseMetadata(mdhandle);

			// sort the vector by tag id
			std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare());

			// update the metadata_count
			metadata_count = (unsigned)vTagList.size();

		} else {
			throw(1);
		}

		// 2) prepare the place for each IFD entries.

		/*
		An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), 
		followed by a sequence of 12-byte field entries, 
		followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD.
		*/

		{		
			// prepare place for 2 bytes for number of entries + 12 bytes for each entry
			unsigned ifd_size = 2 + 12 * metadata_count;
			FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem);
			// record the offset used to write values > 4-bytes
			ifd_offset = FreeImage_TellMemory(hmem);
			// rewind
			FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET);
		}

		// 3) write each IFD entry in tag id ascending order

		// number of directory entries
		WORD nde = (WORD)metadata_count;
		FreeImage_WriteMemory(&nde, 1, 2, hmem);

		// for each entry ...
		for(unsigned i = 0; i < metadata_count; i++) {
			FITAG *tag = vTagList[i];
			// tag id
			WORD tag_id = FreeImage_GetTagID(tag);
			FreeImage_WriteMemory(&tag_id, 1, 2, hmem);
			// tag type (compliant with TIFF specification)
			WORD tag_type = (WORD)FreeImage_GetTagType(tag);
			FreeImage_WriteMemory(&tag_type, 1, 2, hmem);
			// tag count
			DWORD tag_count = FreeImage_GetTagCount(tag);
			FreeImage_WriteMemory(&tag_count, 1, 4, hmem);
			// tag value or offset (results are in BYTE's units)
			unsigned tag_length = FreeImage_GetTagLength(tag);
			if(tag_length <= 4) {
				// 4 bytes or less, write the value (left justified)
				const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag);
				FreeImage_WriteMemory(tag_value, 1, tag_length, hmem);
				for(unsigned k = tag_length; k < 4; k++) {
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
			} else {
				// write an offset
				FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem);
				// write the value
				long current_position = FreeImage_TellMemory(hmem);
				FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
				FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem);
				if(tag_length & 1) {
					// align to the next WORD boundary
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
				// next offset to use
				ifd_offset = FreeImage_TellMemory(hmem);
				// rewind
				FreeImage_SeekMemory(hmem, current_position, SEEK_SET);
			}
		}

		// end-of-IFD or next IFD (0 == none)
		FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
		FreeImage_WriteMemory(&empty_byte, 1, 4, hmem);

		return TRUE;
	}
	catch(int) {
		return FALSE;
	}
}