X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=blobdiff_plain;f=src%2Fexif.c;h=872b9ccd65e54979ccc108845eb952b1af2975e1;hp=267c2b9af20d060a5f2f20e6f1ca680be56f19f7;hb=50f5ef3cedbeaa251a1e838bf5045a9dc7dda1ee;hpb=a8b46cebff20f05d785a4d05854b2ec8c68ac3c2 diff --git a/src/exif.c b/src/exif.c index 267c2b9a..872b9ccd 100644 --- a/src/exif.c +++ b/src/exif.c @@ -1,12 +1,22 @@ /* - * GQView - * (C) 2004 John Ellis + * Copyright (C) 2003, 2006 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team * - * Authors: - * Support for Exif file format, originally written by Eric Swalens. - * Modified by Quy Tonthat + * Authors: Eric Swalens, Quy Tonthat * - * Reimplemented with generic data storage by John Ellis (Nov 2003) + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * The tags were added with information from the FREE document: * http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html @@ -27,33 +37,20 @@ * Unsupported at this time: * IFD1 (thumbnail) * MakerNote - * GPSInfo * * TODO: * Convert data to useable form in the ??_as_text function for: * ComponentsConfiguration * UserComment (convert this to UTF-8?) - * - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * Add support for marker tag 0x0000 + */ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#ifndef HAVE_EXIV2 + #include #include #include @@ -62,12 +59,15 @@ #include #include #include - + #include +#include #include "intl.h" -#include "exif.h" +#include "main.h" +#include "exif-int.h" +#include "jpeg_parser.h" #include "format_raw.h" #include "ui_fileops.h" @@ -93,12 +93,13 @@ ExifFormatAttrib ExifFormatList[] = { { EXIF_FORMAT_RATIONAL, 8, "srational", "signed rational" }, { EXIF_FORMAT_FLOAT, 4, "float", "float" }, { EXIF_FORMAT_DOUBLE, 8, "double", "double" }, - { -1, 0, NULL } + { -1, 0, NULL, NULL } }; /* tags that are special, or need special treatment */ #define TAG_EXIFOFFSET 0x8769 #define TAG_EXIFMAKERNOTE 0x927c +#define TAG_GPSOFFSET 0x8825 /* @@ -106,6 +107,36 @@ ExifFormatAttrib ExifFormatList[] = { * Data *----------------------------------------------------------------------------- */ +static ExifTextList ExifCompressionList[] = { + { 1, "Uncompressed" }, + { 2, "CCITT 1D" }, + { 3, "T4/Group 3 Fax" }, + { 4, "T6/Group 4 Fax" }, + { 5, "LZW" }, + { 6, "JPEG (old style)" }, + { 7, "JPEG" }, + { 8, "Adobe Deflate" }, + { 9, "JBIG B&W" }, + { 10, "JBIG Color" }, + { 32766, "Next" }, + { 32771, "CCIRLEW" }, + { 32773, "PackBits" }, + { 32809, "ThunderScan" }, + { 32895, "IT8CTPAD" }, + { 32896, "IT8LW" }, + { 32897, "IT8MP" }, + { 32898, "IT8BL" }, + { 32908, "PixasFilm" }, + { 32909, "PixasLog" }, + { 32946, "Deflate" }, + { 32947, "DCS" }, + { 34661, "JBIG" }, + { 34676, "SGILog" }, + { 34677, "SGILog24" }, + { 34712, "JPEF 2000" }, + { 34713, "Nikon NEF Compressed" }, + EXIF_TEXT_LIST_END +}; static ExifTextList ExifOrientationList[] = { { EXIF_ORIENTATION_UNKNOWN, N_("unknown") }, @@ -165,21 +196,21 @@ static ExifTextList ExifLightSourceList[] = { { 2, N_("fluorescent") }, { 3, N_("tungsten (incandescent)") }, { 4, N_("flash") }, - { 9, "fine weather" }, - { 10, "cloudy weather" }, - { 11, "shade" }, - { 12, "daylight fluorescent" }, - { 13, "day white fluorescent" }, - { 14, "cool white fluorescent" }, - { 15, "while fluorescent" }, - { 17, "standard light A" }, - { 18, "standard light B" }, - { 19, "standard light C" }, - { 20, "D55" }, - { 21, "D65" }, - { 22, "D75" }, - { 23, "D50" }, - { 24, "ISO studio tungsten" }, + { 9, N_("fine weather") }, + { 10, N_("cloudy weather") }, + { 11, N_("shade") }, + { 12, N_("daylight fluorescent") }, + { 13, N_("day white fluorescent") }, + { 14, N_("cool white fluorescent") }, + { 15, N_("white fluorescent") }, + { 17, N_("standard light A") }, + { 18, N_("standard light B") }, + { 19, N_("standard light C") }, + { 20, N_("D55") }, + { 21, N_("D65") }, + { 22, N_("D75") }, + { 23, N_("D50") }, + { 24, N_("ISO studio tungsten") }, { 255, N_("other") }, EXIF_TEXT_LIST_END }; @@ -193,207 +224,254 @@ static ExifTextList ExifFlashList[] = { }; static ExifTextList ExifColorSpaceList[] = { - { 1, "sRGB" }, - { 65535,"uncalibrated" }, + { 1, N_("sRGB") }, + { 65535,N_("uncalibrated") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSensorList[] = { - { 1, "not defined" }, - { 2, "1 chip color area" }, - { 2, "2 chip color area" }, - { 4, "3 chip color area" }, - { 5, "color sequential area" }, - { 7, "trilinear" }, - { 8, "color sequential linear" }, + { 1, N_("not defined") }, + { 2, N_("1 chip color area") }, + { 2, N_("2 chip color area") }, + { 4, N_("3 chip color area") }, + { 5, N_("color sequential area") }, + { 7, N_("trilinear") }, + { 8, N_("color sequential linear") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSourceList[] = { - { 3, "digital still camera" }, + { 3, N_("digital still camera") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSceneList[] = { - { 1, "direct photo" }, + { 1, N_("direct photo") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifCustRenderList[] = { - { 0, "normal" }, - { 1, "custom" }, + { 0, N_("normal") }, + { 1, N_("custom") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifExposureModeList[] = { - { 0, "auto" }, - { 1, "manual" }, - { 2, "auto bracket" }, + { 0, N_("auto") }, + { 1, N_("manual") }, + { 2, N_("auto bracket") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifWhiteBalanceList[] = { - { 0, "auto" }, - { 1, "manual" }, + { 0, N_("auto") }, + { 1, N_("manual") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSceneCaptureList[] = { - { 0, "standard" }, - { 1, "landscape" }, - { 2, "portrait" }, - { 3, "night scene" }, + { 0, N_("standard") }, + { 1, N_("landscape") }, + { 2, N_("portrait") }, + { 3, N_("night scene") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifGainControlList[] = { - { 0, "none" }, - { 1, "low gain up" }, - { 2, "high gain up" }, - { 3, "low gain down" }, - { 4, "high gain down" }, + { 0, N_("none") }, + { 1, N_("low gain up") }, + { 2, N_("high gain up") }, + { 3, N_("low gain down") }, + { 4, N_("high gain down") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifContrastList[] = { - { 0, "normal" }, - { 1, "soft" }, - { 2, "hard" }, + { 0, N_("normal") }, + { 1, N_("soft") }, + { 2, N_("hard") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSaturationList[] = { - { 0, "normal" }, - { 1, "low" }, - { 2, "high" }, + { 0, N_("normal") }, + { 1, N_("low") }, + { 2, N_("high") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSharpnessList[] = { - { 0, "normal" }, - { 1, "soft" }, - { 2, "hard" }, + { 0, N_("normal") }, + { 1, N_("soft") }, + { 2, N_("hard") }, EXIF_TEXT_LIST_END }; static ExifTextList ExifSubjectRangeList[] = { - { 0, "unknown" }, - { 1, "macro" }, - { 2, "close" }, - { 3, "distant" }, + { 0, N_("unknown") }, + { 1, N_("macro") }, + { 2, N_("close") }, + { 3, N_("distant") }, EXIF_TEXT_LIST_END }; +/* +Tag names should match to exiv2 keys, http://www.exiv2.org/metadata.html +Tags that don't match are not supported by exiv2 and should not be used anywhere in the code +*/ + ExifMarker ExifKnownMarkersList[] = { -{ 0x010e, EXIF_FORMAT_STRING, -1, "ImageDescription", N_("Image description"), NULL }, -{ 0x010f, EXIF_FORMAT_STRING, -1, "Make", "Camera make", NULL }, -{ 0x0110, EXIF_FORMAT_STRING, -1, "Model", "Camera model", NULL }, -{ 0x0112, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Orientation", N_("Orientation"), ExifOrientationList }, -{ 0x011a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "XResolution", "X resolution", NULL }, -{ 0x011b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "YResolution", "Y Resolution", NULL }, -{ 0x0128, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ResolutionUnit", "Resolution units", ExifUnitList }, -{ 0x0131, EXIF_FORMAT_STRING, -1, "Software", "Firmware", NULL }, -{ 0x0132, EXIF_FORMAT_STRING, 20, "DateTime", N_("Date"), NULL }, -{ 0x013e, EXIF_FORMAT_RATIONAL_UNSIGNED, 2, "WhitePoint", "White point", NULL }, -{ 0x013f, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "PrimaryChromaticities","Primary chromaticities", NULL }, -{ 0x0211, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "YCbCrCoefficients", "YCbCy coefficients", NULL }, -{ 0x0213, EXIF_FORMAT_SHORT_UNSIGNED, 1, "YCbCrPositioning", "YCbCr positioning", ExifYCbCrPosList }, -{ 0x0214, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "ReferenceBlackWhite", "Black white reference", NULL }, -{ 0x8298, EXIF_FORMAT_STRING, -1, "Copyright", N_("Copyright"), NULL }, -{ 0x8769, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifOffset", "SubIFD Exif offset", NULL }, +{ 0x0100, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Image.ImageWidth", N_("Image Width"), NULL }, +{ 0x0101, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Image.ImageLength", N_("Image Height"), NULL }, +{ 0x0102, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.BitsPerSample", N_("Bits per Sample/Pixel"), NULL }, +{ 0x0103, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.Compression", N_("Compression"), ExifCompressionList }, +{ 0x010e, EXIF_FORMAT_STRING, -1, "Exif.Image.ImageDescription", N_("Image description"), NULL }, +{ 0x010f, EXIF_FORMAT_STRING, -1, "Exif.Image.Make", N_("Camera make"), NULL }, +{ 0x0110, EXIF_FORMAT_STRING, -1, "Exif.Image.Model", N_("Camera model"), NULL }, +{ 0x0112, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.Orientation", N_("Orientation"), ExifOrientationList }, +{ 0x011a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Image.XResolution", N_("X resolution"), NULL }, +{ 0x011b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Image.YResolution", N_("Y Resolution"), NULL }, +{ 0x0128, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.ResolutionUnit", N_("Resolution units"), ExifUnitList }, +{ 0x0131, EXIF_FORMAT_STRING, -1, "Exif.Image.Software", N_("Firmware"), NULL }, +{ 0x0132, EXIF_FORMAT_STRING, 20, "Exif.Image.DateTime", N_("Date"), NULL }, +{ 0x013e, EXIF_FORMAT_RATIONAL_UNSIGNED, 2, "Exif.Image.WhitePoint", N_("White point"), NULL }, +{ 0x013f, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "Exif.Image.PrimaryChromaticities",N_("Primary chromaticities"), NULL }, +{ 0x0211, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.Image.YCbCrCoefficients", N_("YCbCy coefficients"), NULL }, +{ 0x0213, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.YCbCrPositioning", N_("YCbCr positioning"), ExifYCbCrPosList }, +{ 0x0214, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "Exif.Image.ReferenceBlackWhite",N_("Black white reference"), NULL }, +{ 0x8298, EXIF_FORMAT_STRING, -1, "Exif.Image.Copyright", N_("Copyright"), NULL }, +{ 0x8769, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Image.ExifTag", N_("SubIFD Exif offset"), NULL }, /* subIFD follows */ -{ 0x829a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureTime", "Exposure time (seconds)", NULL }, -{ 0x829d, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FNumber", "FNumber", NULL }, -{ 0x8822, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram", N_("Exposure program"), ExifExposureProgramList }, -{ 0x8824, EXIF_FORMAT_STRING, -1, "SpectralSensitivity", "Spectral Sensitivity", NULL }, -{ 0x8827, EXIF_FORMAT_SHORT_UNSIGNED, -1, "ISOSpeedRatings", N_("ISO sensitivity"), NULL }, -{ 0x8828, EXIF_FORMAT_UNDEFINED, -1, "OECF", "Optoelectric conversion factor", NULL }, -{ 0x9000, EXIF_FORMAT_UNDEFINED, 4, "ExifVersion", "Exif version", NULL }, -{ 0x9003, EXIF_FORMAT_STRING, 20, "DateTimeOriginal", N_("Date original"), NULL }, -{ 0x9004, EXIF_FORMAT_STRING, 20, "DateTimeDigitized", N_("Date digitized"), NULL }, -{ 0x9101, EXIF_FORMAT_UNDEFINED, -1, "ComponentsConfiguration","Pixel format", NULL }, -{ 0x9102, EXIF_FORMAT_RATIONAL_UNSIGNED,1, "CompressedBitsPerPixel","Compression ratio", NULL }, -{ 0x9201, EXIF_FORMAT_RATIONAL, 1, "ShutterSpeedValue", N_("Shutter speed"), NULL }, -{ 0x9202, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ApertureValue", N_("Aperture"), NULL }, -{ 0x9203, EXIF_FORMAT_RATIONAL, 1, "BrightnessValue", "Brightness", NULL }, -{ 0x9204, EXIF_FORMAT_RATIONAL, 1, "ExposureBiasValue", N_("Exposure bias"), NULL }, -{ 0x9205, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "MaxApertureValue", "Maximum aperture", NULL }, -{ 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "SubjectDistance", N_("Subject distance"), NULL }, -{ 0x9207, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MeteringMode", N_("Metering mode"), ExifMeteringModeList }, -{ 0x9208, EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource", N_("Light source"), ExifLightSourceList }, -{ 0x9209, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Flash", N_("Flash"), ExifFlashList }, -{ 0x920a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalLength", N_("Focal length"), NULL }, -{ 0x9214, EXIF_FORMAT_SHORT_UNSIGNED, -1, "SubjectArea", "Subject area", NULL }, -{ 0x927c, EXIF_FORMAT_UNDEFINED, -1, "MakerNote", "MakerNote", NULL }, -{ 0x9286, EXIF_FORMAT_UNDEFINED, -1, "UserComment", "UserComment", NULL }, -{ 0x9290, EXIF_FORMAT_STRING, -1, "SubsecTime", "Subsecond time", NULL }, -{ 0x9291, EXIF_FORMAT_STRING, -1, "SubsecTimeOriginal", "Subsecond time original", NULL }, -{ 0x9292, EXIF_FORMAT_STRING, -1, "SubsecTimeDigitized", "Subsecond time digitized", NULL }, -{ 0xa000, EXIF_FORMAT_UNDEFINED, 4, "FlashPixVersion", "FlashPix version", NULL }, -{ 0xa001, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ColorSpace", "Colorspace", ExifColorSpaceList }, +{ 0x829a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.ExposureTime", N_("Exposure time (seconds)"), NULL }, +{ 0x829d, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FNumber", N_("FNumber"), NULL }, +{ 0x8822, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.ExposureProgram", N_("Exposure program"), ExifExposureProgramList }, +{ 0x8824, EXIF_FORMAT_STRING, -1, "Exif.Photo.SpectralSensitivity",N_("Spectral Sensitivity"), NULL }, +{ 0x8827, EXIF_FORMAT_SHORT_UNSIGNED, -1, "Exif.Photo.ISOSpeedRatings", N_("ISO sensitivity"), NULL }, +{ 0x8828, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.OECF", N_("Optoelectric conversion factor"), NULL }, +{ 0x9000, EXIF_FORMAT_UNDEFINED, 4, "Exif.Photo.ExifVersion", N_("Exif version"), NULL }, +{ 0x9003, EXIF_FORMAT_STRING, 20, "Exif.Photo.DateTimeOriginal", N_("Date original"), NULL }, +{ 0x9004, EXIF_FORMAT_STRING, 20, "Exif.Photo.DateTimeDigitized", N_("Date digitized"), NULL }, +{ 0x9101, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.ComponentsConfiguration",N_("Pixel format"), NULL }, +{ 0x9102, EXIF_FORMAT_RATIONAL_UNSIGNED,1, "Exif.Photo.CompressedBitsPerPixel",N_("Compression ratio"), NULL }, +{ 0x9201, EXIF_FORMAT_RATIONAL, 1, "Exif.Photo.ShutterSpeedValue", N_("Shutter speed"), NULL }, +{ 0x9202, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.ApertureValue", N_("Aperture"), NULL }, +{ 0x9203, EXIF_FORMAT_RATIONAL, 1, "Exif.Photo.BrightnessValue", N_("Brightness"), NULL }, +{ 0x9204, EXIF_FORMAT_RATIONAL, 1, "Exif.Photo.ExposureBiasValue", N_("Exposure bias"), NULL }, +{ 0x9205, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.MaxApertureValue", N_("Maximum aperture"), NULL }, +{ 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.SubjectDistance", N_("Subject distance"), NULL }, +{ 0x9207, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.MeteringMode", N_("Metering mode"), ExifMeteringModeList }, +{ 0x9208, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.LightSource", N_("Light source"), ExifLightSourceList }, +{ 0x9209, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Flash", N_("Flash"), ExifFlashList }, +{ 0x920a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FocalLength", N_("Focal length"), NULL }, +{ 0x9214, EXIF_FORMAT_SHORT_UNSIGNED, -1, "Exif.Photo.SubjectArea", N_("Subject area"), NULL }, +{ 0x927c, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.MakerNote", N_("MakerNote"), NULL }, +{ 0x9286, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.UserComment", N_("UserComment"), NULL }, +{ 0x9290, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTime", N_("Subsecond time"), NULL }, +{ 0x9291, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTimeOriginal",N_("Subsecond time original"), NULL }, +{ 0x9292, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTimeDigitized",N_("Subsecond time digitized"), NULL }, +{ 0xa000, EXIF_FORMAT_UNDEFINED, 4, "Exif.Photo.FlashpixVersion", N_("FlashPix version"), NULL }, +{ 0xa001, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.ColorSpace", N_("Colorspace"), ExifColorSpaceList }, /* ExifImageWidth, ExifImageHeight can also be unsigned short */ -{ 0xa002, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifImageWidth", N_("Width"), NULL }, -{ 0xa003, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifImageHeight", N_("Height"), NULL }, -{ 0xa004, EXIF_FORMAT_STRING, -1, "RelatedSoundFile", "Audio data", NULL }, -{ 0xa005, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifInteroperabilityOffset", "ExifR98 extension", NULL }, -{ 0xa20b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FlashEnergy", "Flash strength", NULL }, -{ 0xa20c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SpatialFrequencyResponse","Spatial frequency response", NULL }, -{ 0xa20e, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalPlaneXResolution", "X Pixel density", NULL }, -{ 0xa20f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FocalPlaneYResolution", "Y Pixel density", NULL }, -{ 0xa210, EXIF_FORMAT_SHORT_UNSIGNED, 1, "FocalPlaneResolutionUnit", "Pixel density units", ExifUnitList }, -{ 0x0214, EXIF_FORMAT_SHORT_UNSIGNED, 2, "SubjectLocation", "Subject location", NULL }, -{ 0xa215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureIndex", N_("ISO sensitivity"), NULL }, -{ 0xa217, EXIF_FORMAT_SHORT_UNSIGNED, -1, "SensingMethod", "Sensor type", ExifSensorList }, -{ 0xa300, EXIF_FORMAT_UNDEFINED, 1, "FileSource", "Source type", ExifSourceList }, -{ 0xa301, EXIF_FORMAT_UNDEFINED, 1, "SceneType", "Scene type", ExifSceneList }, -{ 0xa302, EXIF_FORMAT_UNDEFINED, -1, "CFAPattern", "Color filter array pattern", NULL }, +{ 0xa002, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Photo.PixelXDimension", N_("Width"), NULL }, +{ 0xa003, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Photo.PixelYDimension", N_("Height"), NULL }, +{ 0xa004, EXIF_FORMAT_STRING, -1, "Exif.Photo.RelatedSoundFile", N_("Audio data"), NULL }, +{ 0xa005, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifInteroperabilityOffset", N_("ExifR98 extension"), NULL }, +{ 0xa20b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FlashEnergy", N_("Flash strength"), NULL }, +{ 0xa20c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.SpatialFrequencyResponse",N_("Spatial frequency response"), NULL }, +{ 0xa20e, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FocalPlaneXResolution", N_("X Pixel density"), NULL }, +{ 0xa20f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FocalPlaneYResolution", N_("Y Pixel density"), NULL }, +{ 0xa210, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.FocalPlaneResolutionUnit", N_("Pixel density units"), ExifUnitList }, +{ 0x0214, EXIF_FORMAT_SHORT_UNSIGNED, 2, "Exif.Photo.SubjectLocation", N_("Subject location"), NULL }, +{ 0xa215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.ExposureIndex", N_("ISO sensitivity"), NULL }, +{ 0xa217, EXIF_FORMAT_SHORT_UNSIGNED, -1, "Exif.Photo.SensingMethod", N_("Sensor type"), ExifSensorList }, +{ 0xa300, EXIF_FORMAT_UNDEFINED, 1, "Exif.Photo.FileSource", N_("Source type"), ExifSourceList }, +{ 0xa301, EXIF_FORMAT_UNDEFINED, 1, "Exif.Photo.SceneType", N_("Scene type"), ExifSceneList }, +{ 0xa302, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.CFAPattern", N_("Color filter array pattern"), NULL }, /* tags a4xx were added for Exif 2.2 (not just these - some above, as well) */ -{ 0xa401, EXIF_FORMAT_SHORT_UNSIGNED, 1, "CustomRendered", "Render process", ExifCustRenderList }, -{ 0xa402, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode", "Exposure mode", ExifExposureModeList }, -{ 0xa403, EXIF_FORMAT_SHORT_UNSIGNED, 1, "WhiteBalance", "White balance", ExifWhiteBalanceList }, -{ 0xa404, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "DigitalZoomRatio", "Digital zoom ratio", NULL }, -{ 0xa405, EXIF_FORMAT_SHORT_UNSIGNED, 1, "FocalLength35mmFilm", "Focal length (35mm)", NULL }, -{ 0xa406, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SceneCapturetype", "Scene capture type", ExifSceneCaptureList }, -{ 0xa407, EXIF_FORMAT_SHORT_UNSIGNED, 1, "GainControl", "Gain control", ExifGainControlList }, -{ 0xa408, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Contrast", "Contrast", ExifContrastList }, -{ 0xa409, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Saturation", "Saturation", ExifSaturationList }, -{ 0xa40a, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Sharpness", "Sharpness", ExifSharpnessList }, -{ 0xa40b, EXIF_FORMAT_UNDEFINED, -1, "DeviceSettingDescription","Device setting", NULL }, -{ 0xa40c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubjectDistanceRange", "Subject range", ExifSubjectRangeList }, -{ 0xa420, EXIF_FORMAT_STRING, -1, "ImageUniqueID", "Image serial number", NULL }, +{ 0xa401, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.CustomRendered", N_("Render process"), ExifCustRenderList }, +{ 0xa402, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.ExposureMode", N_("Exposure mode"), ExifExposureModeList }, +{ 0xa403, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.WhiteBalance", N_("White balance"), ExifWhiteBalanceList }, +{ 0xa404, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.DigitalZoomRatio", N_("Digital zoom ratio"), NULL }, +{ 0xa405, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.FocalLengthIn35mmFilm",N_("Focal length (35mm)"), NULL }, +{ 0xa406, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.SceneCaptureType", N_("Scene capture type"), ExifSceneCaptureList }, +{ 0xa407, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.GainControl", N_("Gain control"), ExifGainControlList }, +{ 0xa408, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Contrast", N_("Contrast"), ExifContrastList }, +{ 0xa409, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Saturation", N_("Saturation"), ExifSaturationList }, +{ 0xa40a, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Sharpness", N_("Sharpness"), ExifSharpnessList }, +{ 0xa40b, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.DeviceSettingDescription",N_("Device setting"), NULL }, +{ 0xa40c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.SubjectDistanceRange",N_("Subject range"), ExifSubjectRangeList }, +{ 0xa420, EXIF_FORMAT_STRING, -1, "Exif.Photo.ImageUniqueID", N_("Image serial number"), NULL }, /* place known, but undocumented or lesser used tags here */ -{ 0x00fe, EXIF_FORMAT_LONG_UNSIGNED, 1, "NewSubfileType", NULL, NULL }, -{ 0x00ff, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubfileType", NULL, NULL }, -{ 0x012d, EXIF_FORMAT_SHORT_UNSIGNED, 3, "TransferFunction", NULL, NULL }, -{ 0x013b, EXIF_FORMAT_STRING, -1, "Artist", "Artist", NULL }, +{ 0x00fe, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Image.NewSubfileType", NULL, NULL }, +{ 0x00ff, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubfileType", NULL, NULL }, +{ 0x012d, EXIF_FORMAT_SHORT_UNSIGNED, 3, "Exif.Image.TransferFunction", NULL, NULL }, +{ 0x013b, EXIF_FORMAT_STRING, -1, "Exif.Image.Artist", "Artist", NULL }, { 0x013d, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Predictor", NULL, NULL }, { 0x0142, EXIF_FORMAT_SHORT_UNSIGNED, 1, "TileWidth", NULL, NULL }, { 0x0143, EXIF_FORMAT_SHORT_UNSIGNED, 1, "TileLength", NULL, NULL }, { 0x0144, EXIF_FORMAT_LONG_UNSIGNED, -1, "TileOffsets", NULL, NULL }, { 0x0145, EXIF_FORMAT_SHORT_UNSIGNED, -1, "TileByteCounts", NULL, NULL }, -{ 0x014a, EXIF_FORMAT_LONG_UNSIGNED, -1, "SubIFDs", NULL, NULL }, +{ 0x014a, EXIF_FORMAT_LONG_UNSIGNED, -1, "Exif.Image.SubIFDs", NULL, NULL }, { 0x015b, EXIF_FORMAT_UNDEFINED, -1, "JPEGTables", NULL, NULL }, -{ 0x828d, EXIF_FORMAT_SHORT_UNSIGNED, 2, "CFARepeatPatternDim", NULL, NULL }, -{ 0x828e, EXIF_FORMAT_BYTE_UNSIGNED, -1, "CFAPattern", NULL, NULL }, -{ 0x828f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "BatteryLevel", NULL, NULL }, +{ 0x828d, EXIF_FORMAT_SHORT_UNSIGNED, 2, "Exif.Image.CFARepeatPatternDim", NULL, NULL }, +{ 0x828e, EXIF_FORMAT_BYTE_UNSIGNED, -1, "Exif.Image.CFAPattern", NULL, NULL }, +{ 0x828f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Image.BatteryLevel", NULL, NULL }, { 0x83bb, EXIF_FORMAT_LONG_UNSIGNED, -1, "IPTC/NAA", NULL, NULL }, -{ 0x8773, EXIF_FORMAT_UNDEFINED, -1, "InterColorProfile", NULL, NULL }, +{ 0x8773, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.InterColorProfile", NULL, NULL }, { 0x8825, EXIF_FORMAT_LONG_UNSIGNED, 1, "GPSInfo", "SubIFD GPS offset", NULL }, { 0x8829, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Interlace", NULL, NULL }, { 0x882a, EXIF_FORMAT_SHORT, 1, "TimeZoneOffset", NULL, NULL }, { 0x882b, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SelfTimerMode", NULL, NULL }, -{ 0x920b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "FlashEnergy", NULL, NULL }, -{ 0x920c, EXIF_FORMAT_UNDEFINED, -1, "SpatialFrequencyResponse", NULL, NULL }, +{ 0x920b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FlashEnergy", NULL, NULL }, +{ 0x920c, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.SpatialFrequencyResponse", NULL, NULL }, { 0x920d, EXIF_FORMAT_UNDEFINED, -1, "Noise", NULL, NULL }, { 0x9211, EXIF_FORMAT_LONG_UNSIGNED, 1, "ImageNumber", NULL, NULL }, { 0x9212, EXIF_FORMAT_STRING, 1, "SecurityClassification", NULL, NULL }, { 0x9213, EXIF_FORMAT_STRING, -1, "ImageHistory", NULL, NULL }, -{ 0x9215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "ExposureIndex", NULL, NULL }, +{ 0x9215, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.ExposureIndex", NULL, NULL }, { 0x9216, EXIF_FORMAT_BYTE_UNSIGNED, 4, "TIFF/EPStandardID", NULL, NULL }, EXIF_MARKER_LIST_END }; +ExifMarker ExifKnownGPSInfoMarkersList[] = { + /* The following do not work at the moment as the tag value 0x0000 has a + * special meaning. */ + /* { 0x0000, EXIF_FORMAT_BYTE, -1, "Exif.GPSInfo.GPSVersionID", NULL, NULL }, */ + { 0x0001, EXIF_FORMAT_STRING, 2, "Exif.GPSInfo.GPSLatitudeRef", NULL, NULL }, + { 0x0002, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSLatitude", NULL, NULL }, + { 0x0003, EXIF_FORMAT_STRING, 2, "Exif.GPSInfo.GPSLongitudeRef", NULL, NULL }, + { 0x0004, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSLongitude", NULL, NULL }, + { 0x0005, EXIF_FORMAT_BYTE_UNSIGNED, 1, "Exif.GPSInfo.GPSAltitudeRef", NULL, NULL }, + { 0x0006, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.GPSInfo.GPSAltitude", NULL, NULL }, + { 0x0007, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSTimeStamp", NULL, NULL }, + { 0x0008, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSSatellites", NULL, NULL }, + { 0x0009, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSStatus", NULL, NULL }, + { 0x000a, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSMeasureMode", NULL, NULL }, + { 0x000b, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDOP", NULL, NULL }, + { 0x000c, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSSpeedRef", NULL, NULL }, + { 0x000d, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSSpeed", NULL, NULL }, + { 0x000e, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSTrackRef", NULL, NULL }, + { 0x000f, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSTrack", NULL, NULL }, + { 0x0010, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSImgDirectionRef", NULL, NULL }, + { 0x0011, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSImgDirection", NULL, NULL }, + { 0x0012, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSMapDatum", NULL, NULL }, + { 0x0013, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestLatitudeRef", NULL, NULL }, + { 0x0014, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestLatitude", NULL, NULL }, + { 0x0015, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestLongitudeRef", NULL, NULL }, + { 0x0016, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestLongitude", NULL, NULL }, + { 0x0017, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestBearingRef", NULL, NULL }, + { 0x0018, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestBearing", NULL, NULL }, + { 0x0019, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestDistanceRef", NULL, NULL }, + { 0x001a, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestDistance", NULL, NULL }, + { 0x001b, EXIF_FORMAT_UNDEFINED, -1, "Exif.GPSInfo.GPSProcessingMethod", NULL, NULL }, + { 0x001c, EXIF_FORMAT_UNDEFINED, -1, "Exif.GPSInfo.GPSAreaInformation", NULL, NULL }, + { 0x001d, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSDateStamp", NULL, NULL }, + { 0x001e, EXIF_FORMAT_SHORT, -1, "Exif.GPSInfo.GPSDifferential", NULL, NULL }, + + EXIF_MARKER_LIST_END +}; + ExifMarker ExifUnknownMarkersList[] = { { 0x0000, EXIF_FORMAT_UNKNOWN, 0, "unknown", NULL, NULL }, { 0x0000, EXIF_FORMAT_BYTE_UNSIGNED, -1, "unknown", NULL, NULL }, @@ -410,23 +488,6 @@ ExifMarker ExifUnknownMarkersList[] = { { 0x0000, EXIF_FORMAT_DOUBLE, -1, "unknown", NULL, NULL }, }; -/* human readable key list */ - -ExifFormattedText ExifFormattedList[] = { - { "fCamera", N_("Camera") }, - { "fDateTime", N_("Date") }, - { "fShutterSpeed", N_("Shutter speed") }, - { "fAperture", N_("Aperture") }, - { "fExposureBias", N_("Exposure bias") }, - { "fISOSpeedRating", N_("ISO sensitivity") }, - { "fFocalLength", N_("Focal length") }, - { "fSubjectDistance", N_("Subject distance") }, - { "fFlash", N_("Flash") }, - { "fResolution", N_("Resolution") }, - { NULL, NULL } -}; - - static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list); /* @@ -445,8 +506,6 @@ ExifItem *exif_item_new(ExifFormatType format, guint tag, item->tag = tag; item->marker = marker; item->elements = elements; - item->data = NULL; - item->data_len = 0; switch (format) { @@ -455,10 +514,10 @@ ExifItem *exif_item_new(ExifFormatType format, guint tag, return item; break; case EXIF_FORMAT_BYTE_UNSIGNED: - item->data_len = sizeof(char) * elements; + item->data_len = sizeof(gchar) * elements; break; case EXIF_FORMAT_STRING: - item->data_len = sizeof(char) * elements; + item->data_len = sizeof(gchar) * elements; break; case EXIF_FORMAT_SHORT_UNSIGNED: item->data_len = sizeof(guint16) * elements; @@ -470,10 +529,10 @@ ExifItem *exif_item_new(ExifFormatType format, guint tag, item->data_len = sizeof(ExifRational) * elements; break; case EXIF_FORMAT_BYTE: - item->data_len = sizeof(char) * elements; + item->data_len = sizeof(gchar) * elements; break; case EXIF_FORMAT_UNDEFINED: - item->data_len = sizeof(char) * elements; + item->data_len = sizeof(gchar) * elements; break; case EXIF_FORMAT_SHORT: item->data_len = sizeof(gint16) * elements; @@ -488,7 +547,7 @@ ExifItem *exif_item_new(ExifFormatType format, guint tag, item->data_len = sizeof(float) * elements; break; case EXIF_FORMAT_DOUBLE: - item->data_len = sizeof(double) * elements; + item->data_len = sizeof(gdouble) * elements; break; } @@ -505,39 +564,62 @@ static void exif_item_free(ExifItem *item) g_free(item); } -const char *exif_item_get_tag_name(ExifItem *item) +gchar *exif_item_get_tag_name(ExifItem *item) { if (!item || !item->marker) return NULL; - return item->marker->key; + return g_strdup(item->marker->key); } -const char *exif_item_get_description(ExifItem *item) +guint exif_item_get_tag_id(ExifItem *item) { - if (!item || !item->marker) return NULL; - return _(item->marker->description); + if (!item) return 0; + return item->tag; } -const char *exif_item_get_format_name(ExifItem *item, gint brief) +guint exif_item_get_elements(ExifItem *item) { - if (!item || !item->marker) return NULL; - return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description; + if (!item) return 0; + return item->elements; +} + +gchar *exif_item_get_data(ExifItem *item, guint *data_len) +{ + if (data_len) + *data_len = item->data_len; + return g_memdup(item->data, item->data_len); +} + +guint exif_item_get_format_id(ExifItem *item) +{ + if (!item) return EXIF_FORMAT_UNKNOWN; + return item->format; } -#define UNDEFINED_TEXT_BYTE_COUNT 16 +gchar *exif_item_get_description(ExifItem *item) +{ + if (!item || !item->marker) return NULL; + return g_strdup(_(item->marker->description)); +} + +const gchar *exif_item_get_format_name(ExifItem *item, gboolean brief) +{ + if (!item || !item->marker) return NULL; + return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description; +} static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne) { gint i; - for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++) + for (i = 0 ; i < ne; i++) { - unsigned char c = ((char *)data)[i]; + guchar c = ((gchar *)data)[i]; if (c < 32 || c > 127) c = '.'; g_string_append_printf(string, "%c", c); } string = g_string_append(string, " : "); - for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++) + for (i = 0 ; i < ne; i++) { const gchar *spacer; if (i > 0) @@ -555,13 +637,13 @@ static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne) { spacer = ""; } - g_string_append_printf(string, "%s%02x", spacer, ((char *)data)[i]); + g_string_append_printf(string, "%s%02x", spacer, ((gchar *)data)[i]); } - if (i >= UNDEFINED_TEXT_BYTE_COUNT) g_string_append_printf(string, " (%d bytes)", ne); return string; } + gchar *exif_text_list_find_value(ExifTextList *list, guint value) { gchar *result = NULL; @@ -570,7 +652,7 @@ gchar *exif_text_list_find_value(ExifTextList *list, guint value) i = 0; while (!result && list[i].value >= 0) { - if (value == list[i].value) result = g_strdup(_(list[i].description)); + if (value == (guint) list[i].value) result = g_strdup(_(list[i].description)); i++; } if (!result) result = g_strdup_printf("%d (%s)", value, _("unknown")); @@ -578,52 +660,72 @@ gchar *exif_text_list_find_value(ExifTextList *list, guint value) return result; } + /* *------------------------------------------------------------------- - * byte size utils + * byte order utils *------------------------------------------------------------------- */ -guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo) +/* note: the align_buf is used to avoid alignment issues (on sparc) */ + +guint16 exif_byte_get_int16(guchar *f, ExifByteOrder bo) { + guint16 align_buf; + + memcpy(&align_buf, f, sizeof(guint16)); + if (bo == EXIF_BYTE_ORDER_INTEL) - return GUINT16_FROM_LE(*(guint16*)f); + return GUINT16_FROM_LE(align_buf); else - return GUINT16_FROM_BE(*(guint16*)f); + return GUINT16_FROM_BE(align_buf); } -guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo) +guint32 exif_byte_get_int32(guchar *f, ExifByteOrder bo) { + guint32 align_buf; + + memcpy(&align_buf, f, sizeof(guint32)); + if (bo == EXIF_BYTE_ORDER_INTEL) - return GUINT32_FROM_LE(*(guint32*)f); + return GUINT32_FROM_LE(align_buf); else - return GUINT32_FROM_BE(*(guint32*)f); + return GUINT32_FROM_BE(align_buf); } -guint16 exif_byte_swab_int16(guint16 n, ExifByteOrder bo) +void exif_byte_put_int16(guchar *f, guint16 n, ExifByteOrder bo) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - if (bo == EXIF_BYTE_ORDER_MOTOROLA) -#else + guint16 align_buf; + if (bo == EXIF_BYTE_ORDER_INTEL) -#endif - return GUINT16_SWAP_LE_BE(n); + { + align_buf = GUINT16_TO_LE(n); + } else - return n; + { + align_buf = GUINT16_TO_BE(n); + } + + memcpy(f, &align_buf, sizeof(guint16)); } -guint32 exif_byte_swab_int32(guint32 n, ExifByteOrder bo) +void exif_byte_put_int32(guchar *f, guint32 n, ExifByteOrder bo) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - if (bo == EXIF_BYTE_ORDER_MOTOROLA) -#else + guint32 align_buf; + if (bo == EXIF_BYTE_ORDER_INTEL) -#endif - return GUINT32_SWAP_LE_BE(n); + { + align_buf = GUINT32_TO_LE(n); + } else - return n; + { + align_buf = GUINT32_TO_BE(n); + } + + memcpy(f, &align_buf, sizeof(guint32)); } + /* *------------------------------------------------------------------- * IFD utils @@ -644,7 +746,7 @@ static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *lis return (list[i].tag == 0 ? NULL : &list[i]); } -static void rational_from_data(ExifRational *r, void *src, ExifByteOrder bo) +static void rational_from_data(ExifRational *r, gpointer src, ExifByteOrder bo) { r->num = exif_byte_get_int32(src, bo); r->den = exif_byte_get_int32(src + sizeof(guint32), bo); @@ -653,7 +755,7 @@ static void rational_from_data(ExifRational *r, void *src, ExifByteOrder bo) /* src_format and item->format must be compatible * and not overrun src or item->data. */ -void exif_item_copy_data(ExifItem *item, void *src, guint len, +void exif_item_copy_data(ExifItem *item, gpointer src, guint len, ExifFormatType src_format, ExifByteOrder bo) { gint bs; @@ -668,7 +770,9 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len, if (!dest || ExifFormatList[src_format].size * ne > len) { - printf("exif tag %s data size mismatch\n", exif_item_get_tag_name(item)); + gchar *tag = exif_item_get_tag_name(item); + log_printf("exif tag %s data size mismatch\n", tag); + g_free(tag); return; } @@ -684,7 +788,7 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len, case EXIF_FORMAT_STRING: memcpy(dest, src, len); /* string is NULL terminated, make sure this is true */ - if (((char *)dest)[len - 1] != '\0') ((char *)dest)[len - 1] = '\0'; + if (((gchar *)dest)[len - 1] != '\0') ((gchar *)dest)[len - 1] = '\0'; break; case EXIF_FORMAT_SHORT_UNSIGNED: case EXIF_FORMAT_SHORT: @@ -736,13 +840,13 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len, ExifRational r; rational_from_data(&r, src + i * bs, bo); - if (r.den) ((double *)dest)[i] = (double)r.num / r.den; + if (r.den) ((gdouble *)dest)[i] = (gdouble)r.num / r.den; } break; } } -static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offset, +static gint exif_parse_IFD_entry(ExifData *exif, guchar *tiff, guint offset, guint size, ExifByteOrder bo, gint level, const ExifMarker *list) @@ -769,7 +873,7 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs { if (format >= EXIF_FORMAT_COUNT) { - printf("warning: exif tag 0x%4x has invalid format %d\n", tag, format); + log_printf("warning: exif tag 0x%4x has invalid format %d\n", tag, format); return 0; } /* allow non recognized tags to be displayed */ @@ -792,13 +896,13 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs { if (format < EXIF_FORMAT_COUNT) { - printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n", + log_printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n", marker->key, ExifFormatList[format].short_name, ExifFormatList[marker->format].short_name); } else { - printf("warning: exif tag %s format mismatch, found unknown id %d exif spec requests %d (%s)\n", + log_printf("warning: exif tag %s format mismatch, found unknown id %d exif spec requests %d (%s)\n", marker->key, format, marker->format, ExifFormatList[marker->format].short_name); } @@ -808,9 +912,9 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs /* Where is the data, is it available? */ - if (marker->components > 0 && marker->components != count) + if (marker->components > 0 && (guint) marker->components != count) { - printf("warning: exif tag %s has %d elements, exif spec requests %d\n", + log_printf("warning: exif tag %s has %d elements, exif spec requests %d\n", marker->key, count, marker->components); } @@ -818,9 +922,9 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs if (data_length > 4) { data_offset = data_val; - if (size < data_offset + data_length) + if (size < data_offset || size < data_offset + data_length) { - printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key); + log_printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key); return -1; } } @@ -840,6 +944,9 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs case TAG_EXIFOFFSET: exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, list); break; + case TAG_GPSOFFSET: + exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, ExifKnownGPSInfoMarkersList); + break; case TAG_EXIFMAKERNOTE: format_exif_makernote_parse(exif, tiff, data_val, size, bo); break; @@ -850,7 +957,7 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs } gint exif_parse_IFD_table(ExifData *exif, - unsigned char *tiff, guint offset, + guchar *tiff, guint offset, guint size, ExifByteOrder bo, gint level, const ExifMarker *list) @@ -884,7 +991,7 @@ gint exif_parse_IFD_table(ExifData *exif, *------------------------------------------------------------------- */ -gint exif_tiff_directory_offset(unsigned char *data, const guint len, +gint exif_tiff_directory_offset(guchar *data, const guint len, guint *offset, ExifByteOrder *bo) { if (len < 8) return FALSE; @@ -912,7 +1019,7 @@ gint exif_tiff_directory_offset(unsigned char *data, const guint len, return (*offset < len); } -gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list) +gint exif_tiff_parse(ExifData *exif, guchar *tiff, guint size, ExifMarker *list) { ExifByteOrder bo; guint offset; @@ -922,101 +1029,131 @@ gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list); } + /* *------------------------------------------------------------------- * jpeg marker utils *------------------------------------------------------------------- */ -#define MARKER_UNKNOWN 0x00 -#define MARKER_SOI 0xD8 -#define MARKER_APP1 0xE1 +#define JPEG_MARKER 0xFF +#define JPEG_MARKER_SOI 0xD8 +#define JPEG_MARKER_EOI 0xD9 +#define JPEG_MARKER_APP1 0xE1 +#define JPEG_MARKER_APP2 0xE2 + +/* jpeg container format: + all data markers start with 0XFF + 2 byte long file start and end markers: 0xFFD8(SOI) and 0XFFD9(EOI) + 4 byte long data segment markers in format: 0xFFTTSSSSNNN... + FF: 1 byte standard marker identifier + TT: 1 byte data type + SSSS: 2 bytes in Motorola byte alignment for length of the data. + This value includes these 2 bytes in the count, making actual + length of NN... == SSSS - 2. + NNN.: the data in this segment + */ +static ExifMarker jpeg_color_marker = { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.InterColorProfile", NULL, NULL }; -static gint jpeg_get_marker_size(unsigned char *data) +void exif_add_jpeg_color_profile(ExifData *exif, guchar *cp_data, guint cp_length) { - /* Size is always in Motorola byte order */ - return exif_byte_get_int16(data + 2, EXIF_BYTE_ORDER_MOTOROLA); + ExifItem *item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1, + &jpeg_color_marker); + g_free(item->data); + item->data = cp_data; + item->elements = cp_length; + item->data_len = cp_length; + exif->items = g_list_prepend(exif->items, item); + } -static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker) +static gint exif_jpeg_parse(ExifData *exif, + guchar *data, guint size, + ExifMarker *list) { - gint marker_size = 2; - - *marker = MARKER_UNKNOWN; + guint seg_offset = 0; + guint seg_length = 0; + gint res = -1; - /* It is safe to access the marker and its size since we have checked - * the SOI and this function guaranties the whole next marker is - * available - */ - if (*(*data + 1) != MARKER_SOI) + if (size < 4 || + memcmp(data, "\xFF\xD8", 2) != 0) { - marker_size += jpeg_get_marker_size(*data); + return -2; } - *size -= marker_size; + if (jpeg_segment_find(data, size, JPEG_MARKER_APP1, + "Exif\x00\x00", 6, + &seg_offset, &seg_length)) + { + res = exif_tiff_parse(exif, data + seg_offset + 6, seg_length - 6, list); + } - /* size should be at least 4, so we can read the marker and its size - * and check data are actually available - */ - if (*size < 4) return -1; + if (exif_jpeg_parse_color(exif, data, size)) + { + res = 0; + } - /* Jump to the next marker and be sure it begins with 0xFF - */ - *data += marker_size; - if (**data != 0xFF) return -1; + return res; +} - if (jpeg_get_marker_size(*data) + 2 > *size) return -1; +guchar *exif_get_color_profile(ExifData *exif, guint *data_len) +{ + ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); + if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) + return (guchar *) exif_item_get_data(prof_item, data_len); + return NULL; +} - *marker = *(*data + 1); - return 0; +gchar* exif_get_image_comment(FileData* fd) +{ + log_printf("%s", _("Can't get image comment: not compiled with Exiv2.\n")); + return g_strdup(""); } - -static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, guint size, ExifMarker *list) +void exif_set_image_comment(FileData* fd, const gchar* comment) { - guint marker; - guint marker_size; + log_printf("%s", _("Can't set image comment: not compiled with Exiv2.\n")); +} - if (size < 4 || *data != 0xFF || *(data + 1) != MARKER_SOI) - { - return -2; - } - do { - if (jpeg_goto_next_marker(&data, &size, &marker) == -1) - { - break; - } - } while (marker != MARKER_APP1); +/* + *------------------------------------------------------------------- + * misc + *------------------------------------------------------------------- + */ + - if (marker != MARKER_APP1) +ExifItem *exif_get_first_item(ExifData *exif) +{ + if (exif->items) { - return -2; + ExifItem *ret = (ExifItem *)exif->items->data; + exif->current = exif->items->next; + return ret; } + exif->current = NULL; + return NULL; +} - marker_size = jpeg_get_marker_size(data) - 2; - - if (marker_size < 6 || memcmp(data + 4, "Exif\x00\x00", 6) != 0) +ExifItem *exif_get_next_item(ExifData *exif) +{ + if (exif->current) { - return -2; + ExifItem *ret = (ExifItem *)exif->current->data; + exif->current = exif->current->next; + return ret; } - - return exif_tiff_parse(exif, data + 10, marker_size - 6, list); + return NULL; } -/* - *------------------------------------------------------------------- - * misc - *------------------------------------------------------------------- - */ - -static gint map_file(const gchar *path, void **mapping, int *size) +static gint map_file(const gchar *path, void **mapping, gint *size) { - int fd; + gint fd; struct stat fs; - if ((fd = open(path, O_RDONLY)) == -1) + fd = open(path, O_RDONLY); + if (fd == -1) { perror(path); return -1; @@ -1031,7 +1168,8 @@ static gint map_file(const gchar *path, void **mapping, int *size) *size = fs.st_size; - if ((*mapping = mmap(0, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) + *mapping = mmap(0, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (*mapping == MAP_FAILED) { perror(path); close(fd); @@ -1042,7 +1180,7 @@ static gint map_file(const gchar *path, void **mapping, int *size) return 0; } -static gint unmap_file(void *mapping, int size) +static gint unmap_file(gpointer mapping, gint size) { if (munmap(mapping, size) == -1) { @@ -1053,6 +1191,11 @@ static gint unmap_file(void *mapping, int size) return 0; } +ExifData *exif_get_original(ExifData *processed) +{ + return processed; +} + void exif_free(ExifData *exif) { GList *work; @@ -1068,14 +1211,15 @@ void exif_free(ExifData *exif) } g_list_free(exif->items); + g_free(exif->path); g_free(exif); } -ExifData *exif_read(const gchar *path) +ExifData *exif_read(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp) { ExifData *exif; - void *f; - int size, res; + gpointer f; + gint size, res; gchar *pathl; if (!path) return NULL; @@ -1089,20 +1233,47 @@ ExifData *exif_read(const gchar *path) g_free(pathl); exif = g_new0(ExifData, 1); - exif->items = NULL; + exif->path = g_strdup(path); - if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2) + res = exif_jpeg_parse(exif, (guchar *)f, size, ExifKnownMarkersList); + if (res == -2) { - res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList); + res = exif_tiff_parse(exif, (guchar *)f, size, ExifKnownMarkersList); } if (res != 0) { + FormatRawExifType exif_type; + FormatRawExifParseFunc exif_parse_func; guint32 offset = 0; - - if (format_raw_img_exif_offsets(f, size, NULL, &offset)) + + exif_type = format_raw_exif_offset(f, size, &offset, &exif_parse_func); + switch (exif_type) { - res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList); + case FORMAT_RAW_EXIF_NONE: + default: + break; + case FORMAT_RAW_EXIF_TIFF: + res = exif_tiff_parse(exif, (guchar *)f + offset, size - offset, + ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_JPEG: + res = exif_jpeg_parse(exif, (guchar *)f + offset, size - offset, + ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_IFD_II: + case FORMAT_RAW_EXIF_IFD_MM: + res = exif_parse_IFD_table(exif, (guchar *)f, offset, size - offset, + (exif_type == FORMAT_RAW_EXIF_IFD_II) ? + EXIF_BYTE_ORDER_INTEL : EXIF_BYTE_ORDER_MOTOROLA, + 0, ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_PROPRIETARY: + if (exif_parse_func) + { + res = exif_parse_func((guchar *)f + offset, size - offset, exif); + } + break; } } @@ -1116,11 +1287,6 @@ ExifData *exif_read(const gchar *path) if (exif) exif->items = g_list_reverse(exif->items); -#if 0 - exif_write_data_list(exif, stdout, TRUE); - exif_write_data_list(exif, stdout, FALSE); -#endif - return exif; } @@ -1132,17 +1298,20 @@ ExifItem *exif_get_item(ExifData *exif, const gchar *key) work = exif->items; while (work) - { + { ExifItem *item; item = work->data; work = work->next; if (item->marker->key && strcmp(key, item->marker->key) == 0) return item; - } + } return NULL; } -gchar *exif_item_get_data_as_text(ExifItem *item) +#define EXIF_DATA_AS_TEXT_MAX_COUNT 16 + + +static gchar *exif_item_get_data_as_text_full(ExifItem *item, MetadataFormat format) { const ExifMarker *marker; gpointer data; @@ -1158,6 +1327,7 @@ gchar *exif_item_get_data_as_text(ExifItem *item) data = item->data; ne = item->elements; + if (ne > EXIF_DATA_AS_TEXT_MAX_COUNT) ne = EXIF_DATA_AS_TEXT_MAX_COUNT; string = g_string_new(""); switch (item->format) { @@ -1166,19 +1336,19 @@ gchar *exif_item_get_data_as_text(ExifItem *item) case EXIF_FORMAT_BYTE_UNSIGNED: case EXIF_FORMAT_BYTE: case EXIF_FORMAT_UNDEFINED: - if (ne == 1 && marker->list) + if (ne == 1 && marker->list && format == METADATA_FORMATTED) { gchar *result; - unsigned char val; + guchar val; if (item->format == EXIF_FORMAT_BYTE_UNSIGNED || item->format == EXIF_FORMAT_UNDEFINED) { - val = ((unsigned char *)data)[0]; + val = ((guchar *)data)[0]; } else { - val = (unsigned char)(((signed char *)data)[0]); + val = (guchar)(((gchar *)data)[0]); } result = exif_text_list_find_value(marker->list, (guint)val); @@ -1191,28 +1361,28 @@ gchar *exif_item_get_data_as_text(ExifItem *item) } break; case EXIF_FORMAT_STRING: - string = g_string_append(string, (gchar *)(item->data)); + if (item->data) string = g_string_append(string, (gchar *)(item->data)); break; case EXIF_FORMAT_SHORT_UNSIGNED: - if (ne == 1 && marker->list) + if (ne == 1 && marker->list && format == METADATA_FORMATTED) { gchar *result; - result = exif_text_list_find_value(marker->list, ((unsigned short *)data)[0]); + result = exif_text_list_find_value(marker->list, ((guint16 *)data)[0]); string = g_string_append(string, result); g_free(result); } else for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%hd", (i > 0) ? ", " : "", - ((unsigned short *)data)[i]); + ((guint16 *)data)[i]); } break; case EXIF_FORMAT_LONG_UNSIGNED: for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", - ((unsigned long *)data)[i]); + (gulong)((guint32 *)data)[i]); } break; case EXIF_FORMAT_RATIONAL_UNSIGNED: @@ -1222,21 +1392,21 @@ gchar *exif_item_get_data_as_text(ExifItem *item) r = &((ExifRational *)data)[i]; g_string_append_printf(string, "%s%ld/%ld", (i > 0) ? ", " : "", - (unsigned long)r->num, (unsigned long)r->den); + (gulong)r->num, (gulong)r->den); } break; case EXIF_FORMAT_SHORT: for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%hd", (i > 0) ? ", " : "", - ((short *)data)[i]); + ((gint16 *)data)[i]); } break; case EXIF_FORMAT_LONG: for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", - ((long *)data)[i]); + (glong)((gint32 *)data)[i]); } break; case EXIF_FORMAT_RATIONAL: @@ -1246,7 +1416,7 @@ gchar *exif_item_get_data_as_text(ExifItem *item) r = &((ExifRational *)data)[i]; g_string_append_printf(string, "%s%ld/%ld", (i > 0) ? ", " : "", - (long)r->num, (long)r->den); + (glong)r->num, (glong)r->den); } break; case EXIF_FORMAT_FLOAT: @@ -1260,38 +1430,55 @@ gchar *exif_item_get_data_as_text(ExifItem *item) for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%f", (i > 0) ? ", " : "", - ((double *)data)[i]); + ((gdouble *)data)[i]); } break; } - text = g_strdup(string->str); - g_string_free(string, TRUE); + if (item->elements > EXIF_DATA_AS_TEXT_MAX_COUNT && + item->format != EXIF_FORMAT_STRING) + { + g_string_append(string, " ..."); + } + + text = string->str; + g_string_free(string, FALSE); return text; } +gchar *exif_item_get_string(ExifItem *item, gint idx) +{ + return exif_item_get_data_as_text_full(item, METADATA_PLAIN); +} + +gchar *exif_item_get_data_as_text(ExifItem *item) +{ + return exif_item_get_data_as_text_full(item, METADATA_FORMATTED); +} + gint exif_item_get_integer(ExifItem *item, gint *value) { if (!item) return FALSE; + if (!item->elements) return FALSE; switch (item->format) { case EXIF_FORMAT_SHORT: - *value = (gint)(((short *)(item->data))[0]); + *value = (gint)(((gint16 *)(item->data))[0]); return TRUE; break; case EXIF_FORMAT_SHORT_UNSIGNED: - *value = (gint)(((unsigned short *)(item->data))[0]); + *value = (gint)(((guint16 *)(item->data))[0]); return TRUE; break; case EXIF_FORMAT_LONG: - *value = (gint)(((long *)(item->data))[0]); + *value = (gint)(((gint32 *)(item->data))[0]); return TRUE; break; case EXIF_FORMAT_LONG_UNSIGNED: /* FIXME: overflow possible */ - *value = (gint)(((unsigned long *)(item->data))[0]); + *value = (gint)(((guint32 *)(item->data))[0]); return TRUE; default: /* all other type return FALSE */ @@ -1300,352 +1487,235 @@ gint exif_item_get_integer(ExifItem *item, gint *value) return FALSE; } -gint exif_get_integer(ExifData *exif, const gchar *key, gint *value) -{ - ExifItem *item; - - item = exif_get_item(exif, key); - return exif_item_get_integer(item, value); -} -ExifRational *exif_item_get_rational(ExifItem *item, gint *sign) +ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n) { if (!item) return NULL; + if (n >= item->elements) return NULL; if (item->format == EXIF_FORMAT_RATIONAL || item->format == EXIF_FORMAT_RATIONAL_UNSIGNED) { if (sign) *sign = (item->format == EXIF_FORMAT_RATIONAL); - return &((ExifRational *)(item->data))[0]; + return &((ExifRational *)(item->data))[n]; } return NULL; } -ExifRational *exif_get_rational(ExifData *exif, const gchar *key, gint *sign) +gchar *exif_get_tag_description_by_key(const gchar *key) { - ExifItem *item; - - item = exif_get_item(exif, key); - return exif_item_get_rational(item, sign); -} + gint i; -double exif_rational_to_double(ExifRational *r, gint sign) -{ - if (!r || r->den == 0.0) return 0.0; + if (!key) return NULL; - if (sign) return (double)((int)r->num) / (double)((int)r->den); - return (double)r->num / r->den; -} + i = 0; + while (ExifKnownMarkersList[i].tag > 0) + { + if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return g_strdup(_(ExifKnownMarkersList[i].description)); + i++; + } -static double exif_get_rational_as_double(ExifData *exif, const gchar *key) -{ - ExifRational *r; - gint sign; + i = 0; + while (ExifKnownGPSInfoMarkersList[i].tag > 0) + { + if (strcmp(key, ExifKnownGPSInfoMarkersList[i].key) == 0) return g_strdup(_(ExifKnownGPSInfoMarkersList[i].description)); + i++; + } - r = exif_get_rational(exif, key, &sign); - return exif_rational_to_double(r, sign); + return NULL; } -static GString *append_comma_text(GString *string, const gchar *text) +static void exif_write_item(FILE *f, ExifItem *item) { - string = g_string_append(string, ", "); - string = g_string_append(string, text); + gchar *text; - return string; + text = exif_item_get_data_as_text(item); + if (text) + { + gchar *tag = exif_item_get_tag_name(item); + g_fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name, + tag, text); + g_free(tag); + } + g_free(text); } -static gchar *exif_get_formatted_by_key(ExifData *exif, const gchar *key, gint *key_valid) +void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) { - /* must begin with f, else not formatted */ - if (key[0] != 'f') - { - if (key_valid) *key_valid = FALSE; - return NULL; - } + if (!f || !exif) return; - if (key_valid) *key_valid = TRUE; + g_fprintf(f, " tag format key value\n"); + g_fprintf(f, "----------------------------------------------------\n"); - if (strcmp(key, "fCamera") == 0) - { - gchar *text; - gchar *make = exif_get_data_as_text(exif, "Make"); - gchar *model = exif_get_data_as_text(exif, "Model"); - gchar *software = exif_get_data_as_text(exif, "Software"); - - text = g_strdup_printf("%s%s%s%s%s%s", (make) ? make : "", ((make) && (model)) ? " " : "", - (model) ? model : "", - (software) ? " (" : "", - (software) ? software : "", - (software) ? ")" : ""); - - g_free(make); - g_free(model); - g_free(software); - return text; - } - if (strcmp(key, "fDateTime") == 0) + if (human_readable_list) { - gchar *text = exif_get_data_as_text(exif, "DateTimeOriginal"); - gchar *subsec = NULL; - if (text) subsec = exif_get_data_as_text(exif, "SubsecTimeOriginal"); - if (!text) - { - text = exif_get_data_as_text(exif, "DateTime"); - if (text) subsec = exif_get_data_as_text(exif, "SubsecTime"); - } - if (subsec) + gint i; + + i = 0; + while (ExifFormattedList[i].key) { - gchar *tmp = text; - text = g_strconcat(tmp, ".", subsec, NULL); - g_free(tmp); - g_free(subsec); + gchar *text; + + text = exif_get_formatted_by_key(exif, ExifFormattedList[i].key, NULL); + if (text) + { + g_fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text); + } + i++; } - return text; } - if (strcmp(key, "fShutterSpeed") == 0) + else { - ExifRational *r; + GList *work; - r = exif_get_rational(exif, "ExposureTime", NULL); - if (r && r->num && r->den) - { - double n = (double)r->den / (double)r->num; - return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "", - n > 1.0 ? n : 1.0 / n); - } - r = exif_get_rational(exif, "ShutterSpeedValue", NULL); - if (r && r->num && r->den) + work = exif->items; + while (work) { - double n = pow(2.0, exif_rational_to_double(r, TRUE)); + ExifItem *item; - /* Correct exposure time to avoid values like 1/91s (seen on Minolta DImage 7) */ - if (n > 1.0 && (int)n - ((int)(n/10))*10 == 1) n--; + item = work->data; + work = work->next; - return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "", - n > 1.0 ? floor(n) : 1.0 / n); + exif_write_item(f, item); } - return NULL; } - if (strcmp(key, "fAperture") == 0) - { - double n; - - n = exif_get_rational_as_double(exif, "FNumber"); - if (n == 0.0) n = exif_get_rational_as_double(exif, "ApertureValue"); - if (n == 0.0) return NULL; + g_fprintf(f, "----------------------------------------------------\n"); +} - return g_strdup_printf("f/%.1f", n); - } - if (strcmp(key, "fExposureBias") == 0) - { - ExifRational *r; - gint sign; - double n; +gboolean exif_write(ExifData *exif) +{ + log_printf("Not compiled with EXIF write support\n"); + return FALSE; +} - r = exif_get_rational(exif, "ExposureBiasValue", &sign); - if (!r) return NULL; +gboolean exif_write_sidecar(ExifData *exif, gchar *path) +{ + log_printf("Not compiled with EXIF write support\n"); + return FALSE; +} - n = exif_rational_to_double(r, sign); - return g_strdup_printf("%+.1f", n); - } - if (strcmp(key, "fFocalLength") == 0) - { - double n; - n = exif_get_rational_as_double(exif, "FocalLength"); - if (n == 0.0) return NULL; - return g_strdup_printf("%.2f mm", n); - } - if (strcmp(key, "fISOSpeedRating") == 0) - { - gchar *text; +gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values) +{ + return 0; +} - text = exif_get_data_as_text(exif, "ISOSpeedRatings"); - /* kodak may set this instead */ - if (!text) text = exif_get_data_as_text(exif, "ExposureIndex"); - return text; - } - if (strcmp(key, "fSubjectDistance") == 0) - { - ExifRational *r; - gint sign; - double n; +GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format) +{ + gchar *str; + ExifItem *item; - r = exif_get_rational(exif, "SubjectDistance", &sign); - if (!r) return NULL; + if (!key) return NULL; - if ((long)r->num == 0xffffffff) return g_strdup(_("infinity")); - if ((long)r->num == 0) return g_strdup(_("unknown")); + /* convert xmp key to exif key */ + if (strcmp(key, "Xmp.tiff.Orientation") == 0) key = "Exif.Image.Orientation"; - n = exif_rational_to_double(r, sign); - if (n == 0.0) return _("unknown"); - return g_strdup_printf("%.3f m", n); - } - if (strcmp(key, "fFlash") == 0) + if (format == METADATA_FORMATTED) { - /* grr, flash is a bitmask... */ - GString *string; gchar *text; - gint n; - gint v; - - if (!exif_get_integer(exif, "Flash", &n)) return NULL; - - /* Exif 2.1 only defines first 3 bits */ - if (n <= 0x07) return exif_text_list_find_value(ExifFlashList, n); - - /* must be Exif 2.2 */ - string = g_string_new(""); - - /* flash fired (bit 0) */ - string = g_string_append(string, (n & 0x01) ? _("yes") : _("no")); - - /* flash mode (bits 3, 4) */ - v = (n >> 3) & 0x03; - if (v) string = append_comma_text(string, _("mode:")); - switch (v) - { - case 1: - string = g_string_append(string, _("on")); - break; - case 2: - string = g_string_append(string, _("off")); - break; - case 3: - string = g_string_append(string, _("auto")); - break; - } - - /* return light (bits 1, 2) */ - v = (n >> 1) & 0x03; - if (v == 2) string = append_comma_text(string, _("not detected by strobe")); - if (v == 3) string = append_comma_text(string, _("detected by strobe")); - - /* we ignore flash function (bit 5) */ - - /* red-eye (bit 6) */ - if ((n >> 5) & 0x01) string = append_comma_text(string, _("red-eye reduction")); - - text = string->str; - g_string_free(string, FALSE); - return text; + gint key_valid; + text = exif_get_formatted_by_key(exif, key, &key_valid); + if (key_valid) return g_list_append(NULL, text); } - if (strcmp(key, "fResolution") == 0) - { - ExifRational *rx, *ry; - gchar *units; - gchar *text; - rx = exif_get_rational(exif, "XResolution", NULL); - ry = exif_get_rational(exif, "YResolution", NULL); - if (!rx || !ry) return NULL; + item = exif_get_item(exif, key); + if (!item) return NULL; - units = exif_get_data_as_text(exif, "ResolutionUnit"); - text = g_strdup_printf("%0.f x %0.f (%s/%s)", rx->den ? (double)rx->num / rx->den : 1.0, - ry->den ? (double)ry->num / ry->den : 1.0, - _("dot"), (units) ? units : _("unknown")); + str = exif_item_get_data_as_text_full(item, format); - g_free(units); - return text; - } + if (!str) return NULL; - if (key_valid) *key_valid = FALSE; - return NULL; + return g_list_append(NULL, str); } -gchar *exif_get_data_as_text(ExifData *exif, const gchar *key) +typedef struct _UnmapData UnmapData; +struct _UnmapData { - ExifItem *item; - gchar *text; - gint key_valid; + guchar *ptr; + guchar *map_data; + size_t map_len; +}; - if (!key) return NULL; +static GList *exif_unmap_list = 0; - text = exif_get_formatted_by_key(exif, key, &key_valid); - if (key_valid) return text; +guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) +{ + guint offset; + const gchar* path; + struct stat st; + guchar *map_data; + size_t map_len; + int fd; - item = exif_get_item(exif, key); - if (item) return exif_item_get_data_as_text(item); + if (!exif) return NULL; + path = exif->path; - return NULL; -} + fd = open(path, O_RDONLY); -const gchar *exif_get_description_by_key(const gchar *key) -{ - gint i; - if (!key) return NULL; + if (fd == -1) + { + return 0; + } - i = 0; - while (ExifFormattedList[i].key != NULL) + if (fstat(fd, &st) == -1) { - if (strcmp(key, ExifFormattedList[i].key) == 0) return _(ExifFormattedList[i].description); - i++; + close(fd); + return 0; } + map_len = st.st_size; + map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); - i = 0; - while (ExifKnownMarkersList[i].tag > 0) + if (map_data == MAP_FAILED) { - if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return _(ExifKnownMarkersList[i].description); - i++; + return 0; } - return NULL; -} + if (format_raw_img_exif_offsets(map_data, map_len, &offset, NULL) && offset) + { + UnmapData *ud; -static void exif_write_item(FILE *f, ExifItem *item) -{ - gchar *text; + DEBUG_1("%s: offset %u", path, offset); - text = exif_item_get_data_as_text(item); - if (text) - { - fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name, - exif_item_get_tag_name(item), text); + *data_len = map_len - offset; + ud = g_new(UnmapData, 1); + ud->ptr = map_data + offset; + ud->map_data = map_data; + ud->map_len = map_len; + + exif_unmap_list = g_list_prepend(exif_unmap_list, ud); + return ud->ptr; } - g_free(text); + + munmap(map_data, map_len); + return NULL; + } -void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) +void exif_free_preview(guchar *buf) { - if (!f || !exif) return; - - fprintf(f, " tag format key value\n"); - fprintf(f, "----------------------------------------------------\n"); + GList *work = exif_unmap_list; - if (human_readable_list) + while (work) { - gint i; - - i = 0; - while (ExifFormattedList[i].key) + UnmapData *ud = (UnmapData *)work->data; + if (ud->ptr == buf) { - gchar *text; - - text = exif_get_formatted_by_key(exif, ExifFormattedList[i].key, NULL); - if (text) - { - fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text); - } - i++; + exif_unmap_list = g_list_remove_link(exif_unmap_list, work); + g_free(ud); + return; } + work = work->next; } - else - { - GList *work; - - work = exif->items; - while (work) - { - ExifItem *item; - - item = work->data; - work = work->next; + g_assert_not_reached(); +} - exif_write_item(f, item); - } - } - fprintf(f, "----------------------------------------------------\n"); +void exif_init(void) +{ } +#endif +/* not HAVE_EXIV2 */ +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */