exif_geo_tag 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }