Mercurial > geeqie.yaz
annotate src/exif.c @ 54:b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
* exif.[ch]: A lot of code clean up, add generic tiff header parser,
remove use of packed structures to interpret tiff file format,
fix possible endless loops in tiff parser with corrupt IFD tables,
and fix possible overflow in jpeg exif parser.
* format_canon.[ch]: Add additional makernote values, plus a few
spelling fixes. Header update.
* format_fuji.[ch]: Header update.
* format_nikon.[ch]: Updates to use new tiff parsing utils in exif.c,
code cleanup. Header update.
* format_raw.[ch]: Add pathname argument to file descriptor version of
raw parser to quickly rule out non-raw files based on file extension.
Add raw header match type to check for tiff "make" field value.
* image-load.c (image_loader_begin): Add image filename for raw parser.
author | gqview |
---|---|
date | Fri, 10 Jun 2005 02:44:36 +0000 |
parents | 276ea4c98d33 |
children | 1e21f094e0be |
rev | line source |
---|---|
9 | 1 /* |
2 * GQView | |
3 * (C) 2004 John Ellis | |
4 * | |
5 * Authors: | |
6 * Support for Exif file format, originally written by Eric Swalens. | |
7 * Modified by Quy Tonthat | |
8 * | |
9 * Reimplemented with generic data storage by John Ellis (Nov 2003) | |
10 * | |
11 * The tags were added with information from the FREE document: | |
12 * http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html | |
13 * | |
14 * For the official Exif Format, please refer to: | |
15 * http://www.exif.org | |
16 * http://www.exif.org/specifications.html (PDF spec sheets) | |
17 * | |
18 * Notes: | |
19 * Additional tag formats should be added to the proper | |
20 * location in ExifKnownMarkersList[]. | |
21 * | |
22 * Human readable ouput (that needs additional processing of data to | |
23 * be useable) can be defined by adding a key to ExifFormattedList[], | |
24 * then handling that tag in the function exif_get_formatted_by_key(). | |
25 * The human readable formatted keys must begin with the character 'f'. | |
26 * | |
27 * Unsupported at this time: | |
28 * IFD1 (thumbnail) | |
29 * MakerNote | |
30 * GPSInfo | |
31 * | |
32 * TODO: | |
33 * Convert data to useable form in the ??_as_text function for: | |
34 * ComponentsConfiguration | |
35 * UserComment (convert this to UTF-8?) | |
36 * | |
37 | |
38 This program is free software; you can redistribute it and/or modify | |
39 it under the terms of the GNU General Public License as published by | |
40 the Free Software Foundation; either version 2 of the License, or | |
41 (at your option) any later version. | |
42 | |
43 This program is distributed in the hope that it will be useful, | |
44 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
45 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
46 GNU General Public License for more details. | |
47 | |
48 You should have received a copy of the GNU General Public License | |
49 along with this program; if not, write to the Free Software | |
50 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
51 */ | |
52 | |
53 #ifdef HAVE_CONFIG_H | |
54 # include "config.h" | |
55 #endif | |
56 | |
57 #include <stdio.h> | |
58 #include <string.h> | |
59 #include <fcntl.h> | |
60 #include <unistd.h> | |
61 #include <sys/types.h> | |
62 #include <sys/stat.h> | |
63 #include <sys/mman.h> | |
64 #include <math.h> | |
65 | |
66 #include <glib.h> | |
67 | |
68 #include "intl.h" | |
69 | |
70 #include "exif.h" | |
71 | |
43
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
72 #include "format_raw.h" |
9 | 73 #include "ui_fileops.h" |
74 | |
75 | |
76 /* | |
77 *----------------------------------------------------------------------------- | |
78 * Tag formats | |
79 *----------------------------------------------------------------------------- | |
80 */ | |
81 | |
82 ExifFormatAttrib ExifFormatList[] = { | |
83 { EXIF_FORMAT_UNKNOWN, 1, "unknown", "unknown" }, | |
84 { EXIF_FORMAT_BYTE_UNSIGNED, 1, "ubyte", "unsigned byte" }, | |
85 { EXIF_FORMAT_STRING, 1, "string", "string" }, | |
86 { EXIF_FORMAT_SHORT_UNSIGNED, 2, "ushort", "unsigned short" }, | |
87 { EXIF_FORMAT_LONG_UNSIGNED, 4, "ulong", "unsigned long" }, | |
88 { EXIF_FORMAT_RATIONAL_UNSIGNED,8, "urational", "unsigned rational" }, | |
89 { EXIF_FORMAT_BYTE, 1, "byte", "byte" }, | |
90 { EXIF_FORMAT_UNDEFINED, 1, "undefined", "undefined" }, | |
91 { EXIF_FORMAT_SHORT, 2, "sshort", "signed short" }, | |
92 { EXIF_FORMAT_LONG, 4, "slong", "signed long" }, | |
93 { EXIF_FORMAT_RATIONAL, 8, "srational", "signed rational" }, | |
94 { EXIF_FORMAT_FLOAT, 4, "float", "float" }, | |
95 { EXIF_FORMAT_DOUBLE, 8, "double", "double" }, | |
96 { -1, 0, NULL } | |
97 }; | |
98 | |
99 /* tags that are special, or need special treatment */ | |
100 #define TAG_EXIFOFFSET 0x8769 | |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
101 #define TAG_EXIFMAKERNOTE 0x927c |
9 | 102 |
103 | |
104 /* | |
105 *----------------------------------------------------------------------------- | |
106 * Data | |
107 *----------------------------------------------------------------------------- | |
108 */ | |
109 | |
110 static ExifTextList ExifOrientationList[] = { | |
111 { EXIF_ORIENTATION_UNKNOWN, N_("unknown") }, | |
112 { EXIF_ORIENTATION_TOP_LEFT, N_("top left") }, | |
113 { EXIF_ORIENTATION_TOP_RIGHT, N_("top right") }, | |
114 { EXIF_ORIENTATION_BOTTOM_RIGHT,N_("bottom right") }, | |
115 { EXIF_ORIENTATION_BOTTOM_LEFT, N_("bottom left") }, | |
116 { EXIF_ORIENTATION_LEFT_TOP, N_("left top") }, | |
117 { EXIF_ORIENTATION_RIGHT_TOP, N_("right top") }, | |
118 { EXIF_ORIENTATION_RIGHT_BOTTOM,N_("right bottom") }, | |
119 { EXIF_ORIENTATION_LEFT_BOTTOM, N_("left bottom") }, | |
120 EXIF_TEXT_LIST_END | |
121 }; | |
122 | |
123 static ExifTextList ExifUnitList[] = { | |
124 { EXIF_UNIT_UNKNOWN, N_("unknown") }, | |
125 { EXIF_UNIT_NOUNIT, "" }, | |
126 { EXIF_UNIT_INCH, N_("inch") }, | |
127 { EXIF_UNIT_CENTIMETER, N_("centimeter") }, | |
128 EXIF_TEXT_LIST_END | |
129 }; | |
130 | |
131 static ExifTextList ExifYCbCrPosList[] = { | |
132 { 1, "center" }, | |
133 { 2, "datum" }, | |
134 EXIF_TEXT_LIST_END | |
135 }; | |
136 | |
137 static ExifTextList ExifMeteringModeList[] = { | |
138 { 0, N_("unknown") }, | |
139 { 1, N_("average") }, | |
140 { 2, N_("center weighted") }, | |
141 { 3, N_("spot") }, | |
142 { 4, N_("multi-spot") }, | |
143 { 5, N_("multi-segment") }, | |
144 { 6, N_("partial") }, | |
145 { 255, N_("other") }, | |
146 EXIF_TEXT_LIST_END | |
147 }; | |
148 | |
149 static ExifTextList ExifExposureProgramList[] = { | |
150 { 0, N_("not defined") }, | |
151 { 1, N_("manual") }, | |
152 { 2, N_("normal") }, | |
153 { 3, N_("aperture") }, | |
154 { 4, N_("shutter") }, | |
155 { 5, N_("creative") }, | |
156 { 6, N_("action") }, | |
157 { 7, N_("portrait") }, | |
158 { 8, N_("landscape") }, | |
159 EXIF_TEXT_LIST_END | |
160 }; | |
161 | |
162 static ExifTextList ExifLightSourceList[] = { | |
163 { 0, N_("unknown") }, | |
164 { 1, N_("daylight") }, | |
165 { 2, N_("fluorescent") }, | |
166 { 3, N_("tungsten (incandescent)") }, | |
167 { 4, N_("flash") }, | |
168 { 9, "fine weather" }, | |
169 { 10, "cloudy weather" }, | |
170 { 11, "shade" }, | |
171 { 12, "daylight fluorescent" }, | |
172 { 13, "day white fluorescent" }, | |
173 { 14, "cool white fluorescent" }, | |
174 { 15, "while fluorescent" }, | |
175 { 17, "standard light A" }, | |
176 { 18, "standard light B" }, | |
177 { 19, "standard light C" }, | |
178 { 20, "D55" }, | |
179 { 21, "D65" }, | |
180 { 22, "D75" }, | |
181 { 23, "D50" }, | |
182 { 24, "ISO studio tungsten" }, | |
183 { 255, N_("other") }, | |
184 EXIF_TEXT_LIST_END | |
185 }; | |
186 | |
187 static ExifTextList ExifFlashList[] = { | |
188 { 0, N_("no") }, | |
189 { 1, N_("yes") }, | |
190 { 5, N_("yes, not detected by strobe") }, | |
191 { 7, N_("yes, detected by strobe") }, | |
192 EXIF_TEXT_LIST_END | |
193 }; | |
194 | |
195 static ExifTextList ExifColorSpaceList[] = { | |
196 { 1, "sRGB" }, | |
197 { 65535,"uncalibrated" }, | |
198 EXIF_TEXT_LIST_END | |
199 }; | |
200 | |
201 static ExifTextList ExifSensorList[] = { | |
202 { 1, "not defined" }, | |
203 { 2, "1 chip color area" }, | |
204 { 2, "2 chip color area" }, | |
205 { 4, "3 chip color area" }, | |
206 { 5, "color sequential area" }, | |
207 { 7, "trilinear" }, | |
208 { 8, "color sequential linear" }, | |
209 EXIF_TEXT_LIST_END | |
210 }; | |
211 | |
212 static ExifTextList ExifSourceList[] = { | |
213 { 3, "digital still camera" }, | |
214 EXIF_TEXT_LIST_END | |
215 }; | |
216 | |
217 static ExifTextList ExifSceneList[] = { | |
218 { 1, "direct photo" }, | |
219 EXIF_TEXT_LIST_END | |
220 }; | |
221 | |
222 static ExifTextList ExifCustRenderList[] = { | |
223 { 0, "normal" }, | |
224 { 1, "custom" }, | |
225 EXIF_TEXT_LIST_END | |
226 }; | |
227 | |
228 static ExifTextList ExifExposureModeList[] = { | |
229 { 0, "auto" }, | |
230 { 1, "manual" }, | |
231 { 2, "auto bracket" }, | |
232 EXIF_TEXT_LIST_END | |
233 }; | |
234 | |
235 static ExifTextList ExifWhiteBalanceList[] = { | |
236 { 0, "auto" }, | |
237 { 1, "manual" }, | |
238 EXIF_TEXT_LIST_END | |
239 }; | |
240 | |
241 static ExifTextList ExifSceneCaptureList[] = { | |
242 { 0, "standard" }, | |
243 { 1, "landscape" }, | |
244 { 2, "portrait" }, | |
245 { 3, "night scene" }, | |
246 EXIF_TEXT_LIST_END | |
247 }; | |
248 | |
249 static ExifTextList ExifGainControlList[] = { | |
250 { 0, "none" }, | |
251 { 1, "low gain up" }, | |
252 { 2, "high gain up" }, | |
253 { 3, "low gain down" }, | |
254 { 4, "high gain down" }, | |
255 EXIF_TEXT_LIST_END | |
256 }; | |
257 | |
258 static ExifTextList ExifContrastList[] = { | |
259 { 0, "normal" }, | |
260 { 1, "soft" }, | |
261 { 2, "hard" }, | |
262 EXIF_TEXT_LIST_END | |
263 }; | |
264 | |
265 static ExifTextList ExifSaturationList[] = { | |
266 { 0, "normal" }, | |
267 { 1, "low" }, | |
268 { 2, "high" }, | |
269 EXIF_TEXT_LIST_END | |
270 }; | |
271 | |
272 static ExifTextList ExifSharpnessList[] = { | |
273 { 0, "normal" }, | |
274 { 1, "soft" }, | |
275 { 2, "hard" }, | |
276 EXIF_TEXT_LIST_END | |
277 }; | |
278 | |
279 static ExifTextList ExifSubjectRangeList[] = { | |
280 { 0, "unknown" }, | |
281 { 1, "macro" }, | |
282 { 2, "close" }, | |
283 { 3, "distant" }, | |
284 EXIF_TEXT_LIST_END | |
285 }; | |
286 | |
287 ExifMarker ExifKnownMarkersList[] = { | |
288 { 0x010e, EXIF_FORMAT_STRING, -1, "ImageDescription", N_("Image description"), NULL }, | |
289 { 0x010f, EXIF_FORMAT_STRING, -1, "Make", "Camera make", NULL }, | |
290 { 0x0110, EXIF_FORMAT_STRING, -1, "Model", "Camera model", NULL }, | |
291 { 0x0112, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Orientation", N_("Orientation"), ExifOrientationList }, | |
292 { 0x011a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "XResolution", "X resolution", NULL }, | |
293 { 0x011b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "YResolution", "Y Resolution", NULL }, | |
294 { 0x0128, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ResolutionUnit", "Resolution units", ExifUnitList }, | |
295 { 0x0131, EXIF_FORMAT_STRING, -1, "Software", "Firmware", NULL }, | |
296 { 0x0132, EXIF_FORMAT_STRING, 20, "DateTime", N_("Date"), NULL }, | |
297 { 0x013e, EXIF_FORMAT_RATIONAL_UNSIGNED, 2, "WhitePoint", "White point", NULL }, | |
298 { 0x013f, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "PrimaryChromaticities","Primary chromaticities", NULL }, | |
299 { 0x0211, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "YCbCrCoefficients", "YCbCy coefficients", NULL }, | |
300 { 0x0213, EXIF_FORMAT_SHORT_UNSIGNED, 1, "YCbCrPositioning", "YCbCr positioning", ExifYCbCrPosList }, | |
301 { 0x0214, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "ReferenceBlackWhite", "Black white reference", NULL }, | |
302 { 0x8298, EXIF_FORMAT_STRING, -1, "Copyright", N_("Copyright"), NULL }, | |
303 { 0x8769, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifOffset", "SubIFD Exif offset", NULL }, | |
304 /* subIFD follows */ | |
305 { 0x829a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureTime", "Exposure time (seconds)", NULL }, | |
306 { 0x829d, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FNumber", "FNumber", NULL }, | |
307 { 0x8822, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram", N_("Exposure program"), ExifExposureProgramList }, | |
308 { 0x8824, EXIF_FORMAT_STRING, -1, "SpectralSensitivity", "Spectral Sensitivity", NULL }, | |
309 { 0x8827, EXIF_FORMAT_SHORT_UNSIGNED, -1, "ISOSpeedRatings", N_("ISO sensitivity"), NULL }, | |
310 { 0x8828, EXIF_FORMAT_UNDEFINED, -1, "OECF", "Optoelectric conversion factor", NULL }, | |
311 { 0x9000, EXIF_FORMAT_UNDEFINED, 4, "ExifVersion", "Exif version", NULL }, | |
312 { 0x9003, EXIF_FORMAT_STRING, 20, "DateTimeOriginal", N_("Date original"), NULL }, | |
313 { 0x9004, EXIF_FORMAT_STRING, 20, "DateTimeDigitized", N_("Date digitized"), NULL }, | |
314 { 0x9101, EXIF_FORMAT_UNDEFINED, -1, "ComponentsConfiguration","Pixel format", NULL }, | |
315 { 0x9102, EXIF_FORMAT_RATIONAL_UNSIGNED,1, "CompressedBitsPerPixel","Compression ratio", NULL }, | |
316 { 0x9201, EXIF_FORMAT_RATIONAL, 1, "ShutterSpeedValue", N_("Shutter speed"), NULL }, | |
317 { 0x9202, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ApertureValue", N_("Aperture"), NULL }, | |
318 { 0x9203, EXIF_FORMAT_RATIONAL, 1, "BrightnessValue", "Brightness", NULL }, | |
319 { 0x9204, EXIF_FORMAT_RATIONAL, 1, "ExposureBiasValue", N_("Exposure bias"), NULL }, | |
320 { 0x9205, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "MaxApertureValue", "Maximum aperture", NULL }, | |
321 { 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "SubjectDistance", N_("Subject distance"), NULL }, | |
322 { 0x9207, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MeteringMode", N_("Metering mode"), ExifMeteringModeList }, | |
323 { 0x9208, EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource", N_("Light source"), ExifLightSourceList }, | |
324 { 0x9209, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Flash", N_("Flash"), ExifFlashList }, | |
325 { 0x920a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalLength", N_("Focal length"), NULL }, | |
326 { 0x9214, EXIF_FORMAT_SHORT_UNSIGNED, -1, "SubjectArea", "Subject area", NULL }, | |
327 { 0x927c, EXIF_FORMAT_UNDEFINED, -1, "MakerNote", "MakerNote", NULL }, | |
328 { 0x9286, EXIF_FORMAT_UNDEFINED, -1, "UserComment", "UserComment", NULL }, | |
329 { 0x9290, EXIF_FORMAT_STRING, -1, "SubsecTime", "Subsecond time", NULL }, | |
330 { 0x9291, EXIF_FORMAT_STRING, -1, "SubsecTimeOriginal", "Subsecond time original", NULL }, | |
331 { 0x9292, EXIF_FORMAT_STRING, -1, "SubsecTimeDigitized", "Subsecond time digitized", NULL }, | |
332 { 0xa000, EXIF_FORMAT_UNDEFINED, 4, "FlashPixVersion", "FlashPix version", NULL }, | |
333 { 0xa001, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ColorSpace", "Colorspace", ExifColorSpaceList }, | |
334 /* ExifImageWidth, ExifImageHeight can also be unsigned short */ | |
335 { 0xa002, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifImageWidth", N_("Width"), NULL }, | |
336 { 0xa003, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifImageHeight", N_("Height"), NULL }, | |
337 { 0xa004, EXIF_FORMAT_STRING, -1, "RelatedSoundFile", "Audio data", NULL }, | |
338 { 0xa005, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifInteroperabilityOffset", "ExifR98 extension", NULL }, | |
339 { 0xa20b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FlashEnergy", "Flash strength", NULL }, | |
340 { 0xa20c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SpatialFrequencyResponse","Spatial frequency response", NULL }, | |
341 { 0xa20e, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalPlaneXResolution", "X Pixel density", NULL }, | |
342 { 0xa20f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalPlaneYResolution", "Y Pixel density", NULL }, | |
343 { 0xa210, EXIF_FORMAT_SHORT_UNSIGNED, 1, "FocalPlaneResolutionUnit", "Pixel density units", ExifUnitList }, | |
344 { 0x0214, EXIF_FORMAT_SHORT_UNSIGNED, 2, "SubjectLocation", "Subject location", NULL }, | |
345 { 0xa215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureIndex", N_("ISO sensitivity"), NULL }, | |
346 { 0xa217, EXIF_FORMAT_SHORT_UNSIGNED, -1, "SensingMethod", "Sensor type", ExifSensorList }, | |
347 { 0xa300, EXIF_FORMAT_UNDEFINED, 1, "FileSource", "Source type", ExifSourceList }, | |
348 { 0xa301, EXIF_FORMAT_UNDEFINED, 1, "SceneType", "Scene type", ExifSceneList }, | |
349 { 0xa302, EXIF_FORMAT_UNDEFINED, -1, "CFAPattern", "Color filter array pattern", NULL }, | |
350 /* tags a4xx were added for Exif 2.2 (not just these - some above, as well) */ | |
351 { 0xa401, EXIF_FORMAT_SHORT_UNSIGNED, 1, "CustomRendered", "Render process", ExifCustRenderList }, | |
352 { 0xa402, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode", "Exposure mode", ExifExposureModeList }, | |
353 { 0xa403, EXIF_FORMAT_SHORT_UNSIGNED, 1, "WhiteBalance", "White balance", ExifWhiteBalanceList }, | |
354 { 0xa404, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "DigitalZoomRatio", "Digital zoom ratio", NULL }, | |
355 { 0xa405, EXIF_FORMAT_SHORT_UNSIGNED, 1, "FocalLength35mmFilm", "Focal length (35mm)", NULL }, | |
356 { 0xa406, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SceneCapturetype", "Scene capture type", ExifSceneCaptureList }, | |
357 { 0xa407, EXIF_FORMAT_SHORT_UNSIGNED, 1, "GainControl", "Gain control", ExifGainControlList }, | |
358 { 0xa408, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Contrast", "Contrast", ExifContrastList }, | |
359 { 0xa409, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Saturation", "Saturation", ExifSaturationList }, | |
360 { 0xa40a, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Sharpness", "Sharpness", ExifSharpnessList }, | |
361 { 0xa40b, EXIF_FORMAT_UNDEFINED, -1, "DeviceSettingDescription","Device setting", NULL }, | |
362 { 0xa40c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubjectDistanceRange", "Subject range", ExifSubjectRangeList }, | |
363 { 0xa420, EXIF_FORMAT_STRING, -1, "ImageUniqueID", "Image serial number", NULL }, | |
364 /* place known, but undocumented or lesser used tags here */ | |
365 { 0x00fe, EXIF_FORMAT_LONG_UNSIGNED, 1, "NewSubfileType", NULL, NULL }, | |
366 { 0x00ff, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubfileType", NULL, NULL }, | |
367 { 0x012d, EXIF_FORMAT_SHORT_UNSIGNED, 3, "TransferFunction", NULL, NULL }, | |
368 { 0x013b, EXIF_FORMAT_STRING, -1, "Artist", "Artist", NULL }, | |
369 { 0x013d, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Predictor", NULL, NULL }, | |
370 { 0x0142, EXIF_FORMAT_SHORT_UNSIGNED, 1, "TileWidth", NULL, NULL }, | |
371 { 0x0143, EXIF_FORMAT_SHORT_UNSIGNED, 1, "TileLength", NULL, NULL }, | |
372 { 0x0144, EXIF_FORMAT_LONG_UNSIGNED, -1, "TileOffsets", NULL, NULL }, | |
373 { 0x0145, EXIF_FORMAT_SHORT_UNSIGNED, -1, "TileByteCounts", NULL, NULL }, | |
374 { 0x014a, EXIF_FORMAT_LONG_UNSIGNED, -1, "SubIFDs", NULL, NULL }, | |
375 { 0x015b, EXIF_FORMAT_UNDEFINED, -1, "JPEGTables", NULL, NULL }, | |
376 { 0x828d, EXIF_FORMAT_SHORT_UNSIGNED, 2, "CFARepeatPatternDim", NULL, NULL }, | |
377 { 0x828e, EXIF_FORMAT_BYTE_UNSIGNED, -1, "CFAPattern", NULL, NULL }, | |
378 { 0x828f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "BatteryLevel", NULL, NULL }, | |
379 { 0x83bb, EXIF_FORMAT_LONG_UNSIGNED, -1, "IPTC/NAA", NULL, NULL }, | |
380 { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "InterColorProfile", NULL, NULL }, | |
381 { 0x8825, EXIF_FORMAT_LONG_UNSIGNED, 1, "GPSInfo", "SubIFD GPS offset", NULL }, | |
382 { 0x8829, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Interlace", NULL, NULL }, | |
383 { 0x882a, EXIF_FORMAT_SHORT, 1, "TimeZoneOffset", NULL, NULL }, | |
384 { 0x882b, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SelfTimerMode", NULL, NULL }, | |
385 { 0x920b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FlashEnergy", NULL, NULL }, | |
386 { 0x920c, EXIF_FORMAT_UNDEFINED, -1, "SpatialFrequencyResponse", NULL, NULL }, | |
387 { 0x920d, EXIF_FORMAT_UNDEFINED, -1, "Noise", NULL, NULL }, | |
388 { 0x9211, EXIF_FORMAT_LONG_UNSIGNED, 1, "ImageNumber", NULL, NULL }, | |
389 { 0x9212, EXIF_FORMAT_STRING, 1, "SecurityClassification", NULL, NULL }, | |
390 { 0x9213, EXIF_FORMAT_STRING, -1, "ImageHistory", NULL, NULL }, | |
391 { 0x9215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureIndex", NULL, NULL }, | |
392 { 0x9216, EXIF_FORMAT_BYTE_UNSIGNED, 4, "TIFF/EPStandardID", NULL, NULL }, | |
393 | |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
394 EXIF_MARKER_LIST_END |
9 | 395 }; |
396 | |
397 ExifMarker ExifUnknownMarkersList[] = { | |
398 { 0x0000, EXIF_FORMAT_UNKNOWN, 0, "unknown", NULL, NULL }, | |
399 { 0x0000, EXIF_FORMAT_BYTE_UNSIGNED, -1, "unknown", NULL, NULL }, | |
400 { 0x0000, EXIF_FORMAT_STRING, -1, "unknown", NULL, NULL }, | |
401 { 0x0000, EXIF_FORMAT_SHORT_UNSIGNED, -1, "unknown", NULL, NULL }, | |
402 { 0x0000, EXIF_FORMAT_LONG_UNSIGNED, -1, "unknown", NULL, NULL }, | |
403 { 0x0000, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "unknown", NULL, NULL }, | |
404 { 0x0000, EXIF_FORMAT_BYTE, -1, "unknown", NULL, NULL }, | |
405 { 0x0000, EXIF_FORMAT_UNDEFINED, -1, "unknown", NULL, NULL }, | |
406 { 0x0000, EXIF_FORMAT_SHORT, -1, "unknown", NULL, NULL }, | |
407 { 0x0000, EXIF_FORMAT_LONG, -1, "unknown", NULL, NULL }, | |
408 { 0x0000, EXIF_FORMAT_RATIONAL, -1, "unknown", NULL, NULL }, | |
409 { 0x0000, EXIF_FORMAT_FLOAT, -1, "unknown", NULL, NULL }, | |
410 { 0x0000, EXIF_FORMAT_DOUBLE, -1, "unknown", NULL, NULL }, | |
411 }; | |
412 | |
413 /* human readable key list */ | |
414 | |
415 ExifFormattedText ExifFormattedList[] = { | |
416 { "fCamera", N_("Camera") }, | |
417 { "fDateTime", N_("Date") }, | |
418 { "fShutterSpeed", N_("Shutter speed") }, | |
419 { "fAperture", N_("Aperture") }, | |
420 { "fExposureBias", N_("Exposure bias") }, | |
421 { "fISOSpeedRating", N_("ISO sensitivity") }, | |
422 { "fFocalLength", N_("Focal length") }, | |
423 { "fSubjectDistance", N_("Subject distance") }, | |
424 { "fFlash", N_("Flash") }, | |
425 { "fResolution", N_("Resolution") }, | |
426 { NULL, NULL } | |
427 }; | |
428 | |
429 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
430 static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list); |
9 | 431 |
432 /* | |
433 *----------------------------------------------------------------------------- | |
434 * ExifItem | |
435 *----------------------------------------------------------------------------- | |
436 */ | |
437 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
438 ExifItem *exif_item_new(ExifFormatType format, guint tag, |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
439 guint elements, const ExifMarker *marker) |
9 | 440 { |
441 ExifItem *item; | |
442 | |
443 item = g_new0(ExifItem, 1); | |
444 item->format = format; | |
445 item->tag = tag; | |
446 item->marker = marker; | |
447 item->elements = elements; | |
448 item->data = NULL; | |
449 item->data_len = 0; | |
450 | |
451 switch (format) | |
452 { | |
453 case EXIF_FORMAT_UNKNOWN: | |
454 /* unknown, data is NULL */ | |
455 return item; | |
456 break; | |
457 case EXIF_FORMAT_BYTE_UNSIGNED: | |
458 item->data_len = sizeof(char) * elements; | |
459 break; | |
460 case EXIF_FORMAT_STRING: | |
461 item->data_len = sizeof(char) * elements; | |
462 break; | |
463 case EXIF_FORMAT_SHORT_UNSIGNED: | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
464 item->data_len = sizeof(guint16) * elements; |
9 | 465 break; |
466 case EXIF_FORMAT_LONG_UNSIGNED: | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
467 item->data_len = sizeof(guint32) * elements; |
9 | 468 break; |
469 case EXIF_FORMAT_RATIONAL_UNSIGNED: | |
470 item->data_len = sizeof(ExifRational) * elements; | |
471 break; | |
472 case EXIF_FORMAT_BYTE: | |
473 item->data_len = sizeof(char) * elements; | |
474 break; | |
475 case EXIF_FORMAT_UNDEFINED: | |
476 item->data_len = sizeof(char) * elements; | |
477 break; | |
478 case EXIF_FORMAT_SHORT: | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
479 item->data_len = sizeof(gint16) * elements; |
9 | 480 break; |
481 case EXIF_FORMAT_LONG: | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
482 item->data_len = sizeof(gint32) * elements; |
9 | 483 break; |
484 case EXIF_FORMAT_RATIONAL: | |
485 item->data_len = sizeof(ExifRational) * elements; | |
486 break; | |
487 case EXIF_FORMAT_FLOAT: | |
488 item->data_len = sizeof(float) * elements; | |
489 break; | |
490 case EXIF_FORMAT_DOUBLE: | |
491 item->data_len = sizeof(double) * elements; | |
492 break; | |
493 } | |
494 | |
495 item->data = g_malloc0(item->data_len); | |
496 | |
497 return item; | |
498 } | |
499 | |
500 static void exif_item_free(ExifItem *item) | |
501 { | |
502 if (!item) return; | |
503 | |
504 g_free(item->data); | |
505 g_free(item); | |
506 } | |
507 | |
508 const char *exif_item_get_tag_name(ExifItem *item) | |
509 { | |
510 if (!item || !item->marker) return NULL; | |
511 return item->marker->key; | |
512 } | |
513 | |
514 const char *exif_item_get_description(ExifItem *item) | |
515 { | |
516 if (!item || !item->marker) return NULL; | |
517 return _(item->marker->description); | |
518 } | |
519 | |
520 const char *exif_item_get_format_name(ExifItem *item, gint brief) | |
521 { | |
522 if (!item || !item->marker) return NULL; | |
523 return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description; | |
524 } | |
525 | |
526 | |
527 #define UNDEFINED_TEXT_BYTE_COUNT 16 | |
528 | |
529 static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne) | |
530 { | |
531 gint i; | |
532 | |
533 for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++) | |
534 { | |
535 unsigned char c = ((char *)data)[i]; | |
536 if (c < 32 || c > 127) c = '.'; | |
537 g_string_append_printf(string, "%c", c); | |
538 } | |
539 string = g_string_append(string, " : "); | |
540 for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++) | |
541 { | |
542 const gchar *spacer; | |
543 if (i > 0) | |
544 { | |
545 if (i%8 == 0) | |
546 { | |
547 spacer = " - "; | |
548 } | |
549 else | |
550 { | |
551 spacer = " "; | |
552 } | |
553 } | |
554 else | |
555 { | |
556 spacer = ""; | |
557 } | |
558 g_string_append_printf(string, "%s%02x", spacer, ((char *)data)[i]); | |
559 } | |
560 if (i >= UNDEFINED_TEXT_BYTE_COUNT) g_string_append_printf(string, " (%d bytes)", ne); | |
561 | |
562 return string; | |
563 } | |
564 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
565 static gchar *text_list_find_value(ExifTextList *list, guint value) |
9 | 566 { |
567 gchar *result = NULL; | |
568 gint i; | |
569 | |
570 i = 0; | |
571 while (!result && list[i].value >= 0) | |
572 { | |
573 if (value == list[i].value) result = g_strdup(_(list[i].description)); | |
574 i++; | |
575 } | |
576 if (!result) result = g_strdup_printf("%d (%s)", value, _("unknown")); | |
577 | |
578 return result; | |
579 } | |
580 | |
581 /* | |
582 *------------------------------------------------------------------- | |
583 * byte size utils | |
584 *------------------------------------------------------------------- | |
585 */ | |
586 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
587 guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo) |
9 | 588 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
589 if (bo == EXIF_BYTE_ORDER_INTEL) |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
590 return GUINT16_FROM_LE(*(guint16*)f); |
9 | 591 else |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
592 return GUINT16_FROM_BE(*(guint16*)f); |
9 | 593 } |
594 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
595 guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo) |
9 | 596 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
597 if (bo == EXIF_BYTE_ORDER_INTEL) |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
598 return GUINT32_FROM_LE(*(guint32*)f); |
9 | 599 else |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
600 return GUINT32_FROM_BE(*(guint32*)f); |
9 | 601 } |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
602 |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
603 guint16 exif_byte_swab_int16(guint16 n, ExifByteOrder bo) |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
604 { |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
605 #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
606 if (bo == EXIF_BYTE_ORDER_MOTOROLA) |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
607 #else |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
608 if (bo == EXIF_BYTE_ORDER_INTEL) |
9 | 609 #endif |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
610 return GUINT16_SWAP_LE_BE(n); |
9 | 611 else |
612 return n; | |
613 } | |
614 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
615 guint32 exif_byte_swab_int32(guint32 n, ExifByteOrder bo) |
9 | 616 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
617 #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
618 if (bo == EXIF_BYTE_ORDER_MOTOROLA) |
9 | 619 #else |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
620 if (bo == EXIF_BYTE_ORDER_INTEL) |
9 | 621 #endif |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
622 return GUINT32_SWAP_LE_BE(n); |
9 | 623 else |
624 return n; | |
625 } | |
626 | |
627 /* | |
628 *------------------------------------------------------------------- | |
629 * IFD utils | |
630 *------------------------------------------------------------------- | |
631 */ | |
632 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
633 static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list) |
9 | 634 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
635 gint i = 0; |
9 | 636 |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
637 if (!list) return NULL; |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
638 |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
639 while (list[i].tag != 0 && list[i].tag != tag) |
9 | 640 { |
641 i++; | |
642 } | |
643 | |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
644 return (list[i].tag == 0 ? NULL : &list[i]); |
9 | 645 } |
646 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
647 static void rational_from_data(ExifRational *r, void *src, ExifByteOrder bo) |
9 | 648 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
649 r->num = exif_byte_get_int32(src, bo); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
650 r->den = exif_byte_get_int32(src + sizeof(guint32), bo); |
9 | 651 } |
652 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
653 /* src_format and item->format must be compatible |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
654 * and not overrun src or item->data. |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
655 */ |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
656 void exif_item_copy_data(ExifItem *item, void *src, guint len, |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
657 ExifFormatType src_format, ExifByteOrder bo) |
9 | 658 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
659 gint bs; |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
660 gint ne; |
9 | 661 gpointer dest; |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
662 gint i; |
9 | 663 |
664 bs = ExifFormatList[item->format].size; | |
665 ne = item->elements; | |
666 dest = item->data; | |
667 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
668 if (!dest || |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
669 ExifFormatList[src_format].size * ne > len) |
9 | 670 { |
671 printf("exif tag %s data size mismatch\n", exif_item_get_tag_name(item)); | |
672 return; | |
673 } | |
674 | |
675 switch (item->format) | |
676 { | |
677 case EXIF_FORMAT_UNKNOWN: | |
678 break; | |
679 case EXIF_FORMAT_BYTE_UNSIGNED: | |
680 case EXIF_FORMAT_BYTE: | |
681 case EXIF_FORMAT_UNDEFINED: | |
682 memcpy(dest, src, len); | |
683 break; | |
684 case EXIF_FORMAT_STRING: | |
685 memcpy(dest, src, len); | |
686 /* string is NULL terminated, make sure this is true */ | |
687 if (((char *)dest)[len - 1] != '\0') ((char *)dest)[len - 1] = '\0'; | |
688 break; | |
689 case EXIF_FORMAT_SHORT_UNSIGNED: | |
690 case EXIF_FORMAT_SHORT: | |
691 for (i = 0; i < ne; i++) | |
692 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
693 ((guint16 *)dest)[i] = exif_byte_get_int16(src + i * bs, bo); |
9 | 694 } |
695 break; | |
696 case EXIF_FORMAT_LONG_UNSIGNED: | |
697 case EXIF_FORMAT_LONG: | |
698 if (src_format == EXIF_FORMAT_SHORT_UNSIGNED || | |
699 src_format == EXIF_FORMAT_SHORT) | |
700 { | |
701 /* a short fits into a long, so allow it */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
702 gint ss; |
9 | 703 |
704 ss = ExifFormatList[src_format].size; | |
705 for (i = 0; i < ne; i++) | |
706 { | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
707 ((gint32 *)dest)[i] = |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
708 (gint32)exif_byte_get_int16(src + i * ss, bo); |
9 | 709 } |
710 } | |
711 else | |
712 { | |
713 for (i = 0; i < ne; i++) | |
714 { | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
715 ((gint32 *)dest)[i] = |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
716 exif_byte_get_int32(src + i * bs, bo); |
9 | 717 } |
718 } | |
719 break; | |
720 case EXIF_FORMAT_RATIONAL_UNSIGNED: | |
721 case EXIF_FORMAT_RATIONAL: | |
722 for (i = 0; i < ne; i++) | |
723 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
724 rational_from_data(&((ExifRational *)dest)[i], src + i * bs, bo); |
9 | 725 } |
726 break; | |
727 case EXIF_FORMAT_FLOAT: | |
728 for (i = 0; i < ne; i++) | |
729 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
730 ((float *)dest)[i] = exif_byte_get_int32(src + i * bs, bo); |
9 | 731 } |
732 break; | |
733 case EXIF_FORMAT_DOUBLE: | |
734 for (i = 0; i < ne; i++) | |
735 { | |
736 ExifRational r; | |
737 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
738 rational_from_data(&r, src + i * bs, bo); |
9 | 739 if (r.den) ((double *)dest)[i] = (double)r.num / r.den; |
740 } | |
741 break; | |
742 } | |
743 } | |
744 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
745 static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offset, |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
746 guint size, ExifByteOrder bo, |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
747 gint level, |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
748 const ExifMarker *list) |
9 | 749 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
750 guint tag; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
751 guint format; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
752 guint count; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
753 guint data_val; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
754 guint data_offset; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
755 guint data_length; |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
756 const ExifMarker *marker; |
9 | 757 ExifItem *item; |
758 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
759 tag = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_TAG, bo); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
760 format = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_FORMAT, bo); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
761 count = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_COUNT, bo); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
762 data_val = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_DATA, bo); |
9 | 763 |
764 /* Check tag type. If it does not match, either the format is wrong, | |
765 * either it is a unknown tag; so it is not really an error. | |
766 */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
767 marker = exif_marker_from_tag(tag, list); |
9 | 768 if (!marker) |
769 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
770 if (format >= EXIF_FORMAT_COUNT) |
9 | 771 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
772 printf("warning: exif tag 0x%4x has invalid format %d\n", tag, format); |
9 | 773 return 0; |
774 } | |
775 /* allow non recognized tags to be displayed */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
776 marker = &ExifUnknownMarkersList[format]; |
9 | 777 } |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
778 if (marker->format != format) |
9 | 779 { |
780 /* Some cameras got mixed up signed/unsigned_rational | |
781 * eg KODAK DC4800 on object_distance tag | |
782 * | |
783 * FIXME: what exactly is this test trying to do? | |
784 * ok, so this test is to allow the case of swapped signed/unsigned mismatch to leak through? | |
785 */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
786 if (!(marker->format == EXIF_FORMAT_RATIONAL_UNSIGNED && format == EXIF_FORMAT_RATIONAL) && |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
787 !(marker->format == EXIF_FORMAT_RATIONAL && format == EXIF_FORMAT_RATIONAL_UNSIGNED) && |
9 | 788 /* short fits into a long so allow this mismatch |
789 * as well (some tags allowed to be unsigned short _or_ unsigned long) | |
790 */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
791 !(marker->format == EXIF_FORMAT_LONG_UNSIGNED && format == EXIF_FORMAT_SHORT_UNSIGNED) ) |
9 | 792 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
793 if (format < EXIF_FORMAT_COUNT) |
9 | 794 { |
795 printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n", | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
796 marker->key, ExifFormatList[format].short_name, |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
797 ExifFormatList[marker->format].short_name); |
9 | 798 } |
799 else | |
800 { | |
801 printf("warning: exif tag %s format mismatch, found unknown id %d exif spec requests %d (%s)\n", | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
802 marker->key, format, marker->format, |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
803 ExifFormatList[marker->format].short_name); |
9 | 804 } |
805 return 0; | |
806 } | |
807 } | |
808 | |
809 /* Where is the data, is it available? | |
810 */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
811 if (marker->components > 0 && marker->components != count) |
9 | 812 { |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
813 printf("warning: exif tag %s has %d elements, exif spec requests %d\n", |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
814 marker->key, count, marker->components); |
9 | 815 } |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
816 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
817 data_length = ExifFormatList[marker->format].size * count; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
818 if (data_length > 4) |
9 | 819 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
820 data_offset = data_val; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
821 if (size < data_offset + data_length) |
9 | 822 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
823 printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key); |
9 | 824 return -1; |
825 } | |
826 } | |
827 else | |
828 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
829 data_offset = offset + EXIF_TIFD_OFFSET_DATA; |
9 | 830 } |
831 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
832 item = exif_item_new(marker->format, tag, count, marker); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
833 exif_item_copy_data(item, tiff + data_offset, data_length, format, bo); |
9 | 834 exif->items = g_list_prepend(exif->items, item); |
835 | |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
836 if (list == ExifKnownMarkersList) |
9 | 837 { |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
838 switch (item->tag) |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
839 { |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
840 case TAG_EXIFOFFSET: |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
841 exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, list); |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
842 break; |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
843 case TAG_EXIFMAKERNOTE: |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
844 format_exif_makernote_parse(exif, tiff, data_val, size, bo); |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
845 break; |
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
846 } |
9 | 847 } |
848 | |
849 return 0; | |
850 } | |
851 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
852 gint exif_parse_IFD_table(ExifData *exif, |
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
853 unsigned char *tiff, guint offset, |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
854 guint size, ExifByteOrder bo, |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
855 gint level, |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
856 const ExifMarker *list) |
9 | 857 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
858 guint count; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
859 guint i; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
860 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
861 /* limit damage from infinite loops */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
862 if (level > EXIF_TIFF_MAX_LEVELS) return -1; |
9 | 863 |
864 /* We should be able to read number of entries in IFD0) */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
865 if (size < offset + 2) return -1; |
9 | 866 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
867 count = exif_byte_get_int16(tiff + offset, bo); |
9 | 868 |
869 /* Entries and next IFD offset must be readable */ | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
870 if (size < offset + count * EXIF_TIFD_SIZE + 4) return -1; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
871 offset += 2; |
9 | 872 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
873 for (i = 0; i < count; i++) |
9 | 874 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
875 exif_parse_IFD_entry(exif, tiff, offset + i * EXIF_TIFD_SIZE, size, bo, level, list); |
9 | 876 } |
877 | |
878 return 0; | |
879 } | |
880 | |
881 /* | |
882 *------------------------------------------------------------------- | |
883 * file formats | |
884 *------------------------------------------------------------------- | |
885 */ | |
886 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
887 gint exif_tiff_directory_offset(unsigned char *data, const guint len, |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
888 guint *offset, ExifByteOrder *bo) |
9 | 889 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
890 if (len < 8) return FALSE; |
9 | 891 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
892 if (memcmp(data, "II", 2) == 0) |
9 | 893 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
894 *bo = EXIF_BYTE_ORDER_INTEL; |
9 | 895 } |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
896 else if (memcmp(data, "MM", 2) == 0) |
9 | 897 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
898 *bo = EXIF_BYTE_ORDER_MOTOROLA; |
9 | 899 } |
900 else | |
901 { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
902 return FALSE; |
9 | 903 } |
904 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
905 if (exif_byte_get_int16(data + 2, *bo) != 0x002A) |
9 | 906 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
907 return FALSE; |
9 | 908 } |
909 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
910 *offset = exif_byte_get_int32(data + 4, *bo); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
911 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
912 return (*offset < len); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
913 } |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
914 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
915 gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list) |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
916 { |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
917 ExifByteOrder bo; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
918 guint offset; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
919 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
920 if (!exif_tiff_directory_offset(tiff, size, &offset, &bo)) return -1; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
921 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
922 return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
923 } |
9 | 924 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
925 /* |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
926 *------------------------------------------------------------------- |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
927 * jpeg marker utils |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
928 *------------------------------------------------------------------- |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
929 */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
930 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
931 #define MARKER_UNKNOWN 0x00 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
932 #define MARKER_SOI 0xD8 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
933 #define MARKER_APP1 0xE1 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
934 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
935 static gint jpeg_get_marker_size(unsigned char *data) |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
936 { |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
937 /* Size is always in Motorola byte order */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
938 return exif_byte_get_int16(data + 2, EXIF_BYTE_ORDER_MOTOROLA); |
9 | 939 } |
940 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
941 static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker) |
9 | 942 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
943 gint marker_size = 2; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
944 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
945 *marker = MARKER_UNKNOWN; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
946 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
947 /* It is safe to access the marker and its size since we have checked |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
948 * the SOI and this function guaranties the whole next marker is |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
949 * available |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
950 */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
951 if (*(*data + 1) != MARKER_SOI) |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
952 { |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
953 marker_size += jpeg_get_marker_size(*data); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
954 } |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
955 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
956 *size -= marker_size; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
957 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
958 /* size should be at least 4, so we can read the marker and its size |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
959 * and check data are actually available |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
960 */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
961 if (*size < 4) return -1; |
9 | 962 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
963 /* Jump to the next marker and be sure it begins with 0xFF |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
964 */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
965 *data += marker_size; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
966 if (**data != 0xFF) return -1; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
967 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
968 if (jpeg_get_marker_size(*data) + 2 > *size) return -1; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
969 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
970 *marker = *(*data + 1); |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
971 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
972 return 0; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
973 } |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
974 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
975 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
976 static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, guint size, ExifMarker *list) |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
977 { |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
978 guint marker; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
979 guint marker_size; |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
980 |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
981 if (size < 4 || *data != 0xFF || *(data + 1) != MARKER_SOI) |
9 | 982 { |
983 return -2; | |
984 } | |
985 | |
986 do { | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
987 if (jpeg_goto_next_marker(&data, &size, &marker) == -1) |
9 | 988 { |
989 break; | |
990 } | |
991 } while (marker != MARKER_APP1); | |
992 | |
993 if (marker != MARKER_APP1) | |
994 { | |
995 return -2; | |
996 } | |
997 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
998 marker_size = jpeg_get_marker_size(data) - 2; |
9 | 999 |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1000 if (marker_size < 6 || strncmp((char*)data + 4, "Exif\0\0", 6) != 0) |
9 | 1001 { |
1002 return -2; | |
1003 } | |
1004 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1005 return exif_tiff_parse(exif, data + 10, marker_size - 6, list); |
9 | 1006 } |
1007 | |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1008 /* |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1009 *------------------------------------------------------------------- |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1010 * misc |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1011 *------------------------------------------------------------------- |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1012 */ |
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1013 |
9 | 1014 static gint map_file(const gchar *path, void **mapping, int *size) |
1015 { | |
1016 int fd; | |
1017 struct stat fs; | |
1018 | |
1019 if ((fd = open(path, O_RDONLY)) == -1) | |
1020 { | |
1021 perror(path); | |
1022 return -1; | |
1023 } | |
1024 | |
1025 if (fstat(fd, &fs) == -1) | |
1026 { | |
1027 perror(path); | |
1028 close(fd); | |
1029 return -1; | |
1030 } | |
1031 | |
1032 *size = fs.st_size; | |
1033 | |
1034 if ((*mapping = mmap(0, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) | |
1035 { | |
1036 perror(path); | |
1037 close(fd); | |
1038 return -1; | |
1039 } | |
1040 | |
1041 close(fd); | |
1042 return 0; | |
1043 } | |
1044 | |
1045 static gint unmap_file(void *mapping, int size) | |
1046 { | |
1047 if (munmap(mapping, size) == -1) | |
1048 { | |
1049 perror("munmap"); | |
1050 return -1; | |
1051 } | |
1052 | |
1053 return 0; | |
1054 } | |
1055 | |
1056 void exif_free(ExifData *exif) | |
1057 { | |
1058 GList *work; | |
1059 | |
1060 if (!exif) return; | |
1061 | |
1062 work = exif->items; | |
1063 while (work) | |
1064 { | |
1065 ExifItem *item = work->data; | |
1066 work = work->next; | |
1067 exif_item_free(item); | |
1068 } | |
1069 | |
1070 g_list_free(exif->items); | |
1071 g_free(exif); | |
1072 } | |
1073 | |
1074 ExifData *exif_read(const gchar *path) | |
1075 { | |
1076 ExifData *exif; | |
1077 void *f; | |
1078 int size, res; | |
1079 gchar *pathl; | |
1080 | |
1081 if (!path) return NULL; | |
1082 | |
1083 pathl = path_from_utf8(path); | |
1084 if (map_file(pathl, &f, &size) == -1) | |
1085 { | |
1086 g_free(pathl); | |
1087 return NULL; | |
1088 } | |
1089 g_free(pathl); | |
1090 | |
1091 exif = g_new0(ExifData, 1); | |
1092 exif->items = NULL; | |
1093 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
1094 if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2) |
9 | 1095 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1096 res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList); |
9 | 1097 } |
1098 | |
1099 if (res != 0) | |
1100 { | |
43
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1101 guint32 offset = 0; |
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1102 |
45
7cfa60beda76
Thu May 26 13:57:19 2005 John Ellis <johne@verizon.net>
gqview
parents:
43
diff
changeset
|
1103 if (format_raw_img_exif_offsets(f, size, NULL, &offset)) |
43
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1104 { |
54
b58cac75ad12
Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
gqview
parents:
51
diff
changeset
|
1105 res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList); |
43
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1106 } |
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1107 } |
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1108 |
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1109 if (res != 0) |
ee03f36e9e4b
Sun May 15 21:40:26 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1110 { |
9 | 1111 exif_free(exif); |
1112 exif = NULL; | |
1113 } | |
1114 | |
1115 unmap_file(f, size); | |
1116 | |
1117 if (exif) exif->items = g_list_reverse(exif->items); | |
1118 | |
1119 #if 0 | |
1120 exif_write_data_list(exif, stdout, TRUE); | |
1121 exif_write_data_list(exif, stdout, FALSE); | |
1122 #endif | |
1123 | |
1124 return exif; | |
1125 } | |
1126 | |
1127 ExifItem *exif_get_item(ExifData *exif, const gchar *key) | |
1128 { | |
1129 GList *work; | |
1130 | |
1131 if (!key) return NULL; | |
1132 | |
1133 work = exif->items; | |
1134 while (work) | |
1135 { | |
1136 ExifItem *item; | |
1137 | |
1138 item = work->data; | |
1139 work = work->next; | |
1140 if (item->marker->key && strcmp(key, item->marker->key) == 0) return item; | |
1141 } | |
1142 return NULL; | |
1143 } | |
1144 | |
1145 gchar *exif_item_get_data_as_text(ExifItem *item) | |
1146 { | |
47
aa4c0e1b54b0
Fri Jun 3 01:49:20 2005 John Ellis <johne@verizon.net>
gqview
parents:
45
diff
changeset
|
1147 const ExifMarker *marker; |
9 | 1148 gpointer data; |
1149 GString *string; | |
1150 gchar *text; | |
1151 gint ne; | |
1152 gint i; | |
1153 | |
1154 if (!item) return NULL; | |
1155 | |
1156 marker = item->marker; | |
1157 if (!marker) return NULL; | |
1158 | |
1159 data = item->data; | |
1160 ne = item->elements; | |
1161 string = g_string_new(""); | |
1162 switch (item->format) | |
1163 { | |
1164 case EXIF_FORMAT_UNKNOWN: | |
1165 break; | |
1166 case EXIF_FORMAT_BYTE_UNSIGNED: | |
1167 case EXIF_FORMAT_BYTE: | |
1168 case EXIF_FORMAT_UNDEFINED: | |
1169 if (ne == 1 && marker->list) | |
1170 { | |
1171 gchar *result; | |
1172 unsigned char val; | |
1173 | |
1174 if (item->format == EXIF_FORMAT_BYTE_UNSIGNED || | |
1175 item->format == EXIF_FORMAT_UNDEFINED) | |
1176 { | |
1177 val = ((unsigned char *)data)[0]; | |
1178 } | |
1179 else | |
1180 { | |
1181 val = (unsigned char)(((signed char *)data)[0]); | |
1182 } | |
1183 | |
51
276ea4c98d33
Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
gqview
parents:
47
diff
changeset
|
1184 result = text_list_find_value(marker->list, (guint)val); |
9 | 1185 string = g_string_append(string, result); |
1186 g_free(result); | |
1187 } | |
1188 else | |
1189 { | |
1190 string = string_append_raw_bytes(string, data, ne); | |
1191 } | |
1192 break; | |
1193 case EXIF_FORMAT_STRING: | |
1194 string = g_string_append(string, (gchar *)(item->data)); | |
1195 break; | |
1196 case EXIF_FORMAT_SHORT_UNSIGNED: | |
1197 if (ne == 1 && marker->list) | |
1198 { | |
1199 gchar *result; | |
1200 | |
1201 result = text_list_find_value(marker->list, ((unsigned short *)data)[0]); | |
1202 string = g_string_append(string, result); | |
1203 g_free(result); | |
1204 } | |
1205 else for (i = 0; i < ne; i++) | |
1206 { | |
1207 g_string_append_printf(string, "%s%hd", (i > 0) ? ", " : "", | |
1208 ((unsigned short *)data)[i]); | |
1209 } | |
1210 break; | |
1211 case EXIF_FORMAT_LONG_UNSIGNED: | |
1212 for (i = 0; i < ne; i++) | |
1213 { | |
1214 g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", | |
1215 ((unsigned long *)data)[i]); | |
1216 } | |
1217 break; | |
1218 case EXIF_FORMAT_RATIONAL_UNSIGNED: | |
1219 for (i = 0; i < ne; i++) | |
1220 { | |
1221 ExifRational *r; | |
1222 | |
1223 r = &((ExifRational *)data)[i]; | |
1224 g_string_append_printf(string, "%s%ld/%ld", (i > 0) ? ", " : "", | |
1225 (unsigned long)r->num, (unsigned long)r->den); | |
1226 } | |
1227 break; | |
1228 case EXIF_FORMAT_SHORT: | |
1229 for (i = 0; i < ne; i++) | |
1230 { | |
1231 g_string_append_printf(string, "%s%hd", (i > 0) ? ", " : "", | |
1232 ((short *)data)[i]); | |
1233 } | |
1234 break; | |
1235 case EXIF_FORMAT_LONG: | |
1236 for (i = 0; i < ne; i++) | |
1237 { | |
1238 g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", | |
1239 ((long *)data)[i]); | |
1240 } | |
1241 break; | |
1242 case EXIF_FORMAT_RATIONAL: | |
1243 for (i = 0; i < ne; i++) | |
1244 { | |
1245 ExifRational *r; | |
1246 | |
1247 r = &((ExifRational *)data)[i]; | |
1248 g_string_append_printf(string, "%s%ld/%ld", (i > 0) ? ", " : "", | |
1249 (long)r->num, (long)r->den); | |
1250 } | |
1251 break; | |
1252 case EXIF_FORMAT_FLOAT: | |
1253 for (i = 0; i < ne; i++) | |
1254 { | |
1255 g_string_append_printf(string, "%s%f", (i > 0) ? ", " : "", | |
1256 ((float *)data)[i]); | |
1257 } | |
1258 break; | |
1259 case EXIF_FORMAT_DOUBLE: | |
1260 for (i = 0; i < ne; i++) | |
1261 { | |
1262 g_string_append_printf(string, "%s%f", (i > 0) ? ", " : "", | |
1263 ((double *)data)[i]); | |
1264 } | |
1265 break; | |
1266 } | |
1267 | |
1268 text = g_strdup(string->str); | |
1269 g_string_free(string, TRUE); | |
1270 | |
1271 return text; | |
1272 } | |
1273 | |
1274 gint exif_item_get_integer(ExifItem *item, gint *value) | |
1275 { | |
1276 if (!item) return FALSE; | |
1277 | |
1278 switch (item->format) | |
1279 { | |
1280 case EXIF_FORMAT_SHORT: | |
1281 *value = (gint)(((short *)(item->data))[0]); | |
1282 return TRUE; | |
1283 break; | |
1284 case EXIF_FORMAT_SHORT_UNSIGNED: | |
1285 *value = (gint)(((unsigned short *)(item->data))[0]); | |
1286 return TRUE; | |
1287 break; | |
1288 case EXIF_FORMAT_LONG: | |
1289 *value = (gint)(((long *)(item->data))[0]); | |
1290 return TRUE; | |
1291 break; | |
1292 case EXIF_FORMAT_LONG_UNSIGNED: | |
1293 /* FIXME: overflow possible */ | |
1294 *value = (gint)(((unsigned long *)(item->data))[0]); | |
1295 return TRUE; | |
1296 default: | |
1297 /* all other type return FALSE */ | |
1298 break; | |
1299 } | |
1300 return FALSE; | |
1301 } | |
1302 | |
1303 gint exif_get_integer(ExifData *exif, const gchar *key, gint *value) | |
1304 { | |
1305 ExifItem *item; | |
1306 | |
1307 item = exif_get_item(exif, key); | |
1308 return exif_item_get_integer(item, value); | |
1309 } | |
1310 | |
1311 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign) | |
1312 { | |
1313 if (!item) return NULL; | |
1314 | |
1315 if (item->format == EXIF_FORMAT_RATIONAL || | |
1316 item->format == EXIF_FORMAT_RATIONAL_UNSIGNED) | |
1317 { | |
1318 if (sign) *sign = (item->format == EXIF_FORMAT_RATIONAL); | |
1319 return &((ExifRational *)(item->data))[0]; | |
1320 } | |
1321 | |
1322 return NULL; | |
1323 } | |
1324 | |
1325 ExifRational *exif_get_rational(ExifData *exif, const gchar *key, gint *sign) | |
1326 { | |
1327 ExifItem *item; | |
1328 | |
1329 item = exif_get_item(exif, key); | |
1330 return exif_item_get_rational(item, sign); | |
1331 } | |
1332 | |
1333 double exif_rational_to_double(ExifRational *r, gint sign) | |
1334 { | |
1335 if (!r || r->den == 0.0) return 0.0; | |
1336 | |
1337 if (sign) return (double)((int)r->num) / (double)((int)r->den); | |
1338 return (double)r->num / r->den; | |
1339 } | |
1340 | |
1341 static double exif_get_rational_as_double(ExifData *exif, const gchar *key) | |
1342 { | |
1343 ExifRational *r; | |
1344 gint sign; | |
1345 | |
1346 r = exif_get_rational(exif, key, &sign); | |
1347 return exif_rational_to_double(r, sign); | |
1348 } | |
1349 | |
1350 static GString *append_comma_text(GString *string, const gchar *text) | |
1351 { | |
1352 string = g_string_append(string, ", "); | |
1353 string = g_string_append(string, text); | |
1354 | |
1355 return string; | |
1356 } | |
1357 | |
1358 static gchar *exif_get_formatted_by_key(ExifData *exif, const gchar *key, gint *key_valid) | |
1359 { | |
1360 /* must begin with f, else not formatted */ | |
1361 if (key[0] != 'f') | |
1362 { | |
1363 if (key_valid) *key_valid = FALSE; | |
1364 return NULL; | |
1365 } | |
1366 | |
1367 if (key_valid) *key_valid = TRUE; | |
1368 | |
1369 if (strcmp(key, "fCamera") == 0) | |
1370 { | |
1371 gchar *text; | |
1372 gchar *make = exif_get_data_as_text(exif, "Make"); | |
1373 gchar *model = exif_get_data_as_text(exif, "Model"); | |
1374 gchar *software = exif_get_data_as_text(exif, "Software"); | |
1375 | |
1376 text = g_strdup_printf("%s%s%s%s%s%s", (make) ? make : "", ((make) && (model)) ? " " : "", | |
1377 (model) ? model : "", | |
1378 (software) ? " (" : "", | |
1379 (software) ? software : "", | |
1380 (software) ? ")" : ""); | |
1381 | |
1382 g_free(make); | |
1383 g_free(model); | |
1384 g_free(software); | |
1385 return text; | |
1386 } | |
1387 if (strcmp(key, "fDateTime") == 0) | |
1388 { | |
1389 gchar *text = exif_get_data_as_text(exif, "DateTimeOriginal"); | |
1390 gchar *subsec = NULL; | |
1391 if (text) subsec = exif_get_data_as_text(exif, "SubsecTimeOriginal"); | |
1392 if (!text) | |
1393 { | |
1394 text = exif_get_data_as_text(exif, "DateTime"); | |
1395 if (text) subsec = exif_get_data_as_text(exif, "SubsecTime"); | |
1396 } | |
1397 if (subsec) | |
1398 { | |
1399 gchar *tmp = text; | |
1400 text = g_strconcat(tmp, ".", subsec, NULL); | |
1401 g_free(tmp); | |
1402 g_free(subsec); | |
1403 } | |
1404 return text; | |
1405 } | |
1406 if (strcmp(key, "fShutterSpeed") == 0) | |
1407 { | |
1408 ExifRational *r; | |
1409 | |
1410 r = exif_get_rational(exif, "ExposureTime", NULL); | |
1411 if (r && r->num && r->den) | |
1412 { | |
1413 double n = (double)r->den / (double)r->num; | |
1414 return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "", | |
1415 n > 1.0 ? n : 1.0 / n); | |
1416 } | |
1417 r = exif_get_rational(exif, "ShutterSpeedValue", NULL); | |
1418 if (r && r->num && r->den) | |
1419 { | |
1420 double n = pow(2.0, exif_rational_to_double(r, TRUE)); | |
1421 | |
1422 /* Correct exposure time to avoid values like 1/91s (seen on Minolta DImage 7) */ | |
1423 if (n > 1.0 && (int)n - ((int)(n/10))*10 == 1) n--; | |
1424 | |
1425 return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "", | |
1426 n > 1.0 ? floor(n) : 1.0 / n); | |
1427 } | |
1428 return NULL; | |
1429 } | |
1430 if (strcmp(key, "fAperture") == 0) | |
1431 { | |
1432 double n; | |
1433 | |
1434 n = exif_get_rational_as_double(exif, "FNumber"); | |
1435 if (n == 0.0) n = exif_get_rational_as_double(exif, "ApertureValue"); | |
1436 if (n == 0.0) return NULL; | |
1437 | |
1438 return g_strdup_printf("f/%.1f", n); | |
1439 } | |
1440 if (strcmp(key, "fExposureBias") == 0) | |
1441 { | |
1442 ExifRational *r; | |
1443 gint sign; | |
1444 double n; | |
1445 | |
1446 r = exif_get_rational(exif, "ExposureBiasValue", &sign); | |
1447 if (!r) return NULL; | |
1448 | |
1449 n = exif_rational_to_double(r, sign); | |
1450 return g_strdup_printf("%+.1f", n); | |
1451 } | |
1452 if (strcmp(key, "fFocalLength") == 0) | |
1453 { | |
1454 double n; | |
1455 | |
1456 n = exif_get_rational_as_double(exif, "FocalLength"); | |
1457 if (n == 0.0) return NULL; | |
1458 return g_strdup_printf("%.2f mm", n); | |
1459 } | |
1460 if (strcmp(key, "fISOSpeedRating") == 0) | |
1461 { | |
1462 gchar *text; | |
1463 | |
1464 text = exif_get_data_as_text(exif, "ISOSpeedRatings"); | |
1465 /* kodak may set this instead */ | |
1466 if (!text) text = exif_get_data_as_text(exif, "ExposureIndex"); | |
1467 return text; | |
1468 } | |
1469 if (strcmp(key, "fSubjectDistance") == 0) | |
1470 { | |
1471 ExifRational *r; | |
1472 gint sign; | |
1473 double n; | |
1474 | |
1475 r = exif_get_rational(exif, "SubjectDistance", &sign); | |
1476 if (!r) return NULL; | |
1477 | |
1478 if ((long)r->num == 0xffffffff) return g_strdup(_("infinity")); | |
1479 if ((long)r->num == 0) return g_strdup(_("unknown")); | |
1480 | |
1481 n = exif_rational_to_double(r, sign); | |
1482 if (n == 0.0) return _("unknown"); | |
1483 return g_strdup_printf("%.3f m", n); | |
1484 } | |
1485 if (strcmp(key, "fFlash") == 0) | |
1486 { | |
1487 /* grr, flash is a bitmask... */ | |
1488 GString *string; | |
1489 gchar *text; | |
1490 gint n; | |
1491 gint v; | |
1492 | |
1493 if (!exif_get_integer(exif, "Flash", &n)) return NULL; | |
1494 | |
1495 /* Exif 2.1 only defines first 3 bits */ | |
1496 if (n <= 0x07) return g_strdup(text_list_find_value(ExifFlashList, n)); | |
1497 | |
1498 /* must be Exif 2.2 */ | |
1499 string = g_string_new(""); | |
1500 | |
1501 /* flash fired (bit 0) */ | |
1502 string = g_string_append(string, (n & 0x01) ? _("yes") : _("no")); | |
1503 | |
1504 /* flash mode (bits 3, 4) */ | |
1505 v = (n >> 3) & 0x03; | |
1506 if (v) string = append_comma_text(string, _("mode:")); | |
1507 switch (v) | |
1508 { | |
1509 case 1: | |
1510 string = g_string_append(string, _("on")); | |
1511 break; | |
1512 case 2: | |
1513 string = g_string_append(string, _("off")); | |
1514 break; | |
1515 case 3: | |
1516 string = g_string_append(string, _("auto")); | |
1517 break; | |
1518 } | |
1519 | |
1520 /* return light (bits 1, 2) */ | |
1521 v = (n >> 1) & 0x03; | |
1522 if (v == 2) string = append_comma_text(string, _("not detected by strobe")); | |
1523 if (v == 3) string = append_comma_text(string, _("detected by strobe")); | |
1524 | |
1525 /* we ignore flash function (bit 5) */ | |
1526 | |
1527 /* red-eye (bit 6) */ | |
1528 if ((n >> 5) & 0x01) string = append_comma_text(string, _("red-eye reduction")); | |
1529 | |
1530 text = string->str; | |
1531 g_string_free(string, FALSE); | |
1532 return text; | |
1533 } | |
1534 if (strcmp(key, "fResolution") == 0) | |
1535 { | |
1536 ExifRational *rx, *ry; | |
1537 gchar *units; | |
1538 gchar *text; | |
1539 | |
1540 rx = exif_get_rational(exif, "XResolution", NULL); | |
1541 ry = exif_get_rational(exif, "YResolution", NULL); | |
1542 if (!rx || !ry) return NULL; | |
1543 | |
1544 units = exif_get_data_as_text(exif, "ResolutionUnit"); | |
1545 text = g_strdup_printf("%0.f x %0.f (%s/%s)", rx->den ? (double)rx->num / rx->den : 1.0, | |
1546 ry->den ? (double)ry->num / ry->den : 1.0, | |
1547 _("dot"), (units) ? units : _("unknown")); | |
1548 | |
1549 g_free(units); | |
1550 return text; | |
1551 } | |
1552 | |
1553 if (key_valid) *key_valid = FALSE; | |
1554 return NULL; | |
1555 } | |
1556 | |
1557 gchar *exif_get_data_as_text(ExifData *exif, const gchar *key) | |
1558 { | |
1559 ExifItem *item; | |
1560 gchar *text; | |
1561 gint key_valid; | |
1562 | |
1563 if (!key) return NULL; | |
1564 | |
1565 text = exif_get_formatted_by_key(exif, key, &key_valid); | |
1566 if (key_valid) return text; | |
1567 | |
1568 item = exif_get_item(exif, key); | |
1569 if (item) return exif_item_get_data_as_text(item); | |
1570 | |
1571 return NULL; | |
1572 } | |
1573 | |
1574 const gchar *exif_get_description_by_key(const gchar *key) | |
1575 { | |
1576 gint i; | |
1577 | |
1578 if (!key) return NULL; | |
1579 | |
1580 i = 0; | |
1581 while (ExifFormattedList[i].key != NULL) | |
1582 { | |
1583 if (strcmp(key, ExifFormattedList[i].key) == 0) return _(ExifFormattedList[i].description); | |
1584 i++; | |
1585 } | |
1586 | |
1587 i = 0; | |
1588 while (ExifKnownMarkersList[i].tag > 0) | |
1589 { | |
1590 if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return _(ExifKnownMarkersList[i].description); | |
1591 i++; | |
1592 } | |
1593 | |
1594 return NULL; | |
1595 } | |
1596 | |
1597 static void exif_write_item(FILE *f, ExifItem *item) | |
1598 { | |
1599 gchar *text; | |
1600 | |
1601 text = exif_item_get_data_as_text(item); | |
1602 if (text) | |
1603 { | |
1604 fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name, | |
1605 exif_item_get_tag_name(item), text); | |
1606 } | |
1607 g_free(text); | |
1608 } | |
1609 | |
1610 void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) | |
1611 { | |
1612 if (!f || !exif) return; | |
1613 | |
1614 fprintf(f, " tag format key value\n"); | |
1615 fprintf(f, "----------------------------------------------------\n"); | |
1616 | |
1617 if (human_readable_list) | |
1618 { | |
1619 gint i; | |
1620 | |
1621 i = 0; | |
1622 while (ExifFormattedList[i].key) | |
1623 { | |
1624 gchar *text; | |
1625 | |
1626 text = exif_get_formatted_by_key(exif, ExifFormattedList[i].key, NULL); | |
1627 if (text) | |
1628 { | |
1629 fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text); | |
1630 } | |
1631 i++; | |
1632 } | |
1633 } | |
1634 else | |
1635 { | |
1636 GList *work; | |
1637 | |
1638 work = exif->items; | |
1639 while (work) | |
1640 { | |
1641 ExifItem *item; | |
1642 | |
1643 item = work->data; | |
1644 work = work->next; | |
1645 | |
1646 exif_write_item(f, item); | |
1647 } | |
1648 } | |
1649 fprintf(f, "----------------------------------------------------\n"); | |
1650 } | |
1651 |