2002-01-06 Lutz Müller <urc8@rz.uni-karlsruhe.de>
[platform/upstream/libexif.git] / libexif / exif-data.c
1 /* exif-data.c
2  *
3  * Copyright (C) 2001 Lutz Müller <lutz@users.sourceforge.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, 
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details. 
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #include <config.h>
21 #include "exif-data.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <libjpeg/jpeg-marker.h>
28
29 #undef MAX
30 #define MAX(a, b)  (((a) > (b)) ? (a) : (b))
31
32 #define DEBUG
33
34 static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
35
36 struct _ExifDataPrivate
37 {
38         ExifByteOrder order;
39
40         unsigned int ref_count;
41 };
42
43 ExifData *
44 exif_data_new (void)
45 {
46         ExifData *data;
47
48         data = malloc (sizeof (ExifData));
49         if (!data)
50                 return (NULL);
51         memset (data, 0, sizeof (ExifData));
52         data->priv = malloc (sizeof (ExifDataPrivate));
53         if (!data->priv) {
54                 free (data);
55                 return (NULL);
56         }
57         memset (data->priv, 0, sizeof (ExifDataPrivate));
58         data->priv->ref_count = 1;
59
60         data->ifd0                 = exif_content_new ();
61         data->ifd1                 = exif_content_new ();
62         data->ifd_exif             = exif_content_new ();
63         data->ifd_gps              = exif_content_new ();
64         data->ifd_interoperability = exif_content_new ();
65         if (!data->ifd_exif || !data->ifd_gps || !data->ifd_interoperability ||
66             !data->ifd0 || !data->ifd1) {
67                 exif_data_free (data);
68                 return (NULL);
69         }
70
71         return (data);
72 }
73
74 ExifData *
75 exif_data_new_from_data (const unsigned char *data, unsigned int size)
76 {
77         ExifData *edata;
78
79         edata = exif_data_new ();
80         exif_data_load_data (edata, data, size);
81         return (edata);
82 }
83
84 static void
85 exif_data_load_data_entry (ExifData *data, ExifEntry *entry,
86                            const unsigned char *d,
87                            unsigned int size, unsigned int offset)
88 {
89         unsigned int s, doff;
90
91         entry->order = data->priv->order;
92         entry->tag        = exif_get_short (d + offset + 0, data->priv->order);
93         entry->format     = exif_get_short (d + offset + 2, data->priv->order);
94         entry->components = exif_get_long  (d + offset + 4, data->priv->order);
95
96         /*
97          * Size? If bigger than 4 bytes, the actual data is not
98          * in the entry but somewhere else (offset).
99          */
100         s = exif_format_get_size (entry->format) * entry->components;
101         if (!s)
102                 return;
103         if (s > 4)
104                 doff = exif_get_long (d + offset + 8, data->priv->order);
105         else
106                 doff = offset + 8;
107
108         /* Sanity check */
109         if (size < doff + s)
110                 return;
111
112         entry->data = malloc (sizeof (char) * s);
113         if (!entry->data)
114                 return;
115         entry->size = s;
116         memcpy (entry->data, d + doff, s);
117 }
118
119 static void
120 exif_data_load_data_thumbnail (ExifData *data, const unsigned char *d,
121                                unsigned int ds, ExifLong offset, ExifLong size)
122 {
123         if (ds < offset + size) {
124 #ifdef DEBUG
125                 printf ("Bogus thumbnail offset and size: %i < %i + %i.\n",
126                         (int) ds, (int) offset, (int) size);
127 #endif
128                 return;
129         }
130         if (data->data)
131                 free (data->data);
132         data->size = size;
133         data->data = malloc (sizeof (char) * data->size);
134         memcpy (data->data, d + offset, data->size);
135 }
136
137 static void
138 exif_data_load_data_content (ExifData *data, ExifContent *ifd,
139                              const unsigned char *d,
140                              unsigned int ds, unsigned int offset)
141 {
142         ExifLong o, thumbnail_offset = 0, thumbnail_length = 0;
143         ExifShort n;
144         ExifEntry *entry;
145         unsigned int i;
146         ExifTag tag;
147
148         ifd->order = data->priv->order;
149         
150         /* Read the number of entries */
151         n = exif_get_short (d + offset, data->priv->order);
152         offset += 2;
153         for (i = 0; i < n; i++) {
154
155                 tag = exif_get_short (d + offset + 12 * i, data->priv->order);
156                 switch (tag) {
157                 case EXIF_TAG_EXIF_IFD_POINTER:
158                 case EXIF_TAG_GPS_INFO_IFD_POINTER:
159                 case EXIF_TAG_INTEROPERABILITY_IFD_POINTER:
160                 case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
161                 case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
162                         o = exif_get_long (d + offset + 12 * i + 8,
163                                            data->priv->order);
164                         switch (tag) {
165                         case EXIF_TAG_EXIF_IFD_POINTER:
166                                 exif_data_load_data_content (data,
167                                         data->ifd_exif, d, ds, o);
168                                 break;
169                         case EXIF_TAG_GPS_INFO_IFD_POINTER:
170                                 exif_data_load_data_content (data,
171                                         data->ifd_gps, d, ds, o);
172                                 break;
173                         case EXIF_TAG_INTEROPERABILITY_IFD_POINTER:
174                                 exif_data_load_data_content (data,
175                                         data->ifd_interoperability, d, ds, o);
176                         case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
177 #ifdef DEBUG
178                                 printf ("Thumbnail at %i.\n", (int) o);
179 #endif
180                                 thumbnail_offset = o;
181                                 if (thumbnail_offset && thumbnail_length)
182                                         exif_data_load_data_thumbnail (data, d,
183                                                 ds, thumbnail_offset,
184                                                 thumbnail_length);
185                                 break;
186                         case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
187 #ifdef DEBUG
188                                 printf ("Thumbnail size: %i.\n", (int) o);
189 #endif
190                                 thumbnail_length = o;
191                                 if (thumbnail_offset && thumbnail_length)
192                                         exif_data_load_data_thumbnail (data, d,
193                                                 ds, thumbnail_offset,
194                                                 thumbnail_length);
195                                 break;
196                         default:
197                                 return;
198                         }
199                         break;
200                 default:
201                         entry = exif_entry_new ();
202                         exif_content_add_entry (ifd, entry);
203                         exif_data_load_data_entry (data, entry, d, ds,
204                                                    offset + 12 * i);
205                         exif_entry_unref (entry);
206                 }
207         }
208 }
209
210 void
211 exif_data_load_data (ExifData *data, const unsigned char *d, unsigned int size)
212 {
213         unsigned int o, len;
214         ExifLong offset;
215         ExifShort n;
216
217         if (!data)
218                 return;
219         if (!d)
220                 return;
221
222         /* Search the exif marker */
223         for (o = 0; o < size; o++)
224                 if (d[o] == JPEG_MARKER_APP1)
225                         break;
226         if (o == size)
227                 return;
228         o++;
229         d += o;
230         size -= o;
231
232         /* Size of exif data? */
233         if (size < 2)
234                 return;
235         len = (d[0] << 8) | d[1];
236
237         /*
238          * Verify the exif header
239          * (offset 2, length 6).
240          */
241         if (size < 2 + 6)
242                 return;
243         if (memcmp (d + 2, ExifHeader, 6))
244                 return;
245
246         /* Byte order (offset 8, length 2) */
247         if (size < 16)
248                 return;
249         if (!memcmp (d + 8, "II", 2))
250                 data->priv->order = EXIF_BYTE_ORDER_INTEL;
251         else if (!memcmp (d + 8, "MM", 2))
252                 data->priv->order = EXIF_BYTE_ORDER_MOTOROLA;
253         else
254                 return;
255
256         /* Fixed value */
257         if (exif_get_short (d + 10, data->priv->order) != 0x002a)
258                 return;
259
260         /* IFD 0 offset */
261         offset = exif_get_long (d + 12, data->priv->order);
262 #ifdef DEBUG
263         printf ("IFD 0 at %i.\n", (int) offset);
264 #endif
265
266         /* Parse the actual exif data (offset 16) */
267         exif_data_load_data_content (data, data->ifd0, d + 8,
268                                      size - 8, offset);
269
270         /* IFD 1 offset */
271         n = exif_get_short (d + 8 + offset, data->priv->order);
272         offset = exif_get_long (d + 8 + offset + 2 + 12 * n, data->priv->order);
273         if (offset) {
274 #ifdef DEBUG
275                 printf ("IFD 1 at %i.\n", (int) offset);
276 #endif
277                 exif_data_load_data_content (data, data->ifd1, d + 8,
278                                              size - 8, offset);
279         }
280 }
281
282 void
283 exif_data_save_data (ExifData *data, unsigned char **d, unsigned int *ds)
284 {
285         if (!data)
286                 return;
287         if (!d)
288                 return;
289         if (!ds)
290                 return;
291
292         /* Size */
293         *ds = 2;
294         *d = malloc (sizeof (char) * *ds);
295         (*d)[0] = *ds >> 8;
296         (*d)[1] = *ds >> 0;
297
298         /* Header (offset 2) */
299         *ds += 6;
300         *d = realloc (*d, sizeof (char) * *ds);
301         (*d)[0] = *ds >> 8;
302         (*d)[1] = *ds >> 0;
303         memcpy (*d + 2, ExifHeader, 6);
304
305         /* Order (offset 8) */
306         *ds += 2;
307         *d = realloc (*d, sizeof (char) * *ds);
308         (*d)[0] = *ds >> 8;
309         (*d)[1] = *ds >> 0;
310         if (data->priv->order == EXIF_BYTE_ORDER_INTEL) {
311                 memcpy (*d + 8, "II", 2);
312         } else {
313                 memcpy (*d + 8, "MM", 2);
314         }
315 }
316
317 ExifData *
318 exif_data_new_from_file (const char *path)
319 {
320         FILE *f;
321         unsigned int size;
322         unsigned char *data;
323         ExifData *edata;
324
325         f = fopen (path, "r");
326         if (!f)
327                 return (NULL);
328
329         /* For now, we read the data into memory. Patches welcome... */
330         fseek (f, 0, SEEK_END);
331         size = ftell (f);
332         fseek (f, 0, SEEK_SET);
333         data = malloc (sizeof (char) * size);
334         if (!data)
335                 return (NULL);
336         if (fread (data, 1, size, f) != size) {
337                 free (data);
338                 return (NULL);
339         }
340
341         edata = exif_data_new_from_data (data, size);
342         free (data);
343
344         fclose (f);
345
346         return (edata);
347 }
348
349 void
350 exif_data_ref (ExifData *data)
351 {
352         if (!data)
353                 return;
354
355         data->priv->ref_count++;
356 }
357
358 void
359 exif_data_unref (ExifData *data)
360 {
361         if (!data)
362                 return;
363
364         data->priv->ref_count--;
365         if (!data->priv->ref_count)
366                 exif_data_free (data);
367 }
368
369 void
370 exif_data_free (ExifData *data)
371 {
372         if (data->ifd0)
373                 exif_content_unref (data->ifd0);
374         if (data->ifd1)
375                 exif_content_unref (data->ifd1);
376         if (data->data)
377                 free (data->data);
378         free (data->priv);
379         free (data);
380 }
381
382 void
383 exif_data_dump (ExifData *data)
384 {
385         if (!data)
386                 return;
387
388         if (data->ifd0->count) {
389                 printf ("Dumping IFD 0...\n");
390                 exif_content_dump (data->ifd0, 0);
391         }
392
393         if (data->ifd1->count) {
394                 printf ("Dumping IFD 1...\n");
395                 exif_content_dump (data->ifd1, 0);
396         }
397
398         if (data->ifd_exif->count) {
399                 printf ("Dumping IFD EXIF...\n");
400                 exif_content_dump (data->ifd_exif, 0);
401         }
402
403         if (data->ifd_gps->count) {
404                 printf ("Dumping IFD GPS...\n");
405                 exif_content_dump (data->ifd_gps, 0);
406         }
407
408         if (data->ifd_interoperability->count) {
409                 printf ("Dumping IFD Interoperability...\n");
410                 exif_content_dump (data->ifd_interoperability, 0);
411         }
412
413         if (data->data) {
414                 printf ("%i byte(s) thumbnail data available.", data->size);
415                 if (data->size >= 4) {
416                         printf ("0x%02x 0x%02x ... 0x%02x 0x%02x\n",
417                                 data->data[0], data->data[1],
418                                 data->data[data->size - 2],
419                                 data->data[data->size - 1]);
420                 }
421         }
422 }