exif 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/exif/data.c +166 -559
- data/ext/exif/data.h +1 -18
- data/ext/exif/exif.c +8 -2
- data/ext/exif/exif_entry_to_ivar.c +451 -0
- data/ext/exif/extconf.rb +2 -0
- data/ext/exif/libjpeg/jpeg-data.c +490 -0
- data/ext/exif/libjpeg/jpeg-data.h +92 -0
- data/ext/exif/libjpeg/jpeg-marker.c +122 -0
- data/ext/exif/libjpeg/jpeg-marker.h +103 -0
- data/lib/exif.rb +2 -0
- data/lib/exif/version.rb +3 -1
- metadata +23 -36
- data/.gitignore +0 -14
- data/.rspec +0 -2
- data/.travis.yml +0 -6
- data/Gemfile +0 -2
- data/LICENSE.txt +0 -22
- data/README.md +0 -226
- data/Rakefile +0 -11
- data/benchmark/benchmark.rb +0 -19
- data/exif.gemspec +0 -26
- data/ext/exif/exif.h +0 -6
- data/spec/exif_spec.rb +0 -14
- data/spec/sample.jpg +0 -0
- data/spec/spec_helper.rb +0 -89
data/ext/exif/extconf.rb
CHANGED
@@ -0,0 +1,490 @@
|
|
1
|
+
/* jpeg-data.c
|
2
|
+
*
|
3
|
+
* Copyright � 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., 51 Franklin Street, Fifth Floor,
|
18
|
+
* Boston, MA 02110-1301 USA.
|
19
|
+
*/
|
20
|
+
|
21
|
+
#include "config.h"
|
22
|
+
#include "jpeg-data.h"
|
23
|
+
|
24
|
+
#include <stdlib.h>
|
25
|
+
#include <stdio.h>
|
26
|
+
#include <string.h>
|
27
|
+
|
28
|
+
/* This refers to the exif-i18n.h file from the "exif" package and is
|
29
|
+
* NOT to be confused with the libexif/i18n.h file.
|
30
|
+
*/
|
31
|
+
#include "exif/exif-i18n.h"
|
32
|
+
|
33
|
+
/* realloc that cleans up on memory failure and returns to caller */
|
34
|
+
#define CLEANUP_REALLOC(p,s) { \
|
35
|
+
unsigned char *cleanup_ptr = realloc((p),(s)); \
|
36
|
+
if (!cleanup_ptr) { free(p); (p) = NULL; return; } \
|
37
|
+
(p) = cleanup_ptr; \
|
38
|
+
}
|
39
|
+
|
40
|
+
struct _JPEGDataPrivate
|
41
|
+
{
|
42
|
+
unsigned int ref_count;
|
43
|
+
|
44
|
+
ExifLog *log;
|
45
|
+
};
|
46
|
+
|
47
|
+
JPEGData *
|
48
|
+
jpeg_data_new (void)
|
49
|
+
{
|
50
|
+
JPEGData *data;
|
51
|
+
|
52
|
+
data = malloc (sizeof (JPEGData));
|
53
|
+
if (!data)
|
54
|
+
return (NULL);
|
55
|
+
memset (data, 0, sizeof (JPEGData));
|
56
|
+
data->priv = malloc (sizeof (JPEGDataPrivate));
|
57
|
+
if (!data->priv) {
|
58
|
+
free (data);
|
59
|
+
return (NULL);
|
60
|
+
}
|
61
|
+
memset (data->priv, 0, sizeof (JPEGDataPrivate));
|
62
|
+
data->priv->ref_count = 1;
|
63
|
+
|
64
|
+
return (data);
|
65
|
+
}
|
66
|
+
|
67
|
+
void
|
68
|
+
jpeg_data_append_section (JPEGData *data)
|
69
|
+
{
|
70
|
+
JPEGSection *s;
|
71
|
+
|
72
|
+
if (!data) return;
|
73
|
+
|
74
|
+
if (!data->count)
|
75
|
+
s = malloc (sizeof (JPEGSection));
|
76
|
+
else
|
77
|
+
s = realloc (data->sections,
|
78
|
+
sizeof (JPEGSection) * (data->count + 1));
|
79
|
+
if (!s) {
|
80
|
+
EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data",
|
81
|
+
sizeof (JPEGSection) * (data->count + 1));
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
memset(s + data->count, 0, sizeof (JPEGSection));
|
85
|
+
data->sections = s;
|
86
|
+
data->count++;
|
87
|
+
}
|
88
|
+
|
89
|
+
/*! jpeg_data_save_file returns 1 on success, 0 on failure */
|
90
|
+
int
|
91
|
+
jpeg_data_save_file (JPEGData *data, const char *path)
|
92
|
+
{
|
93
|
+
FILE *f;
|
94
|
+
unsigned char *d = NULL;
|
95
|
+
unsigned int size = 0, written;
|
96
|
+
|
97
|
+
jpeg_data_save_data (data, &d, &size);
|
98
|
+
if (!d)
|
99
|
+
return 0;
|
100
|
+
|
101
|
+
remove (path);
|
102
|
+
f = fopen (path, "wb");
|
103
|
+
if (!f) {
|
104
|
+
free (d);
|
105
|
+
return 0;
|
106
|
+
}
|
107
|
+
written = fwrite (d, 1, size, f);
|
108
|
+
fclose (f);
|
109
|
+
free (d);
|
110
|
+
if (written == size) {
|
111
|
+
return 1;
|
112
|
+
}
|
113
|
+
remove(path);
|
114
|
+
return 0;
|
115
|
+
}
|
116
|
+
|
117
|
+
void
|
118
|
+
jpeg_data_save_data (JPEGData *data, unsigned char **d, unsigned int *ds)
|
119
|
+
{
|
120
|
+
unsigned int i, eds = 0;
|
121
|
+
JPEGSection s;
|
122
|
+
unsigned char *ed = NULL;
|
123
|
+
|
124
|
+
if (!data)
|
125
|
+
return;
|
126
|
+
if (!d)
|
127
|
+
return;
|
128
|
+
if (!ds)
|
129
|
+
return;
|
130
|
+
|
131
|
+
for (*ds = i = 0; i < data->count; i++) {
|
132
|
+
s = data->sections[i];
|
133
|
+
|
134
|
+
/* Write the marker */
|
135
|
+
CLEANUP_REALLOC (*d, sizeof (char) * (*ds + 2));
|
136
|
+
(*d)[*ds + 0] = 0xff;
|
137
|
+
(*d)[*ds + 1] = s.marker;
|
138
|
+
*ds += 2;
|
139
|
+
|
140
|
+
switch (s.marker) {
|
141
|
+
case JPEG_MARKER_SOI:
|
142
|
+
case JPEG_MARKER_EOI:
|
143
|
+
break;
|
144
|
+
case JPEG_MARKER_APP1:
|
145
|
+
exif_data_save_data (s.content.app1, &ed, &eds);
|
146
|
+
if (!ed) break;
|
147
|
+
CLEANUP_REALLOC (*d, sizeof (char) * (*ds + 2));
|
148
|
+
(*d)[*ds + 0] = (eds + 2) >> 8;
|
149
|
+
(*d)[*ds + 1] = (eds + 2) >> 0;
|
150
|
+
*ds += 2;
|
151
|
+
CLEANUP_REALLOC (*d, sizeof (char) * (*ds + eds));
|
152
|
+
memcpy (*d + *ds, ed, eds);
|
153
|
+
*ds += eds;
|
154
|
+
free (ed);
|
155
|
+
break;
|
156
|
+
default:
|
157
|
+
CLEANUP_REALLOC (*d, sizeof (char) *
|
158
|
+
(*ds + s.content.generic.size + 2));
|
159
|
+
(*d)[*ds + 0] = (s.content.generic.size + 2) >> 8;
|
160
|
+
(*d)[*ds + 1] = (s.content.generic.size + 2) >> 0;
|
161
|
+
*ds += 2;
|
162
|
+
memcpy (*d + *ds, s.content.generic.data,
|
163
|
+
s.content.generic.size);
|
164
|
+
*ds += s.content.generic.size;
|
165
|
+
|
166
|
+
/* In case of SOS, we need to write the data. */
|
167
|
+
if (s.marker == JPEG_MARKER_SOS) {
|
168
|
+
CLEANUP_REALLOC (*d, *ds + data->size);
|
169
|
+
memcpy (*d + *ds, data->data, data->size);
|
170
|
+
*ds += data->size;
|
171
|
+
}
|
172
|
+
break;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
JPEGData *
|
178
|
+
jpeg_data_new_from_data (const unsigned char *d,
|
179
|
+
unsigned int size)
|
180
|
+
{
|
181
|
+
JPEGData *data;
|
182
|
+
|
183
|
+
data = jpeg_data_new ();
|
184
|
+
jpeg_data_load_data (data, d, size);
|
185
|
+
return (data);
|
186
|
+
}
|
187
|
+
|
188
|
+
void
|
189
|
+
jpeg_data_load_data (JPEGData *data, const unsigned char *d,
|
190
|
+
unsigned int size)
|
191
|
+
{
|
192
|
+
unsigned int i, o, len;
|
193
|
+
JPEGSection *s;
|
194
|
+
JPEGMarker marker;
|
195
|
+
|
196
|
+
if (!data) return;
|
197
|
+
if (!d) return;
|
198
|
+
|
199
|
+
for (o = 0; o < size;) {
|
200
|
+
|
201
|
+
/*
|
202
|
+
* JPEG sections start with 0xff. The first byte that is
|
203
|
+
* not 0xff is a marker (hopefully).
|
204
|
+
*/
|
205
|
+
for (i = 0; i < MIN(7, size - o); i++)
|
206
|
+
if (d[o + i] != 0xff)
|
207
|
+
break;
|
208
|
+
if ((i >= size - o) || !JPEG_IS_MARKER (d[o + i])) {
|
209
|
+
exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
|
210
|
+
_("Data does not follow JPEG specification."));
|
211
|
+
return;
|
212
|
+
}
|
213
|
+
marker = d[o + i];
|
214
|
+
|
215
|
+
/* Append this section */
|
216
|
+
jpeg_data_append_section (data);
|
217
|
+
if (!data->count) return;
|
218
|
+
s = &data->sections[data->count - 1];
|
219
|
+
s->marker = marker;
|
220
|
+
o += i + 1;
|
221
|
+
|
222
|
+
switch (s->marker) {
|
223
|
+
case JPEG_MARKER_SOI:
|
224
|
+
case JPEG_MARKER_EOI:
|
225
|
+
break;
|
226
|
+
default:
|
227
|
+
|
228
|
+
/* Read the length of the section */
|
229
|
+
if (2 > size - o) { o = size; break; }
|
230
|
+
len = ((d[o] << 8) | d[o + 1]) - 2;
|
231
|
+
if (len > size) { o = size; break; }
|
232
|
+
o += 2;
|
233
|
+
if (len > size - o) { o = size; break; }
|
234
|
+
|
235
|
+
switch (s->marker) {
|
236
|
+
case JPEG_MARKER_APP1:
|
237
|
+
s->content.app1 = exif_data_new_from_data (
|
238
|
+
d + o - 4, len + 4);
|
239
|
+
break;
|
240
|
+
default:
|
241
|
+
s->content.generic.data =
|
242
|
+
malloc (sizeof (char) * len);
|
243
|
+
if (!s->content.generic.data) {
|
244
|
+
EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", sizeof (char) * len);
|
245
|
+
return;
|
246
|
+
}
|
247
|
+
s->content.generic.size = len;
|
248
|
+
memcpy (s->content.generic.data, &d[o], len);
|
249
|
+
|
250
|
+
/* In case of SOS, image data will follow. */
|
251
|
+
if (s->marker == JPEG_MARKER_SOS) {
|
252
|
+
data->size = size - o - len;
|
253
|
+
if (data->size >= 2) {
|
254
|
+
/* -2 means 'take all but the last 2 bytes which are
|
255
|
+
hoped to be JPEG_MARKER_EOI */
|
256
|
+
data->size -= 2;
|
257
|
+
if (d[o + len + data->size] != 0xFF) {
|
258
|
+
/* A truncated file (i.e. w/o JPEG_MARKER_EOI at the end).
|
259
|
+
Instead of trying to use the last two bytes as marker,
|
260
|
+
touching memory beyond allocated memory and posssibly saving
|
261
|
+
back screwed file, we rather take the rest of the file. */
|
262
|
+
data->size += 2;
|
263
|
+
}
|
264
|
+
}
|
265
|
+
data->data = malloc (
|
266
|
+
sizeof (char) * data->size);
|
267
|
+
if (!data->data) {
|
268
|
+
EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", sizeof (char) * data->size);
|
269
|
+
data->size = 0;
|
270
|
+
return;
|
271
|
+
}
|
272
|
+
memcpy (data->data, d + o + len,
|
273
|
+
data->size);
|
274
|
+
o += data->size;
|
275
|
+
}
|
276
|
+
break;
|
277
|
+
}
|
278
|
+
o += len;
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
JPEGData *
|
285
|
+
jpeg_data_new_from_file (const char *path)
|
286
|
+
{
|
287
|
+
JPEGData *data;
|
288
|
+
|
289
|
+
data = jpeg_data_new ();
|
290
|
+
jpeg_data_load_file (data, path);
|
291
|
+
return (data);
|
292
|
+
}
|
293
|
+
|
294
|
+
void
|
295
|
+
jpeg_data_load_file (JPEGData *data, const char *path)
|
296
|
+
{
|
297
|
+
FILE *f;
|
298
|
+
unsigned char *d;
|
299
|
+
unsigned int size;
|
300
|
+
|
301
|
+
if (!data) return;
|
302
|
+
if (!path) return;
|
303
|
+
|
304
|
+
f = fopen (path, "rb");
|
305
|
+
if (!f) {
|
306
|
+
exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
|
307
|
+
_("Path '%s' invalid."), path);
|
308
|
+
return;
|
309
|
+
}
|
310
|
+
|
311
|
+
/* For now, we read the data into memory. Patches welcome... */
|
312
|
+
fseek (f, 0, SEEK_END);
|
313
|
+
size = ftell (f);
|
314
|
+
fseek (f, 0, SEEK_SET);
|
315
|
+
d = malloc (size);
|
316
|
+
if (!d) {
|
317
|
+
EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", size);
|
318
|
+
fclose (f);
|
319
|
+
return;
|
320
|
+
}
|
321
|
+
if (fread (d, 1, size, f) != size) {
|
322
|
+
free (d);
|
323
|
+
fclose (f);
|
324
|
+
exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
|
325
|
+
_("Could not read '%s'."), path);
|
326
|
+
return;
|
327
|
+
}
|
328
|
+
fclose (f);
|
329
|
+
|
330
|
+
jpeg_data_load_data (data, d, size);
|
331
|
+
free (d);
|
332
|
+
}
|
333
|
+
|
334
|
+
void
|
335
|
+
jpeg_data_ref (JPEGData *data)
|
336
|
+
{
|
337
|
+
if (!data)
|
338
|
+
return;
|
339
|
+
|
340
|
+
data->priv->ref_count++;
|
341
|
+
}
|
342
|
+
|
343
|
+
void
|
344
|
+
jpeg_data_unref (JPEGData *data)
|
345
|
+
{
|
346
|
+
if (!data)
|
347
|
+
return;
|
348
|
+
|
349
|
+
if (data->priv) {
|
350
|
+
data->priv->ref_count--;
|
351
|
+
if (!data->priv->ref_count)
|
352
|
+
jpeg_data_free (data);
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
void
|
357
|
+
jpeg_data_free (JPEGData *data)
|
358
|
+
{
|
359
|
+
unsigned int i;
|
360
|
+
JPEGSection s;
|
361
|
+
|
362
|
+
if (!data)
|
363
|
+
return;
|
364
|
+
|
365
|
+
if (data->count) {
|
366
|
+
for (i = 0; i < data->count; i++) {
|
367
|
+
s = data->sections[i];
|
368
|
+
switch (s.marker) {
|
369
|
+
case JPEG_MARKER_SOI:
|
370
|
+
case JPEG_MARKER_EOI:
|
371
|
+
break;
|
372
|
+
case JPEG_MARKER_APP1:
|
373
|
+
exif_data_unref (s.content.app1);
|
374
|
+
break;
|
375
|
+
default:
|
376
|
+
free (s.content.generic.data);
|
377
|
+
break;
|
378
|
+
}
|
379
|
+
}
|
380
|
+
free (data->sections);
|
381
|
+
}
|
382
|
+
|
383
|
+
if (data->data)
|
384
|
+
free (data->data);
|
385
|
+
|
386
|
+
if (data->priv) {
|
387
|
+
if (data->priv->log) {
|
388
|
+
exif_log_unref (data->priv->log);
|
389
|
+
data->priv->log = NULL;
|
390
|
+
}
|
391
|
+
free (data->priv);
|
392
|
+
}
|
393
|
+
|
394
|
+
free (data);
|
395
|
+
}
|
396
|
+
|
397
|
+
void
|
398
|
+
jpeg_data_dump (JPEGData *data)
|
399
|
+
{
|
400
|
+
unsigned int i;
|
401
|
+
JPEGContent content;
|
402
|
+
JPEGMarker marker;
|
403
|
+
|
404
|
+
if (!data)
|
405
|
+
return;
|
406
|
+
|
407
|
+
printf ("Dumping JPEG data (%i bytes of data)...\n", data->size);
|
408
|
+
for (i = 0; i < data->count; i++) {
|
409
|
+
marker = data->sections[i].marker;
|
410
|
+
content = data->sections[i].content;
|
411
|
+
printf ("Section %i (marker 0x%x - %s):\n", i, marker,
|
412
|
+
jpeg_marker_get_name (marker));
|
413
|
+
printf (" Description: %s\n",
|
414
|
+
jpeg_marker_get_description (marker));
|
415
|
+
switch (marker) {
|
416
|
+
case JPEG_MARKER_SOI:
|
417
|
+
case JPEG_MARKER_EOI:
|
418
|
+
break;
|
419
|
+
case JPEG_MARKER_APP1:
|
420
|
+
exif_data_dump (content.app1);
|
421
|
+
break;
|
422
|
+
default:
|
423
|
+
printf (" Size: %i\n", content.generic.size);
|
424
|
+
printf (" Unknown content.\n");
|
425
|
+
break;
|
426
|
+
}
|
427
|
+
}
|
428
|
+
}
|
429
|
+
|
430
|
+
static JPEGSection *
|
431
|
+
jpeg_data_get_section (JPEGData *data, JPEGMarker marker)
|
432
|
+
{
|
433
|
+
unsigned int i;
|
434
|
+
|
435
|
+
if (!data)
|
436
|
+
return (NULL);
|
437
|
+
|
438
|
+
for (i = 0; i < data->count; i++)
|
439
|
+
if (data->sections[i].marker == marker)
|
440
|
+
return (&data->sections[i]);
|
441
|
+
return (NULL);
|
442
|
+
}
|
443
|
+
|
444
|
+
ExifData *
|
445
|
+
jpeg_data_get_exif_data (JPEGData *data)
|
446
|
+
{
|
447
|
+
JPEGSection *section;
|
448
|
+
|
449
|
+
if (!data)
|
450
|
+
return NULL;
|
451
|
+
|
452
|
+
section = jpeg_data_get_section (data, JPEG_MARKER_APP1);
|
453
|
+
if (section) {
|
454
|
+
exif_data_ref (section->content.app1);
|
455
|
+
return (section->content.app1);
|
456
|
+
}
|
457
|
+
|
458
|
+
return (NULL);
|
459
|
+
}
|
460
|
+
|
461
|
+
void
|
462
|
+
jpeg_data_set_exif_data (JPEGData *data, ExifData *exif_data)
|
463
|
+
{
|
464
|
+
JPEGSection *section;
|
465
|
+
|
466
|
+
if (!data) return;
|
467
|
+
|
468
|
+
section = jpeg_data_get_section (data, JPEG_MARKER_APP1);
|
469
|
+
if (!section) {
|
470
|
+
jpeg_data_append_section (data);
|
471
|
+
if (data->count < 2) return;
|
472
|
+
memmove (&data->sections[2], &data->sections[1],
|
473
|
+
sizeof (JPEGSection) * (data->count - 2));
|
474
|
+
section = &data->sections[1];
|
475
|
+
} else {
|
476
|
+
exif_data_unref (section->content.app1);
|
477
|
+
}
|
478
|
+
section->marker = JPEG_MARKER_APP1;
|
479
|
+
section->content.app1 = exif_data;
|
480
|
+
exif_data_ref (exif_data);
|
481
|
+
}
|
482
|
+
|
483
|
+
void
|
484
|
+
jpeg_data_log (JPEGData *data, ExifLog *log)
|
485
|
+
{
|
486
|
+
if (!data || !data->priv) return;
|
487
|
+
if (data->priv->log) exif_log_unref (data->priv->log);
|
488
|
+
data->priv->log = log;
|
489
|
+
exif_log_ref (log);
|
490
|
+
}
|