1 // ==========================================================
2 // Metadata functions implementation
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 //
7 // This file is part of FreeImage 3
8 //
9 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17 // THIS DISCLAIMER.
18 //
19 // Use at your own risk!
20 // ==========================================================
21
22 #ifdef _MSC_VER
23 #pragma warning (disable : 4786) // identifier was truncated to 'number' characters
24 #endif
25
26 #include "FreeImage.h"
27 #include "Utilities.h"
28 #include "FreeImageTag.h"
29
30 // ----------------------------------------------------------
31 // IPTC JPEG / TIFF markers routines
32 // ----------------------------------------------------------
33
34 static const char* IPTC_DELIMITER = ";"; // keywords/supplemental category delimiter
35 /**
36 Read and decode IPTC binary data
37 */
38 BOOL
read_iptc_profile(FIBITMAP * dib,const BYTE * dataptr,unsigned int datalen)39 read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
40 char defaultKey[16];
41 size_t length = datalen;
42 BYTE *profile = (BYTE*)dataptr;
43
44 const char *JPEG_AdobeCM_Tag = "Adobe_CM";
45
46 std::string Keywords;
47 std::string SupplementalCategory;
48
49 WORD tag_id;
50
51 if(!dataptr || (datalen == 0)) {
52 return FALSE;
53 }
54
55 if(datalen > 8) {
56 if(memcmp(JPEG_AdobeCM_Tag, dataptr, 8) == 0) {
57 // the "Adobe_CM" APP13 segment presumably contains color management information,
58 // but the meaning of the data is currently unknown.
59 // If anyone has an idea about what this means, please let me know.
60 return FALSE;
61 }
62 }
63
64
65 // create a tag
66
67 FITAG *tag = FreeImage_CreateTag();
68
69 TagLib& tag_lib = TagLib::instance();
70
71 // find start of the BIM portion of the binary data
72 size_t offset = 0;
73 while(offset < length - 1) {
74 if((profile[offset] == 0x1C) && (profile[offset+1] == 0x02))
75 break;
76 offset++;
77 }
78
79 // for each tag
80 while (offset < length) {
81
82 // identifies start of a tag
83 if (profile[offset] != 0x1c) {
84 break;
85 }
86 // we need at least five bytes left to read a tag
87 if ((offset + 5) >= length) {
88 break;
89 }
90
91 offset++;
92
93 int directoryType = profile[offset++];
94 int tagType = profile[offset++];;
95 int tagByteCount = ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF);
96 offset += 2;
97
98 if ((offset + tagByteCount) > length) {
99 // data for tag extends beyond end of iptc segment
100 break;
101 }
102
103 if(tagByteCount == 0) {
104 // go to next tag
105 continue;
106 }
107
108 // process the tag
109
110 tag_id = (WORD)(tagType | (directoryType << 8));
111
112 FreeImage_SetTagID(tag, tag_id);
113 FreeImage_SetTagLength(tag, tagByteCount);
114
115 // allocate a buffer to store the tag value
116 BYTE *iptc_value = (BYTE*)malloc((tagByteCount + 1) * sizeof(BYTE));
117 memset(iptc_value, 0, (tagByteCount + 1) * sizeof(BYTE));
118
119 // get the tag value
120
121 switch (tag_id) {
122 case TAG_RECORD_VERSION:
123 {
124 // short
125 FreeImage_SetTagType(tag, FIDT_SSHORT);
126 FreeImage_SetTagCount(tag, 1);
127 short *pvalue = (short*)&iptc_value[0];
128 *pvalue = (short)((profile[offset] << 8) | profile[offset + 1]);
129 FreeImage_SetTagValue(tag, pvalue);
130 break;
131 }
132
133 case TAG_RELEASE_DATE:
134 case TAG_DATE_CREATED:
135 // Date object
136 case TAG_RELEASE_TIME:
137 case TAG_TIME_CREATED:
138 // time
139 default:
140 {
141 // string
142 FreeImage_SetTagType(tag, FIDT_ASCII);
143 FreeImage_SetTagCount(tag, tagByteCount);
144 for(int i = 0; i < tagByteCount; i++) {
145 iptc_value[i] = profile[offset + i];
146 }
147 iptc_value[tagByteCount] = '\0';
148 FreeImage_SetTagValue(tag, (char*)&iptc_value[0]);
149 break;
150 }
151 }
152
153 if(tag_id == TAG_SUPPLEMENTAL_CATEGORIES) {
154 // concatenate the categories
155 if(SupplementalCategory.length() == 0) {
156 SupplementalCategory.append((char*)iptc_value);
157 } else {
158 SupplementalCategory.append(IPTC_DELIMITER);
159 SupplementalCategory.append((char*)iptc_value);
160 }
161 }
162 else if(tag_id == TAG_KEYWORDS) {
163 // concatenate the keywords
164 if(Keywords.length() == 0) {
165 Keywords.append((char*)iptc_value);
166 } else {
167 Keywords.append(IPTC_DELIMITER);
168 Keywords.append((char*)iptc_value);
169 }
170 }
171 else {
172 // get the tag key and description
173 const char *key = tag_lib.getTagFieldName(TagLib::IPTC, tag_id, defaultKey);
174 FreeImage_SetTagKey(tag, key);
175 const char *description = tag_lib.getTagDescription(TagLib::IPTC, tag_id);
176 FreeImage_SetTagDescription(tag, description);
177
178 // store the tag
179 if(key) {
180 FreeImage_SetMetadata(FIMD_IPTC, dib, key, tag);
181 }
182 }
183
184 free(iptc_value);
185
186 // next tag
187 offset += tagByteCount;
188
189 }
190
191 // store the 'keywords' tag
192 if(Keywords.length()) {
193 FreeImage_SetTagType(tag, FIDT_ASCII);
194 FreeImage_SetTagID(tag, TAG_KEYWORDS);
195 FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_KEYWORDS, defaultKey));
196 FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_KEYWORDS));
197 FreeImage_SetTagLength(tag, (DWORD)Keywords.length());
198 FreeImage_SetTagCount(tag, (DWORD)Keywords.length());
199 FreeImage_SetTagValue(tag, (char*)Keywords.c_str());
200 FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
201 }
202
203 // store the 'supplemental category' tag
204 if(SupplementalCategory.length()) {
205 FreeImage_SetTagType(tag, FIDT_ASCII);
206 FreeImage_SetTagID(tag, TAG_SUPPLEMENTAL_CATEGORIES);
207 FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES, defaultKey));
208 FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES));
209 FreeImage_SetTagLength(tag, (DWORD)SupplementalCategory.length());
210 FreeImage_SetTagCount(tag, (DWORD)SupplementalCategory.length());
211 FreeImage_SetTagValue(tag, (char*)SupplementalCategory.c_str());
212 FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
213 }
214
215 // delete the tag
216
217 FreeImage_DeleteTag(tag);
218
219 return TRUE;
220 }
221
222 // --------------------------------------------------------------------------
223
224 static BYTE*
append_iptc_tag(BYTE * profile,unsigned * profile_size,WORD id,DWORD length,const void * value)225 append_iptc_tag(BYTE *profile, unsigned *profile_size, WORD id, DWORD length, const void *value) {
226 BYTE *buffer = NULL;
227
228 // calculate the new buffer size
229 size_t buffer_size = (5 + *profile_size + length) * sizeof(BYTE);
230 buffer = (BYTE*)malloc(buffer_size);
231 if(!buffer)
232 return NULL;
233
234 // add the header
235 buffer[0] = 0x1C;
236 buffer[1] = 0x02;
237 // add the tag type
238 buffer[2] = (BYTE)(id & 0x00FF);
239 // add the tag length
240 buffer[3] = (BYTE)(length >> 8);
241 buffer[4] = (BYTE)(length & 0xFF);
242 // add the tag value
243 memcpy(buffer + 5, (BYTE*)value, length);
244 // append the previous profile
245 if(NULL == profile) {
246 *profile_size = (5 + length);
247 }
248 else {
249 memcpy(buffer + 5 + length, profile, *profile_size);
250 *profile_size += (5 + length);
251 free(profile);
252 }
253
254 return buffer;
255 }
256
257 /**
258 Encode IPTC metadata into a binary buffer.
259 The buffer is allocated by the function and must be freed by the caller.
260 */
261 BOOL
write_iptc_profile(FIBITMAP * dib,BYTE ** profile,unsigned * profile_size)262 write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) {
263 FITAG *tag = NULL;
264 FIMETADATA *mdhandle = NULL;
265
266 BYTE *buffer = NULL;
267 unsigned buffer_size = 0;
268
269 // parse all IPTC tags and rebuild a IPTC profile
270 mdhandle = FreeImage_FindFirstMetadata(FIMD_IPTC, dib, &tag);
271
272 if(mdhandle) {
273 do {
274 WORD tag_id = FreeImage_GetTagID(tag);
275
276 // append the tag to the profile
277
278 switch(tag_id) {
279 case TAG_RECORD_VERSION:
280 // ignore (already handled)
281 break;
282
283 case TAG_SUPPLEMENTAL_CATEGORIES:
284 case TAG_KEYWORDS:
285 if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
286 std::string value = (const char*)FreeImage_GetTagValue(tag);
287
288 // split the tag value
289 std::vector<std::string> output;
290 std::string delimiter = IPTC_DELIMITER;
291
292 size_t offset = 0;
293 size_t delimiterIndex = 0;
294
295 delimiterIndex = value.find(delimiter, offset);
296 while (delimiterIndex != std::string::npos) {
297 output.push_back(value.substr(offset, delimiterIndex - offset));
298 offset += delimiterIndex - offset + delimiter.length();
299 delimiterIndex = value.find(delimiter, offset);
300 }
301 output.push_back(value.substr(offset));
302
303 // add as many tags as there are comma separated strings
304 for(int i = 0; i < (int)output.size(); i++) {
305 std::string& tag_value = output[i];
306 buffer = append_iptc_tag(buffer, &buffer_size, tag_id, (DWORD)tag_value.length(), tag_value.c_str());
307 }
308
309 }
310 break;
311
312 case TAG_URGENCY:
313 if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
314 DWORD length = 1; // keep the first octet only
315 buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
316 }
317 break;
318
319 default:
320 if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
321 DWORD length = FreeImage_GetTagLength(tag);
322 buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
323 }
324 break;
325 }
326
327 } while(FreeImage_FindNextMetadata(mdhandle, &tag));
328
329 FreeImage_FindCloseMetadata(mdhandle);
330
331 // add the DirectoryVersion tag
332 const short version = 0x0200;
333 buffer = append_iptc_tag(buffer, &buffer_size, TAG_RECORD_VERSION, sizeof(version), &version);
334
335 *profile = buffer;
336 *profile_size = buffer_size;
337
338 return TRUE;
339 }
340
341 return FALSE;
342 }
343