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