summaryrefslogtreecommitdiff
path: root/plugins/FreeImage/src/Metadata/IPTC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/FreeImage/src/Metadata/IPTC.cpp')
-rw-r--r--plugins/FreeImage/src/Metadata/IPTC.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/plugins/FreeImage/src/Metadata/IPTC.cpp b/plugins/FreeImage/src/Metadata/IPTC.cpp
new file mode 100644
index 0000000000..817beddf98
--- /dev/null
+++ b/plugins/FreeImage/src/Metadata/IPTC.cpp
@@ -0,0 +1,325 @@
+// ==========================================================
+// Metadata functions implementation
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// 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 "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+
+// ----------------------------------------------------------
+// IPTC JPEG / TIFF markers routines
+// ----------------------------------------------------------
+
+static const char* IPTC_DELIMITER = ";"; // keywords/supplemental category delimiter
+/**
+ Read and decode IPTC binary data
+*/
+BOOL
+read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+ char defaultKey[16];
+ size_t length = datalen;
+ BYTE *profile = (BYTE*)dataptr;
+
+ std::string Keywords;
+ std::string SupplementalCategory;
+
+ WORD tag_id;
+
+ if (!dataptr || (datalen == 0)) {
+ return FALSE;
+ }
+
+ // create a tag
+
+ FITAG *tag = FreeImage_CreateTag();
+
+ TagLib& tag_lib = TagLib::instance();
+
+ // find start of the BIM portion of the binary data
+ size_t offset = 0;
+ while(offset < length - 1) {
+ if ((profile[offset] == 0x1C) && (profile[offset+1] == 0x02))
+ break;
+ offset++;
+ }
+
+ // for each tag
+ while (offset < length) {
+
+ // identifies start of a tag
+ if (profile[offset] != 0x1c) {
+ break;
+ }
+ // we need at least five bytes left to read a tag
+ if ((offset + 5) >= length) {
+ break;
+ }
+
+ offset++;
+
+ int directoryType = profile[offset++];
+ int tagType = profile[offset++];;
+ int tagByteCount = ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF);
+ offset += 2;
+
+ if ((offset + tagByteCount) > length) {
+ // data for tag extends beyond end of iptc segment
+ break;
+ }
+
+ // process the tag
+
+ tag_id = (WORD)(tagType | (directoryType << 8));
+
+ FreeImage_SetTagID(tag, tag_id);
+ FreeImage_SetTagLength(tag, tagByteCount);
+
+ // allocate a buffer to store the tag value
+ BYTE *iptc_value = (BYTE*)malloc((tagByteCount + 1) * sizeof(BYTE));
+ memset(iptc_value, 0, (tagByteCount + 1) * sizeof(BYTE));
+
+ // get the tag value
+
+ switch (tag_id) {
+ case TAG_RECORD_VERSION:
+ {
+ // short
+ FreeImage_SetTagType(tag, FIDT_SSHORT);
+ FreeImage_SetTagCount(tag, 1);
+ short *pvalue = (short*)&iptc_value[0];
+ *pvalue = (short)((profile[offset] << 8) | profile[offset + 1]);
+ FreeImage_SetTagValue(tag, pvalue);
+ break;
+ }
+
+ case TAG_RELEASE_DATE:
+ case TAG_DATE_CREATED:
+ // Date object
+ case TAG_RELEASE_TIME:
+ case TAG_TIME_CREATED:
+ // time
+ default:
+ {
+ // string
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagCount(tag, tagByteCount);
+ for(int i = 0; i < tagByteCount; i++) {
+ iptc_value[i] = profile[offset + i];
+ }
+ iptc_value[tagByteCount] = '\0';
+ FreeImage_SetTagValue(tag, (char*)&iptc_value[0]);
+ break;
+ }
+ }
+
+ if(tag_id == TAG_SUPPLEMENTAL_CATEGORIES) {
+ // concatenate the categories
+ if(SupplementalCategory.length() == 0) {
+ SupplementalCategory.append((char*)iptc_value);
+ } else {
+ SupplementalCategory.append(IPTC_DELIMITER);
+ SupplementalCategory.append((char*)iptc_value);
+ }
+ }
+ else if(tag_id == TAG_KEYWORDS) {
+ // concatenate the keywords
+ if(Keywords.length() == 0) {
+ Keywords.append((char*)iptc_value);
+ } else {
+ Keywords.append(IPTC_DELIMITER);
+ Keywords.append((char*)iptc_value);
+ }
+ }
+ else {
+ // get the tag key and description
+ const char *key = tag_lib.getTagFieldName(TagLib::IPTC, tag_id, defaultKey);
+ FreeImage_SetTagKey(tag, key);
+ const char *description = tag_lib.getTagDescription(TagLib::IPTC, tag_id);
+ FreeImage_SetTagDescription(tag, description);
+
+ // store the tag
+ if(key) {
+ FreeImage_SetMetadata(FIMD_IPTC, dib, key, tag);
+ }
+ }
+
+ free(iptc_value);
+
+ // next tag
+ offset += tagByteCount;
+
+ }
+
+ // store the 'keywords' tag
+ if(Keywords.length()) {
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagID(tag, TAG_KEYWORDS);
+ FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_KEYWORDS, defaultKey));
+ FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_KEYWORDS));
+ FreeImage_SetTagLength(tag, (DWORD)Keywords.length());
+ FreeImage_SetTagCount(tag, (DWORD)Keywords.length());
+ FreeImage_SetTagValue(tag, (char*)Keywords.c_str());
+ FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
+ }
+
+ // store the 'supplemental category' tag
+ if(SupplementalCategory.length()) {
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagID(tag, TAG_SUPPLEMENTAL_CATEGORIES);
+ FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES, defaultKey));
+ FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES));
+ FreeImage_SetTagLength(tag, (DWORD)SupplementalCategory.length());
+ FreeImage_SetTagCount(tag, (DWORD)SupplementalCategory.length());
+ FreeImage_SetTagValue(tag, (char*)SupplementalCategory.c_str());
+ FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
+ }
+
+ // delete the tag
+
+ FreeImage_DeleteTag(tag);
+
+ return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static BYTE*
+append_iptc_tag(BYTE *profile, unsigned *profile_size, WORD id, DWORD length, const void *value) {
+ BYTE *buffer = NULL;
+
+ // calculate the new buffer size
+ size_t buffer_size = (5 + *profile_size + length) * sizeof(BYTE);
+ buffer = (BYTE*)malloc(buffer_size);
+ if (!buffer)
+ return NULL;
+
+ // add the header
+ buffer[0] = 0x1C;
+ buffer[1] = 0x02;
+ // add the tag type
+ buffer[2] = (BYTE)(id & 0x00FF);
+ // add the tag length
+ buffer[3] = (BYTE)(length >> 8);
+ buffer[4] = (BYTE)(length & 0xFF);
+ // add the tag value
+ memcpy(buffer + 5, (BYTE*)value, length);
+ // append the previous profile
+ if(NULL == profile) {
+ *profile_size = (5 + length);
+ }
+ else {
+ memcpy(buffer + 5 + length, profile, *profile_size);
+ *profile_size += (5 + length);
+ free(profile);
+ }
+
+ return buffer;
+}
+
+/**
+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;
+}