exif_geo_tag 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b1a70e702e26ab2af7ab5f458fa2374c5a01e42
4
+ data.tar.gz: d9cf1596376692989acce8e6ca9e57725fcc8b20
5
+ SHA512:
6
+ metadata.gz: 6e2131defc20cd5bc5fbf46bf0a915e22e6157192b7ff0edfc1e0ec4cd092ab9dcc41e22e178560eb88ef1acf0dc3672d0d8c02fa19c93712a6ab9bfce9c016c
7
+ data.tar.gz: c2a563d55250730da2339d7b16dbffce530b0df4ff317ef48a75d5e1f99b18647e363e1cd67623de5c8b9a144ae94e7dd48a03d3b2c502a4620d621518043a20
data/ext/exif-i18n.c ADDED
@@ -0,0 +1,42 @@
1
+ #include <config.h>
2
+ #include "exif-i18n.h"
3
+
4
+ #ifdef HAVE_ICONV
5
+ # include <iconv.h>
6
+ # include <langinfo.h>
7
+ #endif
8
+
9
+ #include <string.h>
10
+ #include <sys/types.h>
11
+
12
+ #undef MIN
13
+ #define MIN(a, b) (((a) < (b)) ? (a) : (b))
14
+
15
+ const char *
16
+ exif_i18n_convert_utf8_to_locale (const char *in)
17
+ {
18
+ #if defined(ENABLE_GETTEXT_ICONV) && defined(HAVE_ICONV)
19
+ /* If gettext() doesn't convert the message texts into the proper
20
+ * encoding for the current locale, then it's broken (because there's
21
+ * no way for the app to know the encoding of the translated text).
22
+ * In this case, assume the translated text is in UTF-8 (which could
23
+ * be wrong) and use iconv to convert to the proper encoding.
24
+ * This is only an issue with really old gettext versions (< 0.10.36)
25
+ */
26
+ static iconv_t tr = 0;
27
+ size_t t = (in ? strlen (in) : 0);
28
+ static char buf[2048];
29
+ size_t buf_size = sizeof (buf);
30
+ char *out = buf;
31
+
32
+ if (!in) return "";
33
+
34
+ memset (buf, 0, sizeof (buf));
35
+ if (!tr) tr = iconv_open (nl_langinfo (CODESET), "UTF-8");
36
+ iconv (tr, (char **) &in, &t, (char **) &out, &buf_size);
37
+ return buf;
38
+ #else
39
+ if (!in) return "";
40
+ return in;
41
+ #endif
42
+ }
data/ext/exif-i18n.h ADDED
@@ -0,0 +1,56 @@
1
+ /* i18n.h
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
+ #ifndef __I18N_H__
22
+ #define __I18N_H__
23
+
24
+ #ifdef ENABLE_NLS
25
+ # include <libintl.h>
26
+ # undef _
27
+ # define _(String) dgettext (GETTEXT_PACKAGE, String)
28
+ # ifdef gettext_noop
29
+ # define N_(String) gettext_noop (String)
30
+ # else
31
+ # define N_(String) (String)
32
+ # endif
33
+ #else
34
+ # define textdomain(String) (String)
35
+ # define gettext(String) (String)
36
+ # define ngettext(String1,String2,Count) ((Count)==1?(String1):(String2))
37
+ # define dgettext(Domain,Message) (Message)
38
+ # define dcgettext(Domain,Message,Type) (Message)
39
+ # define bind_textdomain_codeset(Domain,Codeset) (Codeset)
40
+ # define bindtextdomain(Domain,Directory) (Domain)
41
+ # define _(String) (String)
42
+ # define N_(String) (String)
43
+ #endif
44
+
45
+
46
+ /*! Convert a string from UTF-8 into one appropriate for the current locale
47
+ * if gettext doesn't doesn't do the conversion itself.
48
+ * If given a NULL pointer, returns a pointer to an empty string.
49
+ * \param[in] in the string to convert
50
+ * \returns pointer to converted string, which may be in a static buffer
51
+ */
52
+ const char *exif_i18n_convert_utf8_to_locale (const char *);
53
+ #define C(s) (exif_i18n_convert_utf8_to_locale(s))
54
+
55
+
56
+ #endif /* __I18N_H__ */
@@ -0,0 +1,1081 @@
1
+ #include <strings.h>
2
+
3
+ #include "ruby.h"
4
+ #include "ruby/encoding.h"
5
+
6
+ RUBY_EXTERN VALUE rb_cRational;
7
+ RUBY_EXTERN VALUE rb_cTime;
8
+ RUBY_EXTERN VALUE rb_cNumeric;
9
+
10
+ #include <libexif/exif-data.h>
11
+ #include <libexif/exif-entry.h>
12
+ #include <libexif/exif-loader.h>
13
+
14
+ #include "jpeg-data.h"
15
+
16
+ ExifLog *logger;
17
+ #ifndef DEBUG
18
+ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
19
+ #define exif_log(...) \
20
+ do { \
21
+ } while (0)
22
+ #elif defined(__GNUC__)
23
+ #define exif_log(x...) \
24
+ do { \
25
+ } while (0)
26
+ #else
27
+ #define exif_log (void)
28
+ #endif
29
+ #endif
30
+
31
+ VALUE egt_mExifGeoTag;
32
+
33
+ #define TAG_MAPPING(X) \
34
+ X(EXIF_TAG_GPS_VERSION_ID, version_id) \
35
+ X(EXIF_TAG_GPS_LATITUDE_REF, latitude_ref) \
36
+ X(EXIF_TAG_GPS_LATITUDE, latitude) \
37
+ X(EXIF_TAG_GPS_LONGITUDE_REF, longitude_ref) \
38
+ X(EXIF_TAG_GPS_LONGITUDE, longitude) \
39
+ X(EXIF_TAG_GPS_ALTITUDE_REF, altitude_ref) \
40
+ X(EXIF_TAG_GPS_ALTITUDE, altitude) \
41
+ X(EXIF_TAG_GPS_TIME_STAMP, time_stamp) \
42
+ X(EXIF_TAG_GPS_DATE_STAMP, date_stamp) \
43
+ X(EXIF_TAG_GPS_SATELLITES, satellites) \
44
+ X(EXIF_TAG_GPS_STATUS, status) \
45
+ X(EXIF_TAG_GPS_MEASURE_MODE, measure_mode) \
46
+ X(EXIF_TAG_GPS_DOP, dop) \
47
+ X(EXIF_TAG_GPS_SPEED_REF, speed_ref) \
48
+ X(EXIF_TAG_GPS_SPEED, speed) \
49
+ X(EXIF_TAG_GPS_TRACK_REF, track_ref) \
50
+ X(EXIF_TAG_GPS_TRACK, track) \
51
+ X(EXIF_TAG_GPS_IMG_DIRECTION_REF, img_direction_ref) \
52
+ X(EXIF_TAG_GPS_IMG_DIRECTION, img_direction) \
53
+ X(EXIF_TAG_GPS_MAP_DATUM, map_datum) \
54
+ X(EXIF_TAG_GPS_DEST_LATITUDE_REF, dest_latitude_ref) \
55
+ X(EXIF_TAG_GPS_DEST_LATITUDE, dest_latitude) \
56
+ X(EXIF_TAG_GPS_DEST_LONGITUDE_REF, dest_longitude_ref) \
57
+ X(EXIF_TAG_GPS_DEST_LONGITUDE, dest_longitude) \
58
+ X(EXIF_TAG_GPS_DEST_BEARING_REF, dest_bearing_ref) \
59
+ X(EXIF_TAG_GPS_DEST_BEARING, dest_bearing) \
60
+ X(EXIF_TAG_GPS_DEST_DISTANCE_REF, dest_distance_ref) \
61
+ X(EXIF_TAG_GPS_DEST_DISTANCE, dest_distance) \
62
+ X(EXIF_TAG_GPS_PROCESSING_METHOD, processing_method) \
63
+ X(EXIF_TAG_GPS_AREA_INFORMATION, area_information) \
64
+ X(EXIF_TAG_GPS_DIFFERENTIAL, differential)
65
+
66
+ #define X(e, i) ID egt_sym_##i;
67
+ TAG_MAPPING(X)
68
+ #undef X
69
+
70
+ ID egt_sym__latitude;
71
+ ID egt_sym__longitude;
72
+ ID egt_sym__altitude;
73
+ ID egt_sym__timestamp;
74
+
75
+ ID egt_id_Rational;
76
+ ID egt_id_add;
77
+ ID egt_id_div;
78
+ ID egt_id_sub;
79
+ ID egt_id_mul;
80
+ ID egt_id_to_f;
81
+ ID egt_id_to_i;
82
+ ID egt_id_rationalize;
83
+ ID egt_id_round;
84
+ ID egt_id_split;
85
+ ID egt_id_utc;
86
+ ID egt_id_numerator;
87
+ ID egt_id_denominator;
88
+ ID egt_id_strftime;
89
+ ID egt_id_hour;
90
+ ID egt_id_min;
91
+ ID egt_id_sec;
92
+ ID egt_id_truncate;
93
+ ID egt_id_negative_p;
94
+
95
+ VALUE egt_str_colon;
96
+ VALUE egt_str_period;
97
+ VALUE egt_str_date_format;
98
+ VALUE egt_str_south;
99
+ VALUE egt_str_north;
100
+ VALUE egt_str_west;
101
+ VALUE egt_str_east;
102
+
103
+ VALUE egt_flt_min;
104
+ VALUE egt_flt_sec;
105
+
106
+ static void *egt_exif_entry_alloc(ExifMem *mem, ExifEntry *entry, unsigned int size)
107
+ {
108
+ void *data;
109
+
110
+ if (!entry || !size) {
111
+ return NULL;
112
+ }
113
+
114
+ data = exif_mem_alloc(mem, size);
115
+ if (data) {
116
+ return data;
117
+ }
118
+
119
+ EXIF_LOG_NO_MEMORY(logger, "RubyExt", size);
120
+ return NULL;
121
+ }
122
+
123
+ static void egt_exif_entry_initialize(ExifMem *mem, ExifEntry *entry, ExifTag tag)
124
+ {
125
+ /* We need the byte order */
126
+ if (!entry || !entry->parent || entry->data) {
127
+ return;
128
+ }
129
+
130
+ entry->tag = tag;
131
+
132
+ switch ((int)tag) {
133
+ case EXIF_TAG_GPS_VERSION_ID:
134
+ /*
135
+ * Indicates the version of GPSInfoIFD. The version is given as 2.2.0.0.
136
+ * This tag is mandatory when GPSInfo tag is present. Note that the
137
+ * GPSVersionID tag is written as a different byte than the Exif Version
138
+ * tag. Default 2.2.0.0
139
+ */
140
+ entry->format = EXIF_FORMAT_BYTE;
141
+ entry->components = 4;
142
+ entry->size = exif_format_get_size(entry->format) * entry->components;
143
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
144
+ entry->data[0] = 2;
145
+ entry->data[1] = 2;
146
+ entry->data[2] = 0;
147
+ entry->data[3] = 0;
148
+ break;
149
+ case EXIF_TAG_GPS_LATITUDE_REF:
150
+ /*
151
+ * Indicates whether the latitude is north or south latitude. The ASCII
152
+ * value 'N' indicates north latitude, and 'S' is south latitude.
153
+ */
154
+ entry->format = EXIF_FORMAT_ASCII;
155
+ entry->components = 2;
156
+ break;
157
+ case EXIF_TAG_GPS_LATITUDE:
158
+ /*
159
+ * Indicates the latitude. The latitude is expressed as three RATIONAL
160
+ * values giving the degrees, minutes, and seconds, respectively. If
161
+ * latitude is expressed as degrees, minutes and seconds, a typical
162
+ * format would be dd/1,mm/1,ss/1. When degrees and minutes are used
163
+ * and, for example, fractions of minutes are given up to two decimal
164
+ * places, the format would be dd/1,mmmm/100,0/1.
165
+ */
166
+ entry->format = EXIF_FORMAT_RATIONAL;
167
+ entry->components = 3;
168
+ break;
169
+ case EXIF_TAG_GPS_LONGITUDE_REF:
170
+ /*
171
+ * Indicates whether the longitude is east or west longitude. ASCII 'E'
172
+ * indicates east longitude, and 'W' is west longitude.
173
+ */
174
+ entry->format = EXIF_FORMAT_ASCII;
175
+ entry->components = 2;
176
+ break;
177
+ case EXIF_TAG_GPS_LONGITUDE:
178
+ /*
179
+ * Indicates the longitude. The longitude is expressed as three RATIONAL
180
+ * values giving the degrees, minutes, and seconds, respectively. If
181
+ * longitude is expressed as degrees, minutes and seconds, a typical
182
+ * format would be ddd/1,mm/1,ss/1. When degrees and minutes are used
183
+ * and, for example, fractions of minutes are given up to two decimal
184
+ * places, the format would be ddd/1,mmmm/100,0/1.
185
+ */
186
+ entry->format = EXIF_FORMAT_RATIONAL;
187
+ entry->components = 3;
188
+ break;
189
+
190
+ case EXIF_TAG_GPS_ALTITUDE_REF:
191
+ /*
192
+ * Indicates the altitude used as the reference altitude. If the
193
+ * reference is sea level and the altitude is above sea level, 0 is
194
+ * given. If the altitude is below sea level, a value of 1 is given and
195
+ * the altitude is indicated as an absolute value in the GPSAltitude
196
+ * tag. The reference unit is meters. Note that this tag is BYTE type,
197
+ * unlike other reference tags. Default 0.
198
+ */
199
+ entry->format = EXIF_FORMAT_BYTE;
200
+ entry->components = 1;
201
+ entry->size = exif_format_get_size(entry->format) * entry->components;
202
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
203
+ entry->data[0] = 0;
204
+ break;
205
+ case EXIF_TAG_GPS_ALTITUDE:
206
+ /*
207
+ * Indicates the altitude based on the reference in GPSAltitudeRef.
208
+ * Altitude is expressed as one RATIONAL value. The reference unit is
209
+ * meters.
210
+ */
211
+ entry->format = EXIF_FORMAT_RATIONAL;
212
+ entry->components = 1;
213
+ break;
214
+ case EXIF_TAG_GPS_TIME_STAMP:
215
+ /*
216
+ * Indicates the time as UTC (Coordinated Universal Time). TimeStamp is
217
+ * expressed as three RATIONAL values giving the hour, minute, and
218
+ * second.
219
+ */
220
+ entry->format = EXIF_FORMAT_RATIONAL;
221
+ entry->components = 3;
222
+ break;
223
+ case EXIF_TAG_GPS_SATELLITES:
224
+ /*
225
+ * Indicates the GPS satellites used for measurements. This tag can be
226
+ * used to describe the number of satellites, their ID number, angle of
227
+ * elevation, azimuth, SNR and other information in ASCII notation. The
228
+ * format is not specified. If the GPS receiver is incapable of taking
229
+ * measurements, value of the tag shall be set to NULL.
230
+ */
231
+ entry->format = EXIF_FORMAT_ASCII;
232
+ entry->components = 0;
233
+ break;
234
+ case EXIF_TAG_GPS_STATUS:
235
+ /*
236
+ * Indicates the status of the GPS receiver when the image is recorded.
237
+ * 'A' means measurement is in progress, and 'V' means the measurement
238
+ * is Interoperability.
239
+ */
240
+ entry->format = EXIF_FORMAT_ASCII;
241
+ entry->components = 2;
242
+ break;
243
+ case EXIF_TAG_GPS_MEASURE_MODE:
244
+ /*
245
+ * Indicates the GPS measurement mode. '2' means two-dimensional
246
+ * measurement and '3' means three-dimensional measurement is in
247
+ * progress.
248
+ */
249
+ entry->format = EXIF_FORMAT_ASCII;
250
+ entry->components = 2;
251
+ break;
252
+ case EXIF_TAG_GPS_DOP:
253
+ /*
254
+ * Indicates the GPS DOP (data degree of precision). An HDOP value is
255
+ * written during two-dimensional measurement, and PDOP during
256
+ * three-dimensional measurement.
257
+ */
258
+ entry->format = EXIF_FORMAT_ASCII;
259
+ entry->components = 2;
260
+ break;
261
+ case EXIF_TAG_GPS_SPEED_REF:
262
+ /*
263
+ * Indicates the unit used to express the GPS receiver speed of
264
+ * movement. 'K' 'M' and 'N' represents kilometers per hour, miles per
265
+ * hour, and knots. Default 'K'.
266
+ */
267
+ entry->format = EXIF_FORMAT_ASCII;
268
+ entry->components = 2;
269
+ entry->size = exif_format_get_size(entry->format) * entry->components;
270
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
271
+ entry->data[0] = 'K';
272
+ entry->data[1] = 0;
273
+ break;
274
+ case EXIF_TAG_GPS_SPEED:
275
+ /*
276
+ * Indicates the speed of GPS receiver movement.
277
+ */
278
+ entry->format = EXIF_FORMAT_RATIONAL;
279
+ entry->components = 1;
280
+ break;
281
+ case EXIF_TAG_GPS_TRACK_REF:
282
+ /*
283
+ * Indicates the reference for giving the direction of GPS receiver
284
+ * movement. 'T' denotes true direction and 'M' is magnetic direction.
285
+ * Default 'T'.
286
+ */
287
+ entry->format = EXIF_FORMAT_ASCII;
288
+ entry->components = 2;
289
+ entry->size = exif_format_get_size(entry->format) * entry->components;
290
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
291
+ entry->data[0] = 'T';
292
+ entry->data[1] = 0;
293
+ break;
294
+ case EXIF_TAG_GPS_TRACK:
295
+ /*
296
+ * Indicates the direction of GPS receiver movement. The range of values
297
+ * is from 0.00 to 359.99.
298
+ */
299
+ entry->format = EXIF_FORMAT_RATIONAL;
300
+ entry->components = 1;
301
+ break;
302
+ case EXIF_TAG_GPS_IMG_DIRECTION_REF:
303
+ /*
304
+ * Indicates the reference for giving the direction of the image when it
305
+ * is captured. 'T' denotes true direction and 'M' is magnetic
306
+ * direction. Default 'T'.
307
+ */
308
+ entry->format = EXIF_FORMAT_ASCII;
309
+ entry->components = 2;
310
+ entry->size = exif_format_get_size(entry->format) * entry->components;
311
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
312
+ entry->data[0] = 'T';
313
+ entry->data[1] = 0;
314
+ break;
315
+ case EXIF_TAG_GPS_IMG_DIRECTION:
316
+ /*
317
+ * Indicates the direction of the image when it was captured. The range
318
+ * of values is from 0.00 to 359.99.
319
+ */
320
+ entry->format = EXIF_FORMAT_RATIONAL;
321
+ entry->components = 1;
322
+ break;
323
+ case EXIF_TAG_GPS_MAP_DATUM:
324
+ /*
325
+ * Indicates the geodetic survey data used by the GPS receiver. If the
326
+ * survey data is restricted to Japan, the value of this tag is 'TOKYO'
327
+ * or 'WGS-84'. If a GPS Info tag is recorded, it is strongly
328
+ * recommended that this tag be recorded.
329
+ */
330
+ entry->format = EXIF_FORMAT_ASCII;
331
+ entry->components = 0;
332
+ break;
333
+ case EXIF_TAG_GPS_DEST_LATITUDE_REF:
334
+ /*
335
+ * Indicates whether the latitude of the destination point is north or
336
+ * south latitude. The ASCII value 'N' indicates north latitude, and 'S'
337
+ * is south latitude.
338
+ */
339
+ entry->format = EXIF_FORMAT_ASCII;
340
+ entry->components = 2;
341
+ break;
342
+ case EXIF_TAG_GPS_DEST_LATITUDE:
343
+ /*
344
+ * Indicates the latitude of the destination point. The latitude is
345
+ * expressed as three RATIONAL values giving the degrees, minutes, and
346
+ * seconds, respectively. If latitude is expressed as degrees, minutes
347
+ * and seconds, a typical format would be dd/1,mm/1,ss/1. When degrees
348
+ * and minutes are used and, for example, fractions of minutes are given
349
+ * up to two decimal places, the format would be dd/1,mmmm/100,0/1.
350
+ */
351
+ entry->format = EXIF_FORMAT_RATIONAL;
352
+ entry->components = 3;
353
+ break;
354
+ case EXIF_TAG_GPS_DEST_LONGITUDE_REF:
355
+ /*
356
+ * Indicates whether the longitude of the destination point is east or
357
+ * west longitude. ASCII 'E' indicates east longitude, and 'W' is west
358
+ * longitude.
359
+ */
360
+ entry->format = EXIF_FORMAT_ASCII;
361
+ entry->components = 2;
362
+ break;
363
+ case EXIF_TAG_GPS_DEST_LONGITUDE:
364
+ /*
365
+ * Indicates the longitude of the destination point. The longitude is
366
+ * expressed as three RATIONAL values giving the degrees, minutes, and
367
+ * seconds, respectively. If longitude is expressed as degrees, minutes
368
+ * and seconds, a typical format would be ddd/1,mm/1,ss/1. When degrees
369
+ * and minutes are used and, for example, fractions of minutes are given
370
+ * up to two decimal places, the format would be ddd/1,mmmm/100,0/1.
371
+ */
372
+ entry->format = EXIF_FORMAT_RATIONAL;
373
+ entry->components = 3;
374
+ break;
375
+ case EXIF_TAG_GPS_DEST_BEARING_REF:
376
+ /*
377
+ * Indicates the reference used for giving the bearing to the
378
+ * destination point. 'T' denotes true direction and 'M' is magnetic
379
+ * direction. Default 'T'.
380
+ */
381
+ entry->format = EXIF_FORMAT_ASCII;
382
+ entry->components = 2;
383
+ entry->size = exif_format_get_size(entry->format) * entry->components;
384
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
385
+ entry->data[0] = 'T';
386
+ entry->data[1] = 0;
387
+ break;
388
+ case EXIF_TAG_GPS_DEST_BEARING:
389
+ /*
390
+ * Indicates the bearing to the destination point. The range of values
391
+ * is from 0.00 to 359.99.
392
+ */
393
+ entry->format = EXIF_FORMAT_RATIONAL;
394
+ entry->components = 1;
395
+ break;
396
+ case EXIF_TAG_GPS_DEST_DISTANCE_REF:
397
+ /*
398
+ * Indicates the unit used to express the distance to the destination
399
+ * point. 'K', 'M' and 'N' represent kilometers, miles and knots.
400
+ * Default 'K'.
401
+ */
402
+ entry->format = EXIF_FORMAT_ASCII;
403
+ entry->components = 2;
404
+ entry->size = exif_format_get_size(entry->format) * entry->components;
405
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
406
+ entry->data[0] = 'K';
407
+ entry->data[1] = 0;
408
+ break;
409
+ case EXIF_TAG_GPS_DEST_DISTANCE:
410
+ /*
411
+ * Indicates the distance to the destination point.
412
+ */
413
+ entry->format = EXIF_FORMAT_RATIONAL;
414
+ entry->components = 1;
415
+ break;
416
+ case EXIF_TAG_GPS_PROCESSING_METHOD:
417
+ /*
418
+ * A character string recording the name of the method used for location
419
+ * finding. The first byte indicates the character code used (Table
420
+ * 6,Table 7), and this is followed by the name of the method. Since
421
+ * the Type is not ASCII, NULL termination is not necessary.
422
+ */
423
+ entry->format = EXIF_FORMAT_UNDEFINED;
424
+ entry->components = 0;
425
+ break;
426
+ case EXIF_TAG_GPS_AREA_INFORMATION:
427
+ /*
428
+ * A character string recording the name of the GPS area. The first byte
429
+ * indicates the character code used (Table 6, Table 7), and this is
430
+ * followed by the name of the GPS area. Since the Type is not ASCII,
431
+ * NULL termination is not necessary.
432
+ */
433
+ entry->format = EXIF_FORMAT_UNDEFINED;
434
+ entry->components = 0;
435
+ break;
436
+ case EXIF_TAG_GPS_DATE_STAMP:
437
+ /*
438
+ * A character string recording date and time information relative to
439
+ * UTC (Coordinated Universal Time). The format is "YYYY:MM:DD." The
440
+ * length of the string is 11 bytes including NULL.
441
+ */
442
+ entry->format = EXIF_FORMAT_ASCII;
443
+ entry->components = 11;
444
+ break;
445
+ case EXIF_TAG_GPS_DIFFERENTIAL:
446
+ /*
447
+ * Indicates whether differential correction is applied to the GPS
448
+ * receiver. 0 = Measurement without differential correction. 1 =
449
+ * Differential correction applied.
450
+ */
451
+ entry->format = EXIF_FORMAT_SHORT;
452
+ entry->components = 1;
453
+ break;
454
+ default:
455
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Unexpected tag %d", (int)tag);
456
+ return;
457
+ }
458
+ if (entry->data == NULL) {
459
+ entry->size = exif_format_get_size(entry->format) * entry->components;
460
+ if (entry->size > 0) {
461
+ entry->data = egt_exif_entry_alloc(mem, entry, entry->size);
462
+ }
463
+ }
464
+ }
465
+
466
+ #define CF(entry, expected) \
467
+ { \
468
+ if (entry->format != expected) { \
469
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", \
470
+ "Invalid format '%s' for tag '%s' (0x%04x), expected '%s'.", exif_format_get_name(entry->format), \
471
+ exif_tag_get_name_in_ifd(entry->tag, EXIF_IFD_GPS), (int)entry->tag, \
472
+ exif_format_get_name(expected)); \
473
+ break; \
474
+ } \
475
+ }
476
+
477
+ #define CC(entry, expected) \
478
+ { \
479
+ if (entry->components != expected) { \
480
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Invalid number of components %i for tag '%s' (0x%04x), " \
481
+ "expected %i.", \
482
+ (int)entry->components, exif_tag_get_name_in_ifd(entry->tag, EXIF_IFD_GPS), (int)entry->tag, \
483
+ (int)expected); \
484
+ break; \
485
+ } \
486
+ }
487
+
488
+ static VALUE egt_exif_entry_get_value(ExifEntry *entry)
489
+ {
490
+ ExifByteOrder byte_order;
491
+ ExifRational rat;
492
+ VALUE val;
493
+ unsigned long i;
494
+
495
+ byte_order = exif_data_get_byte_order(entry->parent->parent);
496
+
497
+ /* Sanity check */
498
+ if (entry->size != entry->components * exif_format_get_size(entry->format)) {
499
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Invalid size of entry with tag %d (%i, expected %li x %i).",
500
+ (int)entry->tag, entry->size, entry->components, exif_format_get_size(entry->format));
501
+ return Qnil;
502
+ }
503
+
504
+ #define READ_AND_RETURN_RATIONAL() \
505
+ val = rb_ary_new(); \
506
+ if (entry->components == 1) { \
507
+ rat = exif_get_rational(entry->data, byte_order); \
508
+ if (rat.numerator == 0 && rat.denominator == 0) { \
509
+ rat.denominator = 1; \
510
+ } \
511
+ return rb_funcall(rb_mKernel, egt_id_Rational, 2, INT2FIX(rat.numerator), INT2FIX(rat.denominator)); \
512
+ } else { \
513
+ for (i = 0; i < entry->components; i++) { \
514
+ rat = exif_get_rational(entry->data + exif_format_get_size(entry->format) * i, byte_order); \
515
+ if (rat.numerator == 0 && rat.denominator == 0) { \
516
+ rat.denominator = 1; \
517
+ } \
518
+ rb_ary_push(val, \
519
+ rb_funcall(rb_mKernel, egt_id_Rational, 2, INT2FIX(rat.numerator), INT2FIX(rat.denominator))); \
520
+ } \
521
+ return val; \
522
+ }
523
+
524
+ switch ((int)entry->tag) {
525
+ case EXIF_TAG_GPS_VERSION_ID:
526
+ CF(entry, EXIF_FORMAT_BYTE);
527
+ CC(entry, 4);
528
+ return rb_sprintf("%u.%u.%u.%u", entry->data[0], entry->data[1], entry->data[2], entry->data[3]);
529
+ case EXIF_TAG_GPS_LONGITUDE_REF:
530
+ case EXIF_TAG_GPS_STATUS:
531
+ case EXIF_TAG_GPS_MEASURE_MODE:
532
+ case EXIF_TAG_GPS_DOP:
533
+ case EXIF_TAG_GPS_SPEED_REF:
534
+ case EXIF_TAG_GPS_LATITUDE_REF:
535
+ case EXIF_TAG_GPS_TRACK_REF:
536
+ case EXIF_TAG_GPS_IMG_DIRECTION_REF:
537
+ case EXIF_TAG_GPS_DEST_LATITUDE_REF:
538
+ case EXIF_TAG_GPS_DEST_LONGITUDE_REF:
539
+ case EXIF_TAG_GPS_DEST_BEARING_REF:
540
+ case EXIF_TAG_GPS_DEST_DISTANCE_REF:
541
+ CF(entry, EXIF_FORMAT_ASCII);
542
+ CC(entry, 2);
543
+ return rb_str_new_cstr((char *)entry->data);
544
+ case EXIF_TAG_GPS_ALTITUDE_REF:
545
+ CF(entry, EXIF_FORMAT_BYTE);
546
+ CC(entry, 1);
547
+ return INT2FIX(entry->data[0]);
548
+ case EXIF_TAG_GPS_ALTITUDE:
549
+ case EXIF_TAG_GPS_SPEED:
550
+ case EXIF_TAG_GPS_TRACK:
551
+ case EXIF_TAG_GPS_IMG_DIRECTION:
552
+ case EXIF_TAG_GPS_DEST_BEARING:
553
+ case EXIF_TAG_GPS_DEST_DISTANCE:
554
+ CF(entry, EXIF_FORMAT_RATIONAL);
555
+ CC(entry, 1);
556
+ READ_AND_RETURN_RATIONAL();
557
+ case EXIF_TAG_GPS_TIME_STAMP:
558
+ case EXIF_TAG_GPS_LATITUDE:
559
+ case EXIF_TAG_GPS_LONGITUDE:
560
+ case EXIF_TAG_GPS_DEST_LATITUDE:
561
+ case EXIF_TAG_GPS_DEST_LONGITUDE:
562
+ CF(entry, EXIF_FORMAT_RATIONAL);
563
+ CC(entry, 3);
564
+ READ_AND_RETURN_RATIONAL();
565
+ case EXIF_TAG_GPS_SATELLITES:
566
+ case EXIF_TAG_GPS_MAP_DATUM:
567
+ CF(entry, EXIF_FORMAT_ASCII);
568
+ return rb_str_new_cstr((char *)entry->data);
569
+ case EXIF_TAG_GPS_PROCESSING_METHOD:
570
+ case EXIF_TAG_GPS_AREA_INFORMATION:
571
+ return rb_str_new((char *)entry->data, entry->size);
572
+ case EXIF_TAG_GPS_DATE_STAMP:
573
+ CF(entry, EXIF_FORMAT_ASCII);
574
+ CC(entry, 11);
575
+ return rb_str_new_cstr((char *)entry->data);
576
+ case EXIF_TAG_GPS_DIFFERENTIAL:
577
+ CF(entry, EXIF_FORMAT_SHORT);
578
+ CC(entry, 1);
579
+ return INT2FIX(exif_get_short(entry->data, byte_order));
580
+ default:
581
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Unexpected tag %d", (int)entry->tag);
582
+ }
583
+ return Qnil;
584
+ }
585
+
586
+ static void egt_generate_virtual_fields(VALUE values)
587
+ {
588
+ VALUE val;
589
+
590
+ #define CONVERT_COORDINATES(name, from, to) \
591
+ val = rb_hash_aref(values, from); \
592
+ if (val != Qnil) { \
593
+ Check_Type(val, T_ARRAY); \
594
+ if (RARRAY_LEN(val) == 3) { \
595
+ VALUE deg = rb_ary_entry(val, 0), min = rb_funcall(rb_ary_entry(val, 1), egt_id_div, 1, egt_flt_min), \
596
+ sec = rb_funcall(rb_ary_entry(val, 2), egt_id_div, 1, egt_flt_sec); \
597
+ rb_hash_aset(values, to, rb_funcall(rb_funcall(deg, egt_id_add, 1, rb_funcall(min, egt_id_add, 1, sec)), \
598
+ egt_id_to_f, 0)); \
599
+ } else { \
600
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Expected " name " to have 3 items, but got %ld", \
601
+ RARRAY_LEN(val)); \
602
+ } \
603
+ }
604
+
605
+ CONVERT_COORDINATES(":latitude", egt_sym_latitude, egt_sym__latitude);
606
+ CONVERT_COORDINATES(":longitude", egt_sym_longitude, egt_sym__longitude);
607
+ val = rb_hash_aref(values, egt_sym_altitude);
608
+ if (val != Qnil) {
609
+ rb_hash_aset(values, egt_sym__altitude, rb_funcall(val, egt_id_to_f, 0));
610
+ }
611
+ {
612
+ VALUE time = rb_hash_aref(values, egt_sym_time_stamp);
613
+ VALUE date = rb_hash_aref(values, egt_sym_date_stamp);
614
+
615
+ if (time != Qnil && date != Qnil) {
616
+ VALUE sdate;
617
+ Check_Type(time, T_ARRAY);
618
+ Check_Type(date, T_STRING);
619
+ sdate = rb_funcall(date, egt_id_split, 1, egt_str_colon);
620
+ if (RARRAY_LEN(time) == 3) {
621
+ if (RARRAY_LEN(sdate) == 3) {
622
+ VALUE hour = rb_funcall(rb_ary_entry(time, 0), egt_id_to_i, 0),
623
+ min = rb_funcall(rb_ary_entry(time, 1), egt_id_to_i, 0),
624
+ sec = rb_funcall(rb_ary_entry(time, 2), egt_id_to_i, 0);
625
+ VALUE year = rb_funcall(rb_ary_entry(sdate, 0), egt_id_to_i, 0),
626
+ month = rb_funcall(rb_ary_entry(sdate, 1), egt_id_to_i, 0),
627
+ day = rb_funcall(rb_ary_entry(sdate, 2), egt_id_to_i, 0);
628
+ rb_hash_aset(values, egt_sym__timestamp,
629
+ rb_funcall(rb_cTime, egt_id_utc, 6, year, month, day, hour, min, sec));
630
+ } else {
631
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt",
632
+ "Expected :date_stamp to have 3 sections separated by ':', but got %ld",
633
+ RARRAY_LEN(sdate));
634
+ }
635
+ } else {
636
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Expected :time_stamp to have 3 items, but got %ld",
637
+ RARRAY_LEN(time));
638
+ }
639
+ }
640
+ }
641
+ #undef CONVERT_COORDINATES
642
+ }
643
+
644
+ static void egt_exif_entry_set_value(ExifMem *mem, ExifEntry *entry, VALUE values, ID key)
645
+ {
646
+ VALUE val = rb_hash_aref(values, key);
647
+ ExifByteOrder byte_order = exif_data_get_byte_order(entry->parent->parent);
648
+
649
+ switch ((int)entry->tag) {
650
+ case EXIF_TAG_GPS_VERSION_ID:
651
+ if (val != Qnil) {
652
+ VALUE sval;
653
+
654
+ Check_Type(val, T_STRING);
655
+ sval = rb_funcall(val, egt_id_split, 1, egt_str_period);
656
+ if (RARRAY_LEN(sval) == 3) {
657
+ entry->data[0] = (ExifByte)FIX2INT(rb_funcall(rb_ary_entry(sval, 0), egt_id_to_i, 0));
658
+ entry->data[1] = (ExifByte)FIX2INT(rb_funcall(rb_ary_entry(sval, 1), egt_id_to_i, 0));
659
+ entry->data[2] = (ExifByte)FIX2INT(rb_funcall(rb_ary_entry(sval, 2), egt_id_to_i, 0));
660
+ entry->data[3] = (ExifByte)FIX2INT(rb_funcall(rb_ary_entry(sval, 3), egt_id_to_i, 0));
661
+ } else {
662
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt",
663
+ "Expected :gps_version_id to have 4 sections separated by '.', but got %ld", RARRAY_LEN(sval));
664
+ }
665
+ }
666
+ return;
667
+ case EXIF_TAG_GPS_LONGITUDE_REF:
668
+ case EXIF_TAG_GPS_STATUS:
669
+ case EXIF_TAG_GPS_MEASURE_MODE:
670
+ case EXIF_TAG_GPS_DOP:
671
+ case EXIF_TAG_GPS_SPEED_REF:
672
+ case EXIF_TAG_GPS_LATITUDE_REF:
673
+ case EXIF_TAG_GPS_TRACK_REF:
674
+ case EXIF_TAG_GPS_IMG_DIRECTION_REF:
675
+ case EXIF_TAG_GPS_DEST_LATITUDE_REF:
676
+ case EXIF_TAG_GPS_DEST_LONGITUDE_REF:
677
+ case EXIF_TAG_GPS_DEST_BEARING_REF:
678
+ case EXIF_TAG_GPS_DEST_DISTANCE_REF:
679
+ if (val != Qnil) {
680
+ Check_Type(val, T_STRING);
681
+ if (entry->data) {
682
+ exif_mem_free(mem, entry->data);
683
+ }
684
+ entry->components = 2;
685
+ entry->size = entry->components * sizeof(char);
686
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(char));
687
+ entry->data[0] = RSTRING_PTR(val)[0];
688
+ entry->data[1] = 0;
689
+ }
690
+ return;
691
+ case EXIF_TAG_GPS_ALTITUDE_REF:
692
+ if (val != Qnil) {
693
+ Check_Type(val, T_FIXNUM);
694
+ if (entry->data) {
695
+ exif_mem_free(mem, entry->data);
696
+ }
697
+ entry->components = 1;
698
+ entry->size = entry->components * sizeof(ExifByte);
699
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(ExifByte));
700
+ entry->data[0] = (ExifByte)FIX2INT(val);
701
+ }
702
+ return;
703
+ case EXIF_TAG_GPS_ALTITUDE:
704
+ case EXIF_TAG_GPS_SPEED:
705
+ case EXIF_TAG_GPS_TRACK:
706
+ case EXIF_TAG_GPS_IMG_DIRECTION:
707
+ case EXIF_TAG_GPS_DEST_BEARING:
708
+ case EXIF_TAG_GPS_DEST_DISTANCE:
709
+ if (val != Qnil) {
710
+ ExifRational rat;
711
+
712
+ val = rb_funcall(val, egt_id_rationalize, 0), rat.numerator = FIX2INT(rb_funcall(val, egt_id_numerator, 0));
713
+ rat.denominator = FIX2INT(rb_funcall(val, egt_id_denominator, 0));
714
+ if (entry->data) {
715
+ exif_mem_free(mem, entry->data);
716
+ }
717
+ entry->components = 1;
718
+ entry->size = entry->components * sizeof(ExifRational);
719
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(ExifRational));
720
+ exif_set_rational(entry->data, byte_order, rat);
721
+ }
722
+ return;
723
+ case EXIF_TAG_GPS_TIME_STAMP:
724
+ case EXIF_TAG_GPS_LATITUDE:
725
+ case EXIF_TAG_GPS_LONGITUDE:
726
+ case EXIF_TAG_GPS_DEST_LATITUDE:
727
+ case EXIF_TAG_GPS_DEST_LONGITUDE:
728
+ if (val != Qnil) {
729
+ Check_Type(val, T_ARRAY);
730
+
731
+ if (RARRAY_LEN(val) == 3) {
732
+ unsigned long i;
733
+
734
+ entry->components = 3;
735
+ entry->size = entry->components * sizeof(ExifRational);
736
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(ExifRational));
737
+
738
+ for (i = 0; i < entry->components; i++) {
739
+ ExifRational rat;
740
+ VALUE vrat;
741
+ vrat = rb_funcall(rb_ary_entry(val, i), egt_id_rationalize, 0);
742
+ rat.numerator = FIX2INT(rb_funcall(vrat, egt_id_numerator, 0));
743
+ rat.denominator = FIX2INT(rb_funcall(vrat, egt_id_denominator, 0));
744
+ exif_set_rational(entry->data + exif_format_get_size(entry->format) * i, byte_order, rat);
745
+ }
746
+ } else {
747
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt",
748
+ "Expected %s to have 4 sections separated by '.', but got %ld", StringValueCStr(key),
749
+ RARRAY_LEN(val));
750
+ }
751
+ }
752
+ return;
753
+ case EXIF_TAG_GPS_SATELLITES:
754
+ case EXIF_TAG_GPS_MAP_DATUM:
755
+ case EXIF_TAG_GPS_PROCESSING_METHOD:
756
+ case EXIF_TAG_GPS_AREA_INFORMATION:
757
+ if (val != Qnil) {
758
+ Check_Type(val, T_STRING);
759
+ if (entry->data) {
760
+ exif_mem_free(mem, entry->data);
761
+ }
762
+ entry->components = RSTRING_LEN(val) + 1;
763
+ entry->size = entry->components * sizeof(char);
764
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(char));
765
+ memcpy(entry->data, RSTRING_PTR(val), RSTRING_LEN(val));
766
+ entry->data[RSTRING_LEN(val)] = 0;
767
+ }
768
+ return;
769
+ case EXIF_TAG_GPS_DATE_STAMP:
770
+ if (val != Qnil) {
771
+ Check_Type(val, T_STRING);
772
+ if (RSTRING_LEN(val) == 10) {
773
+ if (entry->data) {
774
+ exif_mem_free(mem, entry->data);
775
+ }
776
+ entry->components = 11;
777
+ entry->size = entry->components * sizeof(char);
778
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(char));
779
+ memcpy(entry->data, RSTRING_PTR(val), 10);
780
+ entry->data[10] = 0;
781
+ }
782
+ }
783
+ return;
784
+ case EXIF_TAG_GPS_DIFFERENTIAL:
785
+ if (val != Qnil) {
786
+ Check_Type(val, T_FIXNUM);
787
+ if (entry->data) {
788
+ exif_mem_free(mem, entry->data);
789
+ }
790
+ entry->components = 1;
791
+ entry->size = entry->components * sizeof(ExifShort);
792
+ entry->data = exif_mem_alloc(mem, entry->components * sizeof(ExifShort));
793
+ exif_set_short(entry->data, byte_order, (ExifShort)FIX2INT(val));
794
+ }
795
+ return;
796
+ default:
797
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "Unexpected tag %d", (int)entry->tag);
798
+ }
799
+ }
800
+
801
+ static void egt_parse_virtual_fields(VALUE values)
802
+ {
803
+
804
+ VALUE val;
805
+
806
+ val = rb_hash_aref(values, egt_sym__altitude);
807
+ if (val != Qnil) {
808
+ rb_hash_aset(values, egt_sym_altitude, rb_funcall(val, egt_id_rationalize, 0));
809
+ }
810
+
811
+ val = rb_hash_aref(values, egt_sym__timestamp);
812
+ if (val != Qnil) {
813
+ VALUE time;
814
+ if (!rb_obj_is_kind_of(val, rb_cTime)) {
815
+ rb_raise(rb_eTypeError, "wrong argument (%" PRIsVALUE ")! (Expected kind of %" PRIsVALUE ")",
816
+ rb_obj_class(val), rb_cTime);
817
+ }
818
+ rb_hash_aset(values, egt_sym_date_stamp, rb_funcall(val, egt_id_strftime, 1, egt_str_date_format));
819
+ time = rb_ary_new();
820
+ rb_ary_push(time, rb_funcall(rb_funcall(val, egt_id_hour, 0), egt_id_rationalize, 0));
821
+ rb_ary_push(time, rb_funcall(rb_funcall(val, egt_id_min, 0), egt_id_rationalize, 0));
822
+ rb_ary_push(time, rb_funcall(rb_funcall(val, egt_id_sec, 0), egt_id_rationalize, 0));
823
+ rb_hash_aset(values, egt_sym_time_stamp, time);
824
+ }
825
+
826
+ #define CONVERT_COORDINATES(from, to, ref, negative, positive) \
827
+ val = rb_hash_aref(values, from); \
828
+ if (val != Qnil) { \
829
+ VALUE deg, min, sec, tmp; \
830
+ \
831
+ if (!rb_obj_is_kind_of(val, rb_cNumeric)) { \
832
+ rb_raise(rb_eTypeError, "wrong argument (%" PRIsVALUE ")! (Expected kind of %" PRIsVALUE ")", \
833
+ rb_obj_class(val), rb_cNumeric); \
834
+ } \
835
+ deg = rb_funcall(val, egt_id_truncate, 0); \
836
+ min = rb_funcall(rb_funcall(rb_funcall(val, egt_id_sub, 1, deg), egt_id_mul, 1, egt_flt_min), egt_id_truncate, \
837
+ 0); \
838
+ tmp = rb_funcall(rb_funcall(rb_funcall(val, egt_id_sub, 1, deg), egt_id_sub, 1, \
839
+ rb_funcall(min, egt_id_div, 1, egt_flt_min)), \
840
+ egt_id_mul, 1, egt_flt_sec); \
841
+ sec = rb_funcall(tmp, egt_id_round, 1, INT2FIX(3)); \
842
+ rb_hash_aset(values, to, rb_ary_new_from_args(3, rb_funcall(deg, egt_id_rationalize, 0), \
843
+ rb_funcall(min, egt_id_rationalize, 0), \
844
+ rb_funcall(sec, egt_id_rationalize, 0))); \
845
+ if (RTEST(rb_funcall(val, egt_id_negative_p, 0))) { \
846
+ rb_hash_aset(values, ref, negative); \
847
+ } else { \
848
+ rb_hash_aset(values, ref, positive); \
849
+ } \
850
+ }
851
+
852
+ CONVERT_COORDINATES(egt_sym__latitude, egt_sym_latitude, egt_sym_latitude_ref, egt_str_south, egt_str_north);
853
+ CONVERT_COORDINATES(egt_sym__longitude, egt_sym_longitude, egt_sym_longitude_ref, egt_str_west, egt_str_east);
854
+ #undef CONVERT_COORDINATES
855
+ }
856
+
857
+ static void egt_handle_tag(ExifMem *mem, ExifData *exif_data, ExifTag tag, VALUE prev_values, VALUE new_values, ID key)
858
+ {
859
+ ExifEntry *exif_entry;
860
+ VALUE val;
861
+
862
+ if (rb_hash_aref(new_values, key) != Qnil) {
863
+ exif_entry = exif_content_get_entry(exif_data->ifd[EXIF_IFD_GPS], tag);
864
+ if (!exif_entry) {
865
+ exif_entry = exif_entry_new();
866
+ exif_entry->tag = tag;
867
+ exif_content_add_entry(exif_data->ifd[EXIF_IFD_GPS], exif_entry);
868
+ egt_exif_entry_initialize(mem, exif_entry, tag);
869
+ /* the entry has been added to the IFD, so we can unref it */
870
+ exif_entry_unref(exif_entry);
871
+ } else {
872
+ val = egt_exif_entry_get_value(exif_entry);
873
+ rb_hash_aset(prev_values, key, val);
874
+ }
875
+
876
+ egt_exif_entry_set_value(mem, exif_entry, new_values, key);
877
+ }
878
+ }
879
+
880
+ static ExifData *egt_exif_data_new_from_file(ExifMem *mem, const char *path)
881
+ {
882
+ ExifData *edata;
883
+ ExifLoader *loader;
884
+
885
+ loader = exif_loader_new_mem(mem);
886
+ exif_loader_log(loader, logger);
887
+ exif_loader_write_file(loader, path);
888
+ edata = exif_loader_get_data(loader);
889
+ exif_loader_unref(loader);
890
+
891
+ return (edata);
892
+ }
893
+
894
+ static void egt_save_exif_to_file(ExifData *exif_data, char *file_path)
895
+ {
896
+ JPEGData *jdata;
897
+ unsigned char *exif_blob = NULL;
898
+ unsigned int exif_blob_len;
899
+
900
+ /* Parse the JPEG file. */
901
+ jdata = jpeg_data_new();
902
+ jpeg_data_log(jdata, logger);
903
+ jpeg_data_load_file(jdata, file_path);
904
+
905
+ /* Make sure the EXIF data is not too big. */
906
+ exif_data_save_data(exif_data, &exif_blob, &exif_blob_len);
907
+ if (exif_blob_len) {
908
+ free(exif_blob);
909
+ if (exif_blob_len > 0xffff) {
910
+ rb_raise(rb_eArgError, "too much EXIF data (%i bytes). Only %i bytes are allowed.", exif_blob_len, 0xffff);
911
+ }
912
+ };
913
+
914
+ jpeg_data_set_exif_data(jdata, exif_data);
915
+
916
+ if (!jpeg_data_save_file(jdata, file_path)) {
917
+ exif_log(logger, EXIF_LOG_CODE_DEBUG, "RubyExt", "failed to write updated EXIF to %s", file_path);
918
+ }
919
+ jpeg_data_unref(jdata);
920
+ }
921
+
922
+ static VALUE egt_write_tag(VALUE self, VALUE file_path, VALUE new_values)
923
+ {
924
+ ExifData *exif_data;
925
+ ExifMem *mem;
926
+ VALUE prev_values;
927
+ (void)self;
928
+
929
+ Check_Type(file_path, T_STRING);
930
+ Check_Type(new_values, T_HASH);
931
+
932
+ mem = exif_mem_new_default();
933
+ exif_data = egt_exif_data_new_from_file(mem, RSTRING_PTR(file_path));
934
+ if (!exif_data) {
935
+ rb_raise(rb_eArgError, "file not readable or no EXIF data in file");
936
+ }
937
+
938
+ prev_values = rb_hash_new();
939
+
940
+ egt_parse_virtual_fields(new_values);
941
+ #define X(e, i) egt_handle_tag(mem, exif_data, e, prev_values, new_values, egt_sym_##i);
942
+ TAG_MAPPING(X)
943
+ #undef X
944
+ egt_generate_virtual_fields(prev_values);
945
+
946
+ if (rb_hash_size(new_values) > 0) {
947
+ egt_save_exif_to_file(exif_data, RSTRING_PTR(file_path));
948
+ }
949
+
950
+ exif_data_free(exif_data);
951
+ exif_mem_unref(mem);
952
+ return prev_values;
953
+ }
954
+
955
+ #ifdef DEBUG
956
+ /* ANSI escape codes for output colors */
957
+ #define COL_BLUE "\033[34m"
958
+ #define COL_GREEN "\033[32m"
959
+ #define COL_RED "\033[31m"
960
+ #define COL_BOLD "\033[1m"
961
+ #define COL_UNDERLINE "\033[4m"
962
+ #define COL_NORMAL "\033[m"
963
+
964
+ #define put_colorstring(file, colorstring) \
965
+ do { \
966
+ if (isatty(fileno(file))) { \
967
+ fputs(colorstring, file); \
968
+ } \
969
+ } while (0)
970
+
971
+ static void log_func(ExifLog *log, ExifLogCode code, const char *domain, const char *format, va_list args, void *data)
972
+ {
973
+ (void)log;
974
+ (void)data;
975
+
976
+ switch (code) {
977
+ case EXIF_LOG_CODE_DEBUG:
978
+ put_colorstring(stdout, COL_GREEN);
979
+ fprintf(stdout, "%s: ", domain);
980
+ vfprintf(stdout, format, args);
981
+ put_colorstring(stdout, COL_NORMAL);
982
+ printf("\n");
983
+ break;
984
+ case EXIF_LOG_CODE_CORRUPT_DATA:
985
+ case EXIF_LOG_CODE_NO_MEMORY:
986
+ put_colorstring(stderr, COL_RED COL_BOLD COL_UNDERLINE);
987
+ fprintf(stderr, "%s\n", exif_log_code_get_title(code));
988
+ put_colorstring(stderr, COL_NORMAL COL_RED);
989
+ fprintf(stderr, "%s\n", exif_log_code_get_message(code));
990
+ fprintf(stderr, "%s: ", domain);
991
+ vfprintf(stderr, format, args);
992
+ put_colorstring(stderr, COL_NORMAL);
993
+ fprintf(stderr, "\n");
994
+
995
+ /*
996
+ * EXIF_LOG_CODE_NO_MEMORY is always a fatal error, so exit.
997
+ * EXIF_LOG_CODE_CORRUPT_DATA is only fatal if debug mode
998
+ * is off.
999
+ *
1000
+ * Exiting the program due to a log message is really a bad
1001
+ * idea to begin with. This should be removed once the libexif
1002
+ * API is fixed to properly return error codes everywhere.
1003
+ */
1004
+ if ((code == EXIF_LOG_CODE_NO_MEMORY))
1005
+ exit(1);
1006
+ break;
1007
+ default:
1008
+ put_colorstring(stdout, COL_BLUE);
1009
+ printf("%s: ", domain);
1010
+ vprintf(format, args);
1011
+ put_colorstring(stdout, COL_NORMAL);
1012
+ printf("\n");
1013
+ break;
1014
+ }
1015
+ }
1016
+ #endif
1017
+
1018
+ void Init_exif_geo_tag_ext(void)
1019
+ {
1020
+ VALUE interned;
1021
+
1022
+ egt_mExifGeoTag = rb_define_module("ExifGeoTag");
1023
+
1024
+ rb_define_singleton_method(egt_mExifGeoTag, "write_tag", egt_write_tag, 2);
1025
+
1026
+ #define X(e, i) egt_sym_##i = ID2SYM(rb_intern(#i));
1027
+ TAG_MAPPING(X)
1028
+ #undef X
1029
+
1030
+ egt_sym__latitude = ID2SYM(rb_intern("_latitude"));
1031
+ egt_sym__longitude = ID2SYM(rb_intern("_longitude"));
1032
+ egt_sym__altitude = ID2SYM(rb_intern("_altitude"));
1033
+ egt_sym__timestamp = ID2SYM(rb_intern("_timestamp"));
1034
+
1035
+ egt_id_Rational = rb_intern("Rational");
1036
+ egt_id_add = rb_intern("+");
1037
+ egt_id_div = rb_intern("/");
1038
+ egt_id_sub = rb_intern("-");
1039
+ egt_id_mul = rb_intern("*");
1040
+ egt_id_to_f = rb_intern("to_f");
1041
+ egt_id_to_i = rb_intern("to_i");
1042
+ egt_id_rationalize = rb_intern("rationalize");
1043
+ egt_id_round = rb_intern("round");
1044
+ egt_id_split = rb_intern("split");
1045
+ egt_id_utc = rb_intern("utc");
1046
+ egt_id_numerator = rb_intern("numerator");
1047
+ egt_id_denominator = rb_intern("denominator");
1048
+ egt_id_strftime = rb_intern("strftime");
1049
+ egt_id_hour = rb_intern("hour");
1050
+ egt_id_min = rb_intern("min");
1051
+ egt_id_sec = rb_intern("sec");
1052
+ egt_id_truncate = rb_intern("truncate");
1053
+ egt_id_negative_p = rb_intern("negative?");
1054
+
1055
+ interned = rb_ary_new();
1056
+ rb_const_set(egt_mExifGeoTag, rb_intern("_INTERNED"), interned);
1057
+ egt_str_colon = rb_str_freeze(rb_str_new_cstr(":"));
1058
+ rb_ary_push(interned, egt_str_colon);
1059
+ egt_str_period = rb_str_freeze(rb_str_new_cstr("."));
1060
+ rb_ary_push(interned, egt_str_period);
1061
+ egt_str_date_format = rb_str_freeze(rb_str_new_cstr("%Y:%m:%d"));
1062
+ rb_ary_push(interned, egt_str_date_format);
1063
+ egt_str_south = rb_str_freeze(rb_str_new_cstr("S"));
1064
+ rb_ary_push(interned, egt_str_south);
1065
+ egt_str_north = rb_str_freeze(rb_str_new_cstr("N"));
1066
+ rb_ary_push(interned, egt_str_north);
1067
+ egt_str_west = rb_str_freeze(rb_str_new_cstr("W"));
1068
+ rb_ary_push(interned, egt_str_west);
1069
+ egt_str_east = rb_str_freeze(rb_str_new_cstr("E"));
1070
+ rb_ary_push(interned, egt_str_east);
1071
+
1072
+ egt_flt_min = rb_float_new(60);
1073
+ egt_flt_sec = rb_float_new(3600);
1074
+
1075
+ #ifdef DEBUG
1076
+ logger = exif_log_new();
1077
+ exif_log_set_func(logger, log_func, NULL);
1078
+ #else
1079
+ logger = NULL;
1080
+ #endif
1081
+ }