X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=blobdiff_plain;f=src%2Fexif.c;h=872b9ccd65e54979ccc108845eb952b1af2975e1;hp=768d5bd5a867dedbc86de8171fc15d7789cafc03;hb=50f5ef3cedbeaa251a1e838bf5045a9dc7dda1ee;hpb=6974277ce5e41e0271523dbbd5ece82abeec678e diff --git a/src/exif.c b/src/exif.c index 768d5bd5..872b9ccd 100644 --- a/src/exif.c +++ b/src/exif.c @@ -1,12 +1,22 @@ /* - * GQView - * (C) 2006 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,28 +37,13 @@ * 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" @@ -64,13 +59,15 @@ #include #include #include - + #include +#include #include "intl.h" -#include "gqview.h" +#include "main.h" #include "exif-int.h" +#include "jpeg_parser.h" #include "format_raw.h" #include "ui_fileops.h" @@ -96,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 /* @@ -109,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") }, @@ -168,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 }; @@ -196,177 +224,186 @@ 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[] = { +{ 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", "Camera make", NULL }, -{ 0x0110, EXIF_FORMAT_STRING, -1, "Exif.Image.Model", "Camera model", NULL }, -{ 0x0112, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.Orientation", N_("Orientation"), ExifOrientationList }, -{ 0x011a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Image.XResolution", "X resolution", NULL }, -{ 0x011b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Image.YResolution", "Y Resolution", NULL }, -{ 0x0128, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.ResolutionUnit", "Resolution units", ExifUnitList }, -{ 0x0131, EXIF_FORMAT_STRING, -1, "Exif.Image.Software", "Firmware", 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", "White point", NULL }, -{ 0x013f, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "Exif.Image.PrimaryChromaticities","Primary chromaticities", NULL }, -{ 0x0211, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.Image.YCbCrCoefficients", "YCbCy coefficients", NULL }, -{ 0x0213, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Image.YCbCrPositioning", "YCbCr positioning", ExifYCbCrPosList }, -{ 0x0214, EXIF_FORMAT_RATIONAL_UNSIGNED, 6, "Exif.Image.ReferenceBlackWhite", "Black white reference", 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, "ExifOffset", "SubIFD Exif offset", NULL }, +{ 0x8769, EXIF_FORMAT_LONG_UNSIGNED, 1, "Exif.Image.ExifTag", N_("SubIFD Exif offset"), NULL }, /* subIFD follows */ -{ 0x829a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.ExposureTime", "Exposure time (seconds)", NULL }, -{ 0x829d, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FNumber", "FNumber", NULL }, +{ 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", "Spectral Sensitivity", NULL }, +{ 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", "Optoelectric conversion factor", NULL }, -{ 0x9000, EXIF_FORMAT_UNDEFINED, 4, "Exif.Photo.ExifVersion", "Exif version", 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","Pixel format", NULL }, -{ 0x9102, EXIF_FORMAT_RATIONAL_UNSIGNED,1, "Exif.Photo.CompressedBitsPerPixel","Compression ratio", 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", "Brightness", 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", "Maximum aperture", 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 }, +{ 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", "Subject area", NULL }, -{ 0x927c, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.MakerNote", "MakerNote", NULL }, -{ 0x9286, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.UserComment", "UserComment", NULL }, -{ 0x9290, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTime", "Subsecond time", NULL }, -{ 0x9291, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTimeOriginal", "Subsecond time original", NULL }, -{ 0x9292, EXIF_FORMAT_STRING, -1, "Exif.Photo.SubSecTimeDigitized", "Subsecond time digitized", NULL }, -{ 0xa000, EXIF_FORMAT_UNDEFINED, 4, "FlashPixVersion", "FlashPix version", NULL }, -{ 0xa001, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.ColorSpace", "Colorspace", ExifColorSpaceList }, +{ 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, "Exif.Photo.RelatedSoundFile", "Audio data", NULL }, -{ 0xa005, EXIF_FORMAT_LONG_UNSIGNED, 1, "ExifInteroperabilityOffset", "ExifR98 extension", NULL }, -{ 0xa20b, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FlashEnergy", "Flash strength", NULL }, -{ 0xa20c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.SpatialFrequencyResponse","Spatial frequency response", NULL }, -{ 0xa20e, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FocalPlaneXResolution", "X Pixel density", NULL }, -{ 0xa20f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.FocalPlaneYResolution", "Y Pixel density", NULL }, -{ 0xa210, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.FocalPlaneResolutionUnit", "Pixel density units", ExifUnitList }, -{ 0x0214, EXIF_FORMAT_SHORT_UNSIGNED, 2, "Exif.Photo.SubjectLocation", "Subject location", 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", "Sensor type", ExifSensorList }, -{ 0xa300, EXIF_FORMAT_UNDEFINED, 1, "Exif.Photo.FileSource", "Source type", ExifSourceList }, -{ 0xa301, EXIF_FORMAT_UNDEFINED, 1, "Exif.Photo.SceneType", "Scene type", ExifSceneList }, -{ 0xa302, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.CFAPattern", "Color filter array pattern", 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, "Exif.Photo.CustomRendered", "Render process", ExifCustRenderList }, -{ 0xa402, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.ExposureMode", "Exposure mode", ExifExposureModeList }, -{ 0xa403, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.WhiteBalance", "White balance", ExifWhiteBalanceList }, -{ 0xa404, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.Photo.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, "Exif.Photo.GainControl", "Gain control", ExifGainControlList }, -{ 0xa408, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Contrast", "Contrast", ExifContrastList }, -{ 0xa409, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Saturation", "Saturation", ExifSaturationList }, -{ 0xa40a, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.Sharpness", "Sharpness", ExifSharpnessList }, -{ 0xa40b, EXIF_FORMAT_UNDEFINED, -1, "Exif.Photo.DeviceSettingDescription","Device setting", NULL }, -{ 0xa40c, EXIF_FORMAT_SHORT_UNSIGNED, 1, "Exif.Photo.SubjectDistanceRange", "Subject range", ExifSubjectRangeList }, -{ 0xa420, EXIF_FORMAT_STRING, -1, "Exif.Photo.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, "Exif.Image.NewSubfileType", NULL, NULL }, -{ 0x00ff, EXIF_FORMAT_SHORT_UNSIGNED, 1, "SubfileType", 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 }, @@ -380,7 +417,7 @@ ExifMarker ExifKnownMarkersList[] = { { 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, "ColorProfile", 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 }, @@ -397,6 +434,44 @@ ExifMarker ExifKnownMarkersList[] = { 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 }, @@ -431,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) { @@ -441,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; @@ -456,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; @@ -474,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; } @@ -491,7 +564,7 @@ static void exif_item_free(ExifItem *item) g_free(item); } -char *exif_item_get_tag_name(ExifItem *item) +gchar *exif_item_get_tag_name(ExifItem *item) { if (!item || !item->marker) return NULL; return g_strdup(item->marker->key); @@ -499,36 +572,39 @@ char *exif_item_get_tag_name(ExifItem *item) guint exif_item_get_tag_id(ExifItem *item) { + if (!item) return 0; return item->tag; } guint exif_item_get_elements(ExifItem *item) { + if (!item) return 0; return item->elements; } -char *exif_item_get_data(ExifItem *item, guint *data_len) +gchar *exif_item_get_data(ExifItem *item, guint *data_len) { if (data_len) *data_len = item->data_len; - return item->data; + 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; } -char *exif_item_get_description(ExifItem *item) +gchar *exif_item_get_description(ExifItem *item) { if (!item || !item->marker) return NULL; return g_strdup(_(item->marker->description)); } -const char *exif_item_get_format_name(ExifItem *item, gint brief) +const gchar *exif_item_get_format_name(ExifItem *item, gboolean brief) { - if (!item || !item->marker) return NULL; + if (!item || !item->marker) return NULL; return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description; } @@ -538,7 +614,7 @@ static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne) 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); } @@ -561,7 +637,7 @@ 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]); } return string; @@ -576,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")); @@ -593,7 +669,7 @@ gchar *exif_text_list_find_value(ExifTextList *list, guint value) /* note: the align_buf is used to avoid alignment issues (on sparc) */ -guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo) +guint16 exif_byte_get_int16(guchar *f, ExifByteOrder bo) { guint16 align_buf; @@ -605,7 +681,7 @@ guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo) 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; @@ -617,7 +693,7 @@ guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo) return GUINT32_FROM_BE(align_buf); } -void exif_byte_put_int16(unsigned char *f, guint16 n, ExifByteOrder bo) +void exif_byte_put_int16(guchar *f, guint16 n, ExifByteOrder bo) { guint16 align_buf; @@ -633,7 +709,7 @@ void exif_byte_put_int16(unsigned char *f, guint16 n, ExifByteOrder bo) memcpy(f, &align_buf, sizeof(guint16)); } -void exif_byte_put_int32(unsigned char *f, guint32 n, ExifByteOrder bo) +void exif_byte_put_int32(guchar *f, guint32 n, ExifByteOrder bo) { guint32 align_buf; @@ -670,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); @@ -679,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; @@ -695,7 +771,7 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len, ExifFormatList[src_format].size * ne > len) { gchar *tag = exif_item_get_tag_name(item); - printf("exif tag %s data size mismatch\n", tag); + log_printf("exif tag %s data size mismatch\n", tag); g_free(tag); return; } @@ -712,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: @@ -764,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) @@ -797,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 */ @@ -820,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); } @@ -836,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); } @@ -846,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; } } @@ -868,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; @@ -878,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) @@ -912,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; @@ -940,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; @@ -950,6 +1029,7 @@ 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 @@ -969,137 +1049,27 @@ gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker 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. + 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 exif_jpeg_segment_find(unsigned char *data, guint size, - guchar app_marker, const gchar *magic, guint magic_len, - guint *seg_offset, guint *seg_length) +void exif_add_jpeg_color_profile(ExifData *exif, guchar *cp_data, guint cp_length) { - guchar marker = 0; - guint offset = 0; - guint length = 0; - - while (marker != app_marker && - marker != JPEG_MARKER_EOI) - { - offset += length; - length = 2; - - if (offset + 2 >= size || - data[offset] != JPEG_MARKER) return FALSE; - - marker = data[offset + 1]; - if (marker != JPEG_MARKER_SOI && - marker != JPEG_MARKER_EOI) - { - if (offset + 4 >= size) return FALSE; - length += exif_byte_get_int16(data + offset + 2, EXIF_BYTE_ORDER_MOTOROLA); - } - } - - if (marker == app_marker && - offset + length < size && - length >= 4 + magic_len && - memcmp(data + offset + 4, magic, magic_len) == 0) - { - *seg_offset = offset + 4; - *seg_length = length - 4; - return TRUE; - } - - return FALSE; -} - -static ExifMarker jpeg_color_marker = { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "ColorProfile", NULL, NULL }; - -static gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size) -{ - guint seg_offset = 0; - guint seg_length = 0; - guint chunk_offset[255]; - guint chunk_length[255]; - guint chunk_count = 0; - - /* For jpeg/jfif, ICC color profile data can be in more than one segment. - the data is in APP2 data segments that start with "ICC_PROFILE\x00\xNN\xTT" - NN = segment number for data - TT = total number of ICC segments (TT in each ICC segment should match) - */ - - while (exif_jpeg_segment_find(data + seg_offset + seg_length, - size - seg_offset - seg_length, - JPEG_MARKER_APP2, - "ICC_PROFILE\x00", 12, - &seg_offset, &seg_length)) - { - guchar chunk_num; - guchar chunk_tot; - - if (seg_length < 14) return FALSE; - - chunk_num = data[seg_offset + 12]; - chunk_tot = data[seg_offset + 13]; - - if (chunk_num == 0 || chunk_tot == 0) return FALSE; - - if (chunk_count == 0) - { - guint i; - - chunk_count = (guint)chunk_tot; - for (i = 0; i < chunk_count; i++) chunk_offset[i] = 0; - for (i = 0; i < chunk_count; i++) chunk_length[i] = 0; - } - - if (chunk_tot != chunk_count || - chunk_num > chunk_count) return FALSE; - - chunk_num--; - chunk_offset[chunk_num] = seg_offset + 14; - chunk_length[chunk_num] = seg_length - 14; - } - - if (chunk_count > 0) - { - ExifItem *item; - unsigned char *cp_data; - guint cp_length = 0; - guint i; - - for (i = 0; i < chunk_count; i++) cp_length += chunk_length[i]; - cp_data = g_malloc(cp_length); - - for (i = 0; i < chunk_count; i++) - { - if (chunk_offset[i] == 0) - { - /* error, we never saw this chunk */ - g_free(cp_data); - return FALSE; - } - memcpy(cp_data, data + chunk_offset[i], chunk_length[i]); - } - - item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1, + 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); - - return TRUE; - } + 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); - return FALSE; } static gint exif_jpeg_parse(ExifData *exif, - unsigned char *data, guint size, - ExifMarker *list, gint parse_color) + guchar *data, guint size, + ExifMarker *list) { guint seg_offset = 0; guint seg_length = 0; @@ -1111,15 +1081,14 @@ static gint exif_jpeg_parse(ExifData *exif, return -2; } - if (exif_jpeg_segment_find(data, size, JPEG_MARKER_APP1, + 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); } - if (parse_color && - exif_jpeg_parse_color(exif, data, size)) + if (exif_jpeg_parse_color(exif, data, size)) { res = 0; } @@ -1127,6 +1096,26 @@ static gint exif_jpeg_parse(ExifData *exif, return res; } +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; +} + + +gchar* exif_get_image_comment(FileData* fd) +{ + log_printf("%s", _("Can't get image comment: not compiled with Exiv2.\n")); + return g_strdup(""); +} + +void exif_set_image_comment(FileData* fd, const gchar* comment) +{ + log_printf("%s", _("Can't set image comment: not compiled with Exiv2.\n")); +} + /* *------------------------------------------------------------------- @@ -1137,7 +1126,7 @@ static gint exif_jpeg_parse(ExifData *exif, ExifItem *exif_get_first_item(ExifData *exif) { - if (exif->items) + if (exif->items) { ExifItem *ret = (ExifItem *)exif->items->data; exif->current = exif->items->next; @@ -1149,7 +1138,7 @@ ExifItem *exif_get_first_item(ExifData *exif) ExifItem *exif_get_next_item(ExifData *exif) { - if (exif->current) + if (exif->current) { ExifItem *ret = (ExifItem *)exif->current->data; exif->current = exif->current->next; @@ -1158,14 +1147,13 @@ ExifItem *exif_get_next_item(ExifData *exif) return NULL; } - - -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; @@ -1180,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); @@ -1191,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) { @@ -1202,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; @@ -1217,14 +1211,15 @@ void exif_free(ExifData *exif) } g_list_free(exif->items); + g_free(exif->path); g_free(exif); } -ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) +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; @@ -1238,14 +1233,12 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) g_free(pathl); exif = g_new0(ExifData, 1); - exif->items = NULL; - exif->current = NULL; + exif->path = g_strdup(path); - if ((res = exif_jpeg_parse(exif, (unsigned char *)f, size, - ExifKnownMarkersList, - parse_color_profile)) == -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) @@ -1261,16 +1254,16 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) default: break; case FORMAT_RAW_EXIF_TIFF: - res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, + res = exif_tiff_parse(exif, (guchar *)f + offset, size - offset, ExifKnownMarkersList); break; case FORMAT_RAW_EXIF_JPEG: - res = exif_jpeg_parse(exif, (unsigned char*)f + offset, size - offset, - ExifKnownMarkersList, FALSE); + 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, (unsigned char*)f, offset, size - offset, + 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); @@ -1278,7 +1271,7 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) case FORMAT_RAW_EXIF_PROPRIETARY: if (exif_parse_func) { - res = exif_parse_func((unsigned char*)f + offset, size - offset, exif); + res = exif_parse_func((guchar *)f + offset, size - offset, exif); } break; } @@ -1294,11 +1287,6 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) 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; } @@ -1310,25 +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; } #define EXIF_DATA_AS_TEXT_MAX_COUNT 16 -gchar *exif_item_get_string(ExifItem *item, int idx) -{ - return exif_item_get_data_as_text(item); -} - -gchar *exif_item_get_data_as_text(ExifItem *item) +static gchar *exif_item_get_data_as_text_full(ExifItem *item, MetadataFormat format) { const ExifMarker *marker; gpointer data; @@ -1353,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); @@ -1378,10 +1361,10 @@ 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; @@ -1399,7 +1382,7 @@ gchar *exif_item_get_data_as_text(ExifItem *item) for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", - (unsigned long int)((guint32 *)data)[i]); + (gulong)((guint32 *)data)[i]); } break; case EXIF_FORMAT_RATIONAL_UNSIGNED: @@ -1409,7 +1392,7 @@ 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: @@ -1423,7 +1406,7 @@ gchar *exif_item_get_data_as_text(ExifItem *item) for (i = 0; i < ne; i++) { g_string_append_printf(string, "%s%ld", (i > 0) ? ", " : "", - (long int)((gint32 *)data)[i]); + (glong)((gint32 *)data)[i]); } break; case EXIF_FORMAT_RATIONAL: @@ -1433,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: @@ -1447,7 +1430,7 @@ 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; } @@ -1464,9 +1447,20 @@ gchar *exif_item_get_data_as_text(ExifItem *item) 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) { @@ -1494,23 +1488,22 @@ gint exif_item_get_integer(ExifItem *item, gint *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; } - - -const gchar *exif_get_tag_description_by_key(const gchar *key) +gchar *exif_get_tag_description_by_key(const gchar *key) { gint i; @@ -1519,10 +1512,17 @@ const gchar *exif_get_tag_description_by_key(const gchar *key) i = 0; while (ExifKnownMarkersList[i].tag > 0) { - if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return _(ExifKnownMarkersList[i].description); + if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return g_strdup(_(ExifKnownMarkersList[i].description)); i++; } + i = 0; + while (ExifKnownGPSInfoMarkersList[i].tag > 0) + { + if (strcmp(key, ExifKnownGPSInfoMarkersList[i].key) == 0) return g_strdup(_(ExifKnownGPSInfoMarkersList[i].description)); + i++; + } + return NULL; } @@ -1534,7 +1534,7 @@ static void exif_write_item(FILE *f, ExifItem *item) if (text) { gchar *tag = exif_item_get_tag_name(item); - fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name, + g_fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name, tag, text); g_free(tag); } @@ -1545,8 +1545,8 @@ void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) { if (!f || !exif) return; - fprintf(f, " tag format key value\n"); - fprintf(f, "----------------------------------------------------\n"); + g_fprintf(f, " tag format key value\n"); + g_fprintf(f, "----------------------------------------------------\n"); if (human_readable_list) { @@ -1560,7 +1560,7 @@ void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) text = exif_get_formatted_by_key(exif, ExifFormattedList[i].key, NULL); if (text) { - fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text); + g_fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text); } i++; } @@ -1580,31 +1580,142 @@ void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list) exif_write_item(f, item); } } - fprintf(f, "----------------------------------------------------\n"); + g_fprintf(f, "----------------------------------------------------\n"); } -int exif_write(ExifData *exif) +gboolean exif_write(ExifData *exif) { - printf("Not compiled with EXIF write support"); - return 0; + log_printf("Not compiled with EXIF write support\n"); + return FALSE; } -ExifItem *exif_add_item(ExifData *exif, const gchar *key) +gboolean exif_write_sidecar(ExifData *exif, gchar *path) { - return NULL; + log_printf("Not compiled with EXIF write support\n"); + return FALSE; } -int exif_item_delete(ExifData *exif, ExifItem *item) + +gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values) { return 0; } -int exif_item_set_string(ExifItem *item, const char *str) +GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format) { - return 0; + gchar *str; + ExifItem *item; + + if (!key) return NULL; + + /* convert xmp key to exif key */ + if (strcmp(key, "Xmp.tiff.Orientation") == 0) key = "Exif.Image.Orientation"; + + if (format == METADATA_FORMATTED) + { + gchar *text; + gint key_valid; + text = exif_get_formatted_by_key(exif, key, &key_valid); + if (key_valid) return g_list_append(NULL, text); + } + + item = exif_get_item(exif, key); + if (!item) return NULL; + + str = exif_item_get_data_as_text_full(item, format); + + if (!str) return NULL; + + return g_list_append(NULL, str); +} + +typedef struct _UnmapData UnmapData; +struct _UnmapData +{ + guchar *ptr; + guchar *map_data; + size_t map_len; +}; + +static GList *exif_unmap_list = 0; + +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; + + if (!exif) return NULL; + path = exif->path; + + fd = open(path, O_RDONLY); + + + if (fd == -1) + { + return 0; + } + + if (fstat(fd, &st) == -1) + { + close(fd); + return 0; + } + map_len = st.st_size; + map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (map_data == MAP_FAILED) + { + return 0; + } + + if (format_raw_img_exif_offsets(map_data, map_len, &offset, NULL) && offset) + { + UnmapData *ud; + + DEBUG_1("%s: offset %u", path, offset); + + *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; + } + + munmap(map_data, map_len); + return NULL; + } +void exif_free_preview(guchar *buf) +{ + GList *work = exif_unmap_list; + while (work) + { + UnmapData *ud = (UnmapData *)work->data; + if (ud->ptr == buf) + { + exif_unmap_list = g_list_remove_link(exif_unmap_list, work); + g_free(ud); + return; + } + work = work->next; + } + g_assert_not_reached(); +} -#endif +void exif_init(void) +{ +} + +#endif /* not HAVE_EXIV2 */ +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */