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 +7 -0
- data/ext/exif-i18n.c +42 -0
- data/ext/exif-i18n.h +56 -0
- data/ext/exif_geo_tag.c +1081 -0
- data/ext/extconf.rb +6 -0
- data/ext/jpeg-data.c +490 -0
- data/ext/jpeg-data.h +92 -0
- data/ext/jpeg-marker.c +122 -0
- data/ext/jpeg-marker.h +103 -0
- data/lib/exif_geo_tag.rb +5 -0
- data/lib/exif_geo_tag/version.rb +3 -0
- metadata +82 -0
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__ */
|
data/ext/exif_geo_tag.c
ADDED
@@ -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
|
+
}
|