/src/freeimage-svn/FreeImage/trunk/Source/Metadata/IPTC.cpp
Line | Count | Source (jump to first uncovered line) |
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 |
39 | 0 | read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
40 | 0 | char defaultKey[16]; |
41 | 0 | size_t length = datalen; |
42 | 0 | BYTE *profile = (BYTE*)dataptr; |
43 | |
|
44 | 0 | const char *JPEG_AdobeCM_Tag = "Adobe_CM"; |
45 | |
|
46 | 0 | std::string Keywords; |
47 | 0 | std::string SupplementalCategory; |
48 | |
|
49 | 0 | WORD tag_id; |
50 | |
|
51 | 0 | if(!dataptr || (datalen == 0)) { |
52 | 0 | return FALSE; |
53 | 0 | } |
54 | | |
55 | 0 | if(datalen > 8) { |
56 | 0 | 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 | 0 | return FALSE; |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | |
65 | | // create a tag |
66 | | |
67 | 0 | FITAG *tag = FreeImage_CreateTag(); |
68 | |
|
69 | 0 | TagLib& tag_lib = TagLib::instance(); |
70 | | |
71 | | // find start of the BIM portion of the binary data |
72 | 0 | size_t offset = 0; |
73 | 0 | while(offset < length - 1) { |
74 | 0 | if((profile[offset] == 0x1C) && (profile[offset+1] == 0x02)) |
75 | 0 | break; |
76 | 0 | offset++; |
77 | 0 | } |
78 | | |
79 | | // for each tag |
80 | 0 | while (offset < length) { |
81 | | |
82 | | // identifies start of a tag |
83 | 0 | if (profile[offset] != 0x1c) { |
84 | 0 | break; |
85 | 0 | } |
86 | | // we need at least five bytes left to read a tag |
87 | 0 | if ((offset + 5) >= length) { |
88 | 0 | break; |
89 | 0 | } |
90 | | |
91 | 0 | offset++; |
92 | |
|
93 | 0 | int directoryType = profile[offset++]; |
94 | 0 | int tagType = profile[offset++];; |
95 | 0 | int tagByteCount = ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF); |
96 | 0 | offset += 2; |
97 | |
|
98 | 0 | if ((offset + tagByteCount) > length) { |
99 | | // data for tag extends beyond end of iptc segment |
100 | 0 | break; |
101 | 0 | } |
102 | | |
103 | 0 | if(tagByteCount == 0) { |
104 | | // go to next tag |
105 | 0 | continue; |
106 | 0 | } |
107 | | |
108 | | // process the tag |
109 | | |
110 | 0 | tag_id = (WORD)(tagType | (directoryType << 8)); |
111 | |
|
112 | 0 | FreeImage_SetTagID(tag, tag_id); |
113 | 0 | FreeImage_SetTagLength(tag, tagByteCount); |
114 | | |
115 | | // allocate a buffer to store the tag value |
116 | 0 | BYTE *iptc_value = (BYTE*)malloc((tagByteCount + 1) * sizeof(BYTE)); |
117 | 0 | memset(iptc_value, 0, (tagByteCount + 1) * sizeof(BYTE)); |
118 | | |
119 | | // get the tag value |
120 | |
|
121 | 0 | switch (tag_id) { |
122 | 0 | case TAG_RECORD_VERSION: |
123 | 0 | { |
124 | | // short |
125 | 0 | FreeImage_SetTagType(tag, FIDT_SSHORT); |
126 | 0 | FreeImage_SetTagCount(tag, 1); |
127 | 0 | short *pvalue = (short*)&iptc_value[0]; |
128 | 0 | *pvalue = (short)((profile[offset] << 8) | profile[offset + 1]); |
129 | 0 | FreeImage_SetTagValue(tag, pvalue); |
130 | 0 | break; |
131 | 0 | } |
132 | | |
133 | 0 | case TAG_RELEASE_DATE: |
134 | 0 | case TAG_DATE_CREATED: |
135 | | // Date object |
136 | 0 | case TAG_RELEASE_TIME: |
137 | 0 | case TAG_TIME_CREATED: |
138 | | // time |
139 | 0 | default: |
140 | 0 | { |
141 | | // string |
142 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
143 | 0 | FreeImage_SetTagCount(tag, tagByteCount); |
144 | 0 | for(int i = 0; i < tagByteCount; i++) { |
145 | 0 | iptc_value[i] = profile[offset + i]; |
146 | 0 | } |
147 | 0 | iptc_value[tagByteCount] = '\0'; |
148 | 0 | FreeImage_SetTagValue(tag, (char*)&iptc_value[0]); |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | 0 | if(tag_id == TAG_SUPPLEMENTAL_CATEGORIES) { |
154 | | // concatenate the categories |
155 | 0 | if(SupplementalCategory.length() == 0) { |
156 | 0 | SupplementalCategory.append((char*)iptc_value); |
157 | 0 | } else { |
158 | 0 | SupplementalCategory.append(IPTC_DELIMITER); |
159 | 0 | SupplementalCategory.append((char*)iptc_value); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | else if(tag_id == TAG_KEYWORDS) { |
163 | | // concatenate the keywords |
164 | 0 | if(Keywords.length() == 0) { |
165 | 0 | Keywords.append((char*)iptc_value); |
166 | 0 | } else { |
167 | 0 | Keywords.append(IPTC_DELIMITER); |
168 | 0 | Keywords.append((char*)iptc_value); |
169 | 0 | } |
170 | 0 | } |
171 | 0 | else { |
172 | | // get the tag key and description |
173 | 0 | const char *key = tag_lib.getTagFieldName(TagLib::IPTC, tag_id, defaultKey); |
174 | 0 | FreeImage_SetTagKey(tag, key); |
175 | 0 | const char *description = tag_lib.getTagDescription(TagLib::IPTC, tag_id); |
176 | 0 | FreeImage_SetTagDescription(tag, description); |
177 | | |
178 | | // store the tag |
179 | 0 | if(key) { |
180 | 0 | FreeImage_SetMetadata(FIMD_IPTC, dib, key, tag); |
181 | 0 | } |
182 | 0 | } |
183 | |
|
184 | 0 | free(iptc_value); |
185 | | |
186 | | // next tag |
187 | 0 | offset += tagByteCount; |
188 | |
|
189 | 0 | } |
190 | | |
191 | | // store the 'keywords' tag |
192 | 0 | if(Keywords.length()) { |
193 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
194 | 0 | FreeImage_SetTagID(tag, TAG_KEYWORDS); |
195 | 0 | FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_KEYWORDS, defaultKey)); |
196 | 0 | FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_KEYWORDS)); |
197 | 0 | FreeImage_SetTagLength(tag, (DWORD)Keywords.length()); |
198 | 0 | FreeImage_SetTagCount(tag, (DWORD)Keywords.length()); |
199 | 0 | FreeImage_SetTagValue(tag, (char*)Keywords.c_str()); |
200 | 0 | FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag); |
201 | 0 | } |
202 | | |
203 | | // store the 'supplemental category' tag |
204 | 0 | if(SupplementalCategory.length()) { |
205 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
206 | 0 | FreeImage_SetTagID(tag, TAG_SUPPLEMENTAL_CATEGORIES); |
207 | 0 | FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES, defaultKey)); |
208 | 0 | FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES)); |
209 | 0 | FreeImage_SetTagLength(tag, (DWORD)SupplementalCategory.length()); |
210 | 0 | FreeImage_SetTagCount(tag, (DWORD)SupplementalCategory.length()); |
211 | 0 | FreeImage_SetTagValue(tag, (char*)SupplementalCategory.c_str()); |
212 | 0 | FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag); |
213 | 0 | } |
214 | | |
215 | | // delete the tag |
216 | |
|
217 | 0 | FreeImage_DeleteTag(tag); |
218 | |
|
219 | 0 | return TRUE; |
220 | 0 | } |
221 | | |
222 | | // -------------------------------------------------------------------------- |
223 | | |
224 | | static BYTE* |
225 | 0 | append_iptc_tag(BYTE *profile, unsigned *profile_size, WORD id, DWORD length, const void *value) { |
226 | 0 | BYTE *buffer = NULL; |
227 | | |
228 | | // calculate the new buffer size |
229 | 0 | size_t buffer_size = (5 + *profile_size + length) * sizeof(BYTE); |
230 | 0 | buffer = (BYTE*)malloc(buffer_size); |
231 | 0 | if(!buffer) |
232 | 0 | return NULL; |
233 | | |
234 | | // add the header |
235 | 0 | buffer[0] = 0x1C; |
236 | 0 | buffer[1] = 0x02; |
237 | | // add the tag type |
238 | 0 | buffer[2] = (BYTE)(id & 0x00FF); |
239 | | // add the tag length |
240 | 0 | buffer[3] = (BYTE)(length >> 8); |
241 | 0 | buffer[4] = (BYTE)(length & 0xFF); |
242 | | // add the tag value |
243 | 0 | memcpy(buffer + 5, (BYTE*)value, length); |
244 | | // append the previous profile |
245 | 0 | if(NULL == profile) { |
246 | 0 | *profile_size = (5 + length); |
247 | 0 | } |
248 | 0 | else { |
249 | 0 | memcpy(buffer + 5 + length, profile, *profile_size); |
250 | 0 | *profile_size += (5 + length); |
251 | 0 | free(profile); |
252 | 0 | } |
253 | | |
254 | 0 | return buffer; |
255 | 0 | } |
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 |
262 | 0 | write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) { |
263 | 0 | FITAG *tag = NULL; |
264 | 0 | FIMETADATA *mdhandle = NULL; |
265 | |
|
266 | 0 | BYTE *buffer = NULL; |
267 | 0 | unsigned buffer_size = 0; |
268 | | |
269 | | // parse all IPTC tags and rebuild a IPTC profile |
270 | 0 | mdhandle = FreeImage_FindFirstMetadata(FIMD_IPTC, dib, &tag); |
271 | |
|
272 | 0 | if(mdhandle) { |
273 | 0 | do { |
274 | 0 | WORD tag_id = FreeImage_GetTagID(tag); |
275 | | |
276 | | // append the tag to the profile |
277 | |
|
278 | 0 | switch(tag_id) { |
279 | 0 | case TAG_RECORD_VERSION: |
280 | | // ignore (already handled) |
281 | 0 | break; |
282 | | |
283 | 0 | case TAG_SUPPLEMENTAL_CATEGORIES: |
284 | 0 | case TAG_KEYWORDS: |
285 | 0 | if(FreeImage_GetTagType(tag) == FIDT_ASCII) { |
286 | 0 | std::string value = (const char*)FreeImage_GetTagValue(tag); |
287 | | |
288 | | // split the tag value |
289 | 0 | std::vector<std::string> output; |
290 | 0 | std::string delimiter = IPTC_DELIMITER; |
291 | | |
292 | 0 | size_t offset = 0; |
293 | 0 | size_t delimiterIndex = 0; |
294 | |
|
295 | 0 | delimiterIndex = value.find(delimiter, offset); |
296 | 0 | while (delimiterIndex != std::string::npos) { |
297 | 0 | output.push_back(value.substr(offset, delimiterIndex - offset)); |
298 | 0 | offset += delimiterIndex - offset + delimiter.length(); |
299 | 0 | delimiterIndex = value.find(delimiter, offset); |
300 | 0 | } |
301 | 0 | output.push_back(value.substr(offset)); |
302 | | |
303 | | // add as many tags as there are comma separated strings |
304 | 0 | for(int i = 0; i < (int)output.size(); i++) { |
305 | 0 | std::string& tag_value = output[i]; |
306 | 0 | buffer = append_iptc_tag(buffer, &buffer_size, tag_id, (DWORD)tag_value.length(), tag_value.c_str()); |
307 | 0 | } |
308 | |
|
309 | 0 | } |
310 | 0 | break; |
311 | | |
312 | 0 | case TAG_URGENCY: |
313 | 0 | if(FreeImage_GetTagType(tag) == FIDT_ASCII) { |
314 | 0 | DWORD length = 1; // keep the first octet only |
315 | 0 | buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag)); |
316 | 0 | } |
317 | 0 | break; |
318 | | |
319 | 0 | default: |
320 | 0 | if(FreeImage_GetTagType(tag) == FIDT_ASCII) { |
321 | 0 | DWORD length = FreeImage_GetTagLength(tag); |
322 | 0 | buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag)); |
323 | 0 | } |
324 | 0 | break; |
325 | 0 | } |
326 | |
|
327 | 0 | } while(FreeImage_FindNextMetadata(mdhandle, &tag)); |
328 | | |
329 | 0 | FreeImage_FindCloseMetadata(mdhandle); |
330 | | |
331 | | // add the DirectoryVersion tag |
332 | 0 | const short version = 0x0200; |
333 | 0 | buffer = append_iptc_tag(buffer, &buffer_size, TAG_RECORD_VERSION, sizeof(version), &version); |
334 | | |
335 | 0 | *profile = buffer; |
336 | 0 | *profile_size = buffer_size; |
337 | |
|
338 | 0 | return TRUE; |
339 | 0 | } |
340 | | |
341 | 0 | return FALSE; |
342 | 0 | } |