No issues found
1 /*
2 * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include "tracker-iptc.h"
25 #include "tracker-utils.h"
26
27 #ifdef HAVE_LIBIPTCDATA
28
29 #include <libiptcdata/iptc-data.h>
30 #include <libiptcdata/iptc-dataset.h>
31
32 #define IPTC_DATE_FORMAT "%Y %m %d"
33
34 /**
35 * SECTION:tracker-iptc
36 * @title: IPTC
37 * @short_description: Information Interchange Model (IIM) /
38 * International Press Telecommunications Council (IPTC)
39 * @stability: Stable
40 * @include: libtracker-extract/tracker-extract.h
41 *
42 * The Information Interchange Model (IIM) is a file structure and set
43 * of metadata attributes that can be applied to text, images and
44 * other media types. It was developed in the early 1990s by the
45 * International Press Telecommunications Council (IPTC) to expedite
46 * the international exchange of news among newspapers and news
47 * agencies.
48 *
49 * The full IIM specification includes a complex data structure and a
50 * set of metadata definitions.
51 *
52 * Although IIM was intended for use with all types of news items —
53 * including simple text articles — a subset found broad worldwide
54 * acceptance as the standard embedded metadata used by news and
55 * commercial photographers. Information such as the name of the
56 * photographer, copyright information and the caption or other
57 * description can be embedded either manually or automatically.
58 *
59 * IIM metadata embedded in images are often referred to as "IPTC
60 * headers," and can be easily encoded and decoded by most popular
61 * photo editing software.
62 *
63 * The Extensible Metadata Platform (XMP) has largely superseded IIM's
64 * file structure, but the IIM image attributes are defined in the
65 * IPTC Core schema for XMP and most image manipulation programs keep
66 * the XMP and non-XMP IPTC attributes synchronized.
67 *
68 * This API is provided to remove code duplication between extractors
69 * using these standards.
70 **/
71
72 static const gchar *
73 fix_iptc_orientation (const gchar *orientation)
74 {
75 if (g_strcmp0 (orientation, "P") == 0) {
76 return "nfo:orientation-left";
77 }
78
79 return "nfo:orientation-top"; /* We take this as default */
80 }
81
82 static void
83 foreach_dataset (IptcDataSet *dataset,
84 void *user_data)
85 {
86 TrackerIptcData *data = user_data;
87 gchar mbuffer[1024];
88
89 /* The meaning of dataset->tag DEPENDS on the value of dataset->record.
90 * See iptc-tag.h for the relationship.
91 *
92 * Now, We only want record-2 tags, otherwise we'll end up mixing
93 * for example IPTC_TAG_CITY and IPTC_TAG_CHARACTER_SET, which BOTH
94 * have a value of 90. */
95 if (dataset->record != IPTC_RECORD_APP_2)
96 return;
97
98 switch (dataset->tag) {
99 case IPTC_TAG_KEYWORDS:
100 if (!data->keywords) {
101 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
102 data->keywords = g_strdup (mbuffer);
103 } else {
104 gchar *tmp = data->keywords;
105 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
106 data->keywords = g_strdup_printf ("%s, %s", data->keywords, mbuffer);
107 g_free (tmp);
108 }
109 break;
110
111 case IPTC_TAG_DATE_CREATED:
112 if (!data->date_created) {
113 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
114 /* From: ex; date "2007 04 15"
115 * To : ex. "2007-04-15T00:00:00+0200 where +0200 is offset w.r.t gmt */
116 data->date_created = tracker_date_format_to_iso8601 (mbuffer, IPTC_DATE_FORMAT);
117 }
118 break;
119
120 case IPTC_TAG_BYLINE:
121 if (!data->byline) {
122 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
123 data->byline = g_strdup (mbuffer);
124 }
125 break;
126
127 case IPTC_TAG_CREDIT:
128 if (!data->credit) {
129 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
130 data->credit = g_strdup (mbuffer);
131 }
132 break;
133
134 case IPTC_TAG_COPYRIGHT_NOTICE:
135 if (!data->copyright_notice) {
136 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
137 data->copyright_notice = g_strdup (mbuffer);
138 }
139 break;
140
141 case IPTC_TAG_IMAGE_ORIENTATION:
142 if (!data->image_orientation) {
143 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
144 data->image_orientation = g_strdup (fix_iptc_orientation (mbuffer));
145 }
146 break;
147
148 case IPTC_TAG_BYLINE_TITLE:
149 if (!data->byline_title) {
150 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
151 data->byline_title = g_strdup (mbuffer);
152 }
153 break;
154
155 case IPTC_TAG_CITY:
156 if (!data->city) {
157 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
158 data->city = g_strdup (mbuffer);
159 }
160 break;
161
162 case IPTC_TAG_STATE:
163 if (!data->state) {
164 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
165 data->state = g_strdup (mbuffer);
166 }
167 break;
168
169 case IPTC_TAG_SUBLOCATION:
170 if (!data->sublocation) {
171 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
172 data->sublocation = g_strdup (mbuffer);
173 }
174 break;
175
176 case IPTC_TAG_COUNTRY_NAME:
177 if (!data->country_name) {
178 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
179 data->country_name = g_strdup (mbuffer);
180 }
181 break;
182
183 case IPTC_TAG_CONTACT:
184 if (!data->contact) {
185 iptc_dataset_get_as_str (dataset, mbuffer, 1024);
186 data->contact = g_strdup (mbuffer);
187 }
188 break;
189
190 default:
191 break;
192 }
193 }
194
195 #endif /* HAVE_LIBIPTCDATA */
196
197 static gboolean
198 parse_iptc (const unsigned char *buffer,
199 size_t len,
200 const gchar *uri,
201 TrackerIptcData *data)
202 {
203 #ifdef HAVE_LIBIPTCDATA
204 IptcData *iptc;
205 #endif /* HAVE_LIBIPTCDATA */
206
207 memset (data, 0, sizeof (TrackerIptcData));
208
209 #ifdef HAVE_LIBIPTCDATA
210
211 /* FIXME According to valgrind this is leaking (together with the unref).
212 * Problem in libiptc (I replaced this with the _free equivalent) */
213
214 iptc = iptc_data_new ();
215
216 if (!iptc)
217 return FALSE;
218
219 if (iptc_data_load (iptc, buffer, len) < 0) {
220 iptc_data_free (iptc);
221 return FALSE;
222 }
223
224 iptc_data_foreach_dataset (iptc, foreach_dataset, data);
225 iptc_data_free (iptc);
226 #endif /* HAVE_LIBIPTCDATA */
227
228 return TRUE;
229 }
230
231 #ifndef TRACKER_DISABLE_DEPRECATED
232
233 // LCOV_EXCL_START
234
235 /**
236 * tracker_iptc_read:
237 * @buffer: a chunk of data with iptc data in it.
238 * @len: the size of @buffer.
239 * @uri: the URI this is related to.
240 * @data: a pointer to a TrackerIptcData struture to populate.
241 *
242 * This function takes @len bytes of @buffer and runs it through the
243 * IPTC library. The result is that @data is populated with the IPTC
244 * data found in @uri.
245 *
246 * Returns: %TRUE if the @data was populated successfully, otherwise
247 * %FALSE is returned.
248 *
249 * Since: 0.8
250 *
251 * Deprecated: 0.9. Use tracker_iptc_new() instead.
252 **/
253 gboolean
254 tracker_iptc_read (const unsigned char *buffer,
255 size_t len,
256 const gchar *uri,
257 TrackerIptcData *data)
258 {
259 g_return_val_if_fail (buffer != NULL, FALSE);
260 g_return_val_if_fail (len > 0, FALSE);
261 g_return_val_if_fail (uri != NULL, FALSE);
262 g_return_val_if_fail (data != NULL, FALSE);
263
264 return parse_iptc (buffer, len, uri, data);
265 }
266
267 // LCOV_EXCL_STOP
268
269 #endif /* TRACKER_DISABLE_DEPRECATED */
270
271 /**
272 * tracker_iptc_new:
273 * @buffer: a chunk of data with iptc data in it.
274 * @len: the size of @buffer.
275 * @uri: the URI this is related to.
276 *
277 * This function takes @len bytes of @buffer and runs it through the
278 * IPTC library.
279 *
280 * Returns: a newly allocated #TrackerIptcData struct if IPTC data was
281 * found, %NULL otherwise. Free the returned struct with
282 * tracker_iptc_free().
283 *
284 * Since: 0.10
285 **/
286 TrackerIptcData *
287 tracker_iptc_new (const guchar *buffer,
288 gsize len,
289 const gchar *uri)
290 {
291 TrackerIptcData *data;
292
293 g_return_val_if_fail (buffer != NULL, NULL);
294 g_return_val_if_fail (len > 0, NULL);
295 g_return_val_if_fail (uri != NULL, NULL);
296
297 data = g_new0 (TrackerIptcData, 1);
298
299 if (!parse_iptc (buffer, len, uri, data)) {
300 tracker_iptc_free (data);
301 return NULL;
302 }
303
304 return data;
305 }
306
307 /**
308 * tracker_iptc_free:
309 * @data: a #TrackerIptcData
310 *
311 * Frees @data and all #TrackerIptcData members. %NULL will produce a
312 * a warning.
313 *
314 * Since: 0.10
315 **/
316 void
317 tracker_iptc_free (TrackerIptcData *data)
318 {
319 g_return_if_fail (data != NULL);
320
321 g_free (data->keywords);
322 g_free (data->date_created);
323 g_free (data->byline);
324 g_free (data->credit);
325 g_free (data->copyright_notice);
326 g_free (data->image_orientation);
327 g_free (data->byline_title);
328 g_free (data->city);
329 g_free (data->state);
330 g_free (data->sublocation);
331 g_free (data->country_name);
332 g_free (data->contact);
333
334 g_free (data);
335 }