/* /-------------------------------------------------------------------- | | $Id: plexif.cpp,v 1.10 2004/09/11 12:41:35 uzadow Exp $ | | Copyright (c) 1996-2003 Ulrich von Zadow | | Implementation of PLExif class by Mike Franklin | Much of the decoding code was adapted from code by Ken Reneris: | http://www.reneris.com/tools/exif.asp - | though it has been heavily modified! | | \-------------------------------------------------------------------- */ #include "config.h" #include "plexif.h" #include "pldebug.h" #include "plexcept.h" #include #include #include #include #include #include extern "C" { #include "jpeglib.h" } using namespace std; struct _PLExifTranslator { int Value; char * Desc; }; struct _PLExifTagValues { PLUINT Tag; // The tag # char * ShortName; // Short name for the tag _PLExifTranslator *Trans; // Translate values / units void (PLExifTag::*Convert)(std::string &); // convert to common format char * Desc; // Normal name for the tag }; struct _PLExifFormatter { PLUINT Size; // Size of each item in this format void (*Swizzle)(PLBYTE *Data); // Swizzle the data size_t (PLExifTag:: *Render)(PLBYTE * & Buffer); }; // place all these globals in an anonymous namespace // so they are only visible (accessible) to this // compilation unit (source file) namespace { const int VALUE_UNIT = -1; const int VALUE_LOWER_STR = -2; enum ExifType {etUint8 = 1, etAsciiStr, etUint16, etUint32, etUrat64, etInt8, etInt16, etInt32, etRat64}; _PLExifTranslator OffOn[] = { 0, "Off", 1, "On", 0, NULL }; _PLExifTranslator OnOff[] = { 0, "On", 1, "Off", 0, NULL }; _PLExifTranslator DisableEnable[] = { 0, "Disable", 1, "Enable", 0, NULL }; _PLExifTranslator EnableDisable[] = { 0, "Enable", 1, "Disable", 0, NULL }; _PLExifTranslator ResUnit[] = { 1, "no-unit", 2, "inch", 3, "cm", 0, NULL }; _PLExifTranslator FPResUnit[] = { 1, "inch", 2, "inch", // should be meter, but broke on some cameras? 3, "centimeter", 4, "milimemeter", 5, "micrometer", 0, NULL }; _PLExifTranslator ExpProg[] = { 1, "Manual", 2, "Program", 3, "Aperture priority", 4, "Shutter priority", 5, "Slow", 6, "Action", 7, "Portrait", 8, "Landscape", 0, NULL }; _PLExifTranslator BpsCompress[] = { 1, "Basic", 2, "Normal", 3, "Fine", 4, "Fine", 0, NULL }; _PLExifTranslator MeterMode[] = { 0, "Unknown", 1, "Average", 2, "Center weighted", 3, "Spot", 4, "Multi-spot", 5, "Multi-segment", 6, "Partial", 255,"Other", 0, NULL }; _PLExifTranslator LightSource[] = { 0, "Unknown/cloudy", 1, "Daylight", 2, "Fluorescent", 3, "Tungsten", 10, "Flash", 17, "Standard light A", 18, "Standard light B", 19, "Standard light C", 20, "D55", 21, "D65", 22, "D75", 0, NULL }; _PLExifTranslator FlashUsed[] = { 0, "No", 1, "Yes", 5, "Yes, no return detect", 7, "Yes, return detected", 9, "Yes", // maybe should be more than just yes ? 16, "No forced", // not sure about this one! 0, NULL }; _PLExifTranslator ColorSpace[] = { 1, "sRGB", 65535, "Uncalibrated", 0, NULL }; _PLExifTranslator SenseMethod[] = { 2, "1 chip color area", 0, NULL }; _PLExifTranslator FileSource[] = { 3, "Digital still camera", 0, NULL }; _PLExifTranslator SceneType[] = { 1, "Directly photographed", 0, NULL }; _PLExifTranslator N2Quality[] = { 1, "VGA Basic", 2, "VGA Normal", 3, "VGA Fine", 4, "SXGA Basic", 5, "SXGA Normal", 6, "SXGA Fine", 12,"UXGA Fine", 20,"HI (Uncompressed)", 0, NULL }; _PLExifTranslator N2Color[] = { 1, "Colour", 2, "Monochrome", 0, NULL }; _PLExifTranslator N2ImgAdjust[] = { 0, "Normal", 1, "Bright+", 2, "Bright-", 3, "Contrast+", 4, "Contrast-", 0, NULL }; _PLExifTranslator N2Iso[] = { 0, "80", 2, "160", 4, "320", 5, "100", 0, NULL }; _PLExifTranslator N2WhiteBal[] = { 0, "Auto", 1, "Preset", 2, "Daylight", 3, "Incandescent", 4, "Flourescent", 5, "Cloudy", 6, "SpeedLight", 0, NULL }; _PLExifTranslator N2Converter[] = { 0, "None", 1, "Fisheye", 0, NULL }; _PLExifTranslator OlyJpegQ[] = { 1, "SQ", 2, "HQ", 3, "SHQ", 0, NULL }; _PLExifTranslator OlyMacro[] = { 0, "Normal", 1, "Macro", 0, NULL }; _PLExifTranslator OlySharp[] = { 0, "Normal", 1, "Hard", 2, "Soft", 0, NULL }; _PLExifTranslator OlyFlash[] = { 0, "On", 1, "Red-eye", 2, "Fill", 2, "Off", 0, NULL }; _PLExifTranslator CanMacro[] = { 1, "Macro", 2, "Off", 0, NULL }; _PLExifTranslator CanFlashMode[] = { 0, "Off", 1, "Auto", 2, "On", 3, "Red-eye", 4, "Slow", 5, "Auto + red-eye", 6, "On + red-eye", 16, "External", 0, NULL }; _PLExifTranslator CanDrvMode[] = { 0, "Single/self timer", 1, "Continuous", 0, NULL }; _PLExifTranslator CanFocusMode[] = { 0, "One-shot", 1, "AI servo", 2, "AI Focus", 3, "Manual", 4, "Single", 5, "Continuous", 6, "Manual", 0, NULL }; _PLExifTranslator CanImgSize[] = { 0, "Large", 1, "Medium", 2, "Small", 0, NULL }; _PLExifTranslator CanShootMode[] = { 0, "Full auto", 1, "Manual", 2, "Landscape", 3, "Fast shutter", 4, "Slow shutter", 5, "Night", 6, "B & W", 7, "Sepia", 8, "Portrait", 9, "Sports", 10, "Macro", 11, "Pan Focus", 0, NULL }; _PLExifTranslator CanLNH[] = { 0xFFFF, "Low", 0, "Normal", 1, "High", 0, NULL }; _PLExifTranslator CanIso[] = { 0, "Not supplied use EXIF tag", 15, "Auto", 16, "50", 17, "100", 18, "200", 19, "400", 0, NULL }; _PLExifTranslator CanMeter[] = { 3, "Evaluative", 4, "Partial", 5, "Center-weighted", 0, NULL }; _PLExifTranslator CanWhiteBal[] = { 0, "Auto", 1, "Sunny", 2, "Cloudy", 3, "Tungsten", 4, "Flourescent", 5, "Flash", 6, "Custom", 0, NULL }; _PLExifTranslator CanFlashBias[] = { 0xFFC0, "-2 EV", 0xFFCC, "-1.67 EV", 0xFFD0, "-1.50 EV", 0xFFD4, "-1.33 EV", 0xFFE0, "-1 EV", 0xFFEC, "-0.67 EV", 0xFFF0, "-0.50 EV", 0xFFF4, "-0.33 EV", 0x0000, "0 EV", 0x000C, "0.33 EV", 0x0010, "0.50 EV", 0x0014, "0.67 EV", 0x0020, "1 EV", 0x002C, "1.33 EV", 0x0030, "1.50 EV", 0x0034, "1.67 EV", 0x0040, "2 EV", 0, NULL }; _PLExifTranslator CanAFPoint[] = { 0x3000, "MF", 0x3001, "Auto selected", 0x3002, "Right", 0x3003, "Center", 0x3004, "Left", 0, NULL }; _PLExifTranslator CFnLock[] = { 0, "AF/AE Lock", 1, "AE Lock/AF", 2, "AF/AF Lock", 3, "AE+release/AE+AF", 0, NULL }; _PLExifTranslator CFnTVAV[] = { 0, "1/2 stop", 1, "1/3 stop", 0, NULL }; _PLExifTranslator CFnAFAssist[] = { 0, "On (auto)", 1, "Off", 2, "External only", 0, NULL }; _PLExifTranslator CFnShutAv[] = { 0, "Auto", 1, "1/200(fixed)", 0, NULL }; _PLExifTranslator CFnAeb[] = { 0, "0,-,+/Enabled", 1, "0,-,+/Disabled", 2, "-,0,+/Enabled", 3, "-,0,+/Disabled", 0, NULL }; _PLExifTranslator CFnShutSync[] = { 0, "1st-curtain sync", 1, "2nd-curtain sync", 0, NULL }; _PLExifTranslator CFnAFStop[] = { 0, "AF Stop", 1, "Operate AF", 2, "Lock AE & start timer", 0, NULL }; _PLExifTranslator CFnMenu[] = { 0, "Top", 1, "Previous(volatile)", 2, "Previous", 0, NULL }; _PLExifTranslator CFnSet[] = { 0, "Not assigned", 1, "Change quality", 2, "Change ISO", 3, "Select Params", 0, NULL }; _PLExifTranslator FujiSharp[] = { 1, "Soft", 2, "Soft", 3, "Normal", 4, "Hard", 5, "Hard", 0, NULL }; _PLExifTranslator FujiWBal[] = { 0x0000, "Auto", 0x0100, "Daylight", 0x0200, "Cloudy", 0x0300, "DaylightColor-fluorescent", 0x0301, "DaywhiteColor-fluorescent", 0x0302, "White-fluorescent", 0x0400, "Incandescent", 0x0F00, "Custom", 0, NULL }; _PLExifTranslator FujiColor[] = { 0x0000, "Normal", 0x0100, "High", 0x0200, "Low", 0x0300, "Black & White", 0, NULL }; _PLExifTranslator FujiTone[] = { 0x0000, "Normal", 0x0100, "High", 0x0200, "Low", 0, NULL }; _PLExifTranslator FujiFlashMode[] = { 0, "Auto", 1, "On", 2, "Off", 3, "Red-eye", 0, NULL }; _PLExifTranslator FujiFlashStrg[] = { 0, "Auto", 1, "On", 2, "Off", 3, "Red-eye", 0, NULL }; _PLExifTranslator FujiMacro[] = { 0, "Off", 1, "Macro", 2, "Super Macro", 0, NULL }; _PLExifTranslator FujiFocus[] = { 0, "Auto", 1, "Manual", 0, NULL }; _PLExifTranslator FujiPicture[] = { 0x0000, "Auto", 0x0001, "Portrait", 0x0002, "Landscape", 0x0004, "Sports", 0x0005, "Night", 0x0006, "Program AE", 0x0100, "Aperture Prior AE", 0x0200, "Shutter Prior AE", 0x0300, "Manual exposure", 0, NULL }; _PLExifTranslator FujiBlurFocus[] = { 0, "OK", 1, "Warning", 0, NULL }; _PLExifTranslator FujiAE[] = { 0, "OK", 1, "Over-exposed", 0, NULL }; _PLExifTranslator CasioRecord[] = { 1, "Single Shutter", 2, "Panorama", 3, "Night Scene", 4, "Portrait", 5, "Landscape", 7, "Panorama", 10, "Night Scene", 15, "Portrait", 16, "Landscape", 0, NULL }; _PLExifTranslator CasioQuality[] = { 1, "Economy", 2, "Normal", 3, "Fine", 0, NULL }; _PLExifTranslator CasioFocus[] = { 2, "Macro", 3, "Auto Focus", 4, "Manual Focus", 5, "Infinity", 0, NULL }; _PLExifTranslator CasioFlashMode[] = { 1, "Auto", 2, "On", 3, "Off", 4, "Off", // on QV2000 this is Red Eye Reduction must be fixed as a special case :( 5, "Red Eye Reduction", 0, NULL }; _PLExifTranslator CasioFlashInt[] = { 11, "Weak", 13, "Normal", 14, "High", 15, "Strong", 0, NULL }; _PLExifTranslator CasioWhite[] = { 1, "Auto", 2, "Tungsten", 3, "Daylight", 4, "Fluorescent", 5, "Shade", 9, "Manual", 0, NULL }; _PLExifTranslator CasioZoom[] = { 0x10000, "Off", 0x10001, "2X", // QV2000 0x20000, "2X", // QV8000 0x40000, "4X", // QV8000 0, NULL }; _PLExifTranslator CasioSharp[] = { 0, "Normal", 1, "Soft", 2, "Hard", 0, NULL }; _PLExifTranslator CasioLowHi[] = { 0, "Normal", 1, "Low", 2, "High", 0, NULL }; _PLExifTranslator CasioSens[] = { 64, "Normal", // QV3k 80, "Normal", // QV8k/2k 100, "High", // 8k/2k 125, "+1.0", // 3k 250, "+2.0", // 3k 244, "+3.0", // 3k 9, "Manual", 0, NULL }; _PLExifTranslator CasioEnhance[] = { 1, "Off", 2, "Red", 3, "Green", 4, "Blue", 5, "Flesh Tones", 0, NULL }; _PLExifTranslator CasioFFrmLoc[] = { 1, "Centre", 2, "Scene S1", 3, "Scene S2", 4, "Scene S3", 5, "Scene S4", 6, "Scene S5", 7, "Scene S8", 8, "Scene S6", 9, "Scene S7", 10, "Top Left", 11, "Top Middle", 12, "Top Right", 13, "Middle Left", 14, "Middle Right", 15, "Bottom Left", 16, "Bottom Middle", 17, "Bottom Right", 0, NULL }; _PLExifTranslator CasioFilter[] = { 1, "Off", 2, "Black & White", 3, "Sepia", 4, "Red", 5, "Green", 6, "Blue", 7, "Yellow", 8, "Pink", 9, "Purple", 0, NULL }; _PLExifTranslator CasioMeter[] = { 2, "Centre", 3, "Spot", 5, "Multi", 0, NULL }; _PLExifTranslator CasioEVShift[] = { -6, "-2.0 EV", -5, "-1.67 EV", -4, "-1.33 EV", -3, "-1.0 EV", -2, "-0.67 EV", -1, "-0.33 EV", 0, "0 EV", 1, "+0.33 EV", 2, "+0.67 EV", 3, "+1.0 EV", 4, "+1.33 EV", 5, "+1.67 EV", 6, "+2.0 EV", 0, NULL }; _PLExifTranslator CasioAperture[] = { 1, "f2.0", 2, "f2.3", 3, "f2.8", 4, "f4.0", 5, "f5.6", 6, "f8.0", 15, "f8.0", 0, NULL }; _PLExifTranslator CasioRecord2[] = { 0, "Normal", 1, "Portrait", 2, "Landscape", 3, "Night Scene", 4, "Slow Shutter", 0, NULL }; _PLExifTranslator CasioComp[] = { 1, "Off", 2, "Scene S1", 3, "Scene S2", 4, "Scene S3", 5, "Scene S4", 6, "Scene S5", 7, "Scene S8", 8, "Scene S6", 9, "Scene S7", 0, NULL }; _PLExifTranslator CasioExp[] = { 1, "Manual", 2, "Program AE", 3, "Aperture Priority", 4, "Shutter Priority", 0, NULL }; // Units _PLExifTranslator UnitsSec[] = { VALUE_UNIT, "s" }; _PLExifTranslator UnitsM[] = { VALUE_UNIT, "m" }; _PLExifTranslator UnitsMM[] = { VALUE_UNIT, "mm" }; _PLExifTranslator UnitsX[] = { VALUE_UNIT, "x" }; _PLExifTranslator LowerStr[] = { VALUE_LOWER_STR, NULL }; PLUINT rgMask[] = { 0, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFF }; static string Nullstring; // Internal EXIF exception struct PLExifException { PLExifException(const string & m) : m_message(m) {} string m_message; }; struct PLExifFormatException : public PLExifException { PLExifFormatException(const string & m) : PLExifException(m) {} }; struct PLExifTagNoException : public PLExifException { PLExifTagNoException(const string & m) : PLExifException(m) {} }; struct PLExifNoCompException : public PLExifException { PLExifNoCompException(const string & m) : PLExifException(m) {} }; } // end of anonymous namespace (see comment at top) // tag arrays contain details of all known tags // declared as static members in PLExifTag so they have access // to the private conversion functions declared in PLExifTag _PLExifTagValues PLExifTag::MainTags[] = { // Prefix Main 0x010E, "Desc", NULL, NULL, "Image Description", 0x010F, "Make", NULL, NULL, "Camera Make", 0x0110, "Model", NULL, NULL, "Camera Model", 0x0112, "Ori", NULL, NULL, "Orientation", 0x011A, "XRes", NULL, NULL, "X Resolution", 0x011B, "YRes", NULL, NULL, "Y Resolution", 0x0128, "ResUnit", ResUnit, NULL, "Resolution Unit", 0x0131, "Software", NULL, NULL, "Camera Software", 0x0132, "ModTime", NULL, NULL, "Last Modification", 0x013E, "WPoint", NULL, NULL, "White Point", 0x013F, "PrimChr", NULL, NULL, "Primary Chromaticities", 0x0211, "YCbCrCoef", NULL, NULL, "YCbCrCoefficients", 0x0213, "YCbCrPos", NULL, NULL, "YCbCrPositioning", 0x0214, "RefBW", NULL, NULL, "Reference Black/White point", 0x8298, "Copy", NULL, NULL, "Copyright", 0x8769, "ExifOffset", NULL, NULL, "Sub IFD Offset", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::SubTags[] = { // Prefix Sub 0x829A, "Shutter", UnitsSec, &PLExifTag::CnvFrac, "Exposure Time", 0x829D, "FStop", NULL, &PLExifTag::CnvRatAp, "F-Stop", 0x8822, "Prog", ExpProg, NULL, "Program", 0x8827, "Iso", NULL, NULL, "Equivalent ISO speed", 0x9000, "ExifVer", NULL, NULL, "Exif Version", 0x9003, "OrigTime", NULL, NULL, "Original Time", 0x9004, "DigTime", NULL, NULL, "Digitized Time", 0x9101, "CompConfig", NULL, &PLExifTag::CnvCompCfg, "Components Configuration", 0x9102, "Bpp", BpsCompress,NULL, "Average compression ratio", 0x9201, "Shuttera", NULL, &PLExifTag::CnvApexShutter, "Shutter Speed", 0x9202, "Aperturea", NULL, &PLExifTag::CnvApexAp, "Aperture", 0x9203, "Brighta", NULL, &PLExifTag::CnvRat, "Brightness APEX", 0x9204, "Expbiasa", NULL, &PLExifTag::CnvRat, "Exposure Bias APEX", 0x9205, "Maxapa", NULL, &PLExifTag::CnvApexAp, "Maximum Aperture APEX", 0x9206, "Dist", UnitsM, &PLExifTag::CnvRat, "Subject Distance", 0x9207, "Meter", MeterMode, NULL, "Metering Mode", 0x9208, "Lights", LightSource,NULL, "Light Source", 0x9209, "Flash", FlashUsed, NULL, "Flash Used", 0x920a, "Focal", UnitsMM, &PLExifTag::CnvRat, "Focal Length", 0x927c, "Maker", NULL, NULL, "Maker Note", 0x9286, "User", NULL, NULL, "User Comment", 0x9290, "STime", NULL, NULL, "Subsecond Time", 0x9291, "SOrigTime", NULL, NULL, "Subsecond Original Time", 0x9292, "SDigTime", NULL, NULL, "Subsecond Digitized Time", 0xA000, "Flashpix", NULL, NULL, "Flash Pix Version", 0xA001, "ColorSpace", ColorSpace, NULL, "Color Space", 0xA002, "Width", NULL, NULL, "Image Width", 0xA003, "Height", NULL, NULL, "Image Height", 0xA004, "SndFile", NULL, NULL, "Sound File", 0xA005, "ExifIntOff", NULL, NULL, "Exif Interoperability Offset", 0xA20E, "FPXRes", NULL, &PLExifTag::CnvRat, "Focal Plane X Resolution", 0xA20F, "FPYRes", NULL, &PLExifTag::CnvRat, "Focal Plane Y Resolution", 0xA210, "FPResUnit", FPResUnit, NULL, "Focal Plane Unit", 0xA215, "ExpIndex", NULL, &PLExifTag::CnvRat, "Exposure Index", 0xA217, "SenseMethod", SenseMethod,NULL, "Sensing Method", 0xA300, "FileSource", FileSource, NULL, "File Source", 0xA301, "SceneType", SceneType, NULL, "Scene Type", 0xA302, "CFAPat", NULL, NULL, "CFA Pattern", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::NikonTags[] = { // Prefix is Nikon 0x0002, "ISO", NULL, NULL, "Nikon ISO Setting", 0x0003, "Color", LowerStr, NULL, "Nikon Color Mode", 0x0004, "Quality", LowerStr, NULL, "Nikon Quality", 0x0005, "WhiteBal", LowerStr, NULL, "Nikon White Balance", 0x0006, "Sharp", LowerStr, NULL, "Nikon Image Sharpening", 0x0007, "Focus", LowerStr, NULL, "Nikon Focus Mode", 0x0008, "Flash", LowerStr, NULL, "Nikon Flash", 0x0009, "FlashMode", LowerStr, NULL, "Nikon Flash Mode", 0x000F, "ISOSel", LowerStr, NULL, "Nikon ISO Selection", 0x0080, "ImgAdjust", LowerStr, NULL, "Nikon Image Adjustment", 0x0082, "Adapter", LowerStr, NULL, "Nikon Adapter Setting", 0x0084, "Lens", LowerStr, NULL, "Nikon Lens", 0x0085, "ManFocus", UnitsM, &PLExifTag::CnvRat, "Nikon Manual Focus Distance", 0x0086, "DigZoom", UnitsX, &PLExifTag::CnvRat, "Nikon Digital Zoom", 0x0088, "AFPos", NULL, NULL, "Nikon Auto Focus Position", //0x0090, "FlashType?", LowerStr, "Nikon Flash Type", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::Nikon2Tags[] = { // Prefix is Nikon2 0x0002, "Unknown0002", NULL, NULL, "Nikon Unknown (0002)", 0x0003, "Quality", N2Quality, NULL, "Nikon Quality", 0x0004, "Color", N2Color, NULL, "Nikon Color Mode", 0x0005, "ImgAdjust", N2ImgAdjust,NULL, "Nikon Image Adjustment", 0x0006, "CCDSens" , N2Iso, NULL, "Nikon ISO Setting", 0x0007, "WhiteBal", N2WhiteBal, NULL, "Nikon White Balance", 0x0008, "Focus", NULL, &PLExifTag::CnvRat, "Nikon Focus", 0x0009, "Unknown0009", NULL, NULL, "Nikon Unknown (0009)", 0x000A, "DigZoom", UnitsX, &PLExifTag::CnvRat, "Nikon Digital Zoom", 0x000B, "Converter", N2Converter,NULL, "Nikon Converter", 0x0F00, "Unknown0F00", NULL, NULL, "Nikon Unknown (0F00)", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::OlympusTags[] = { // Prefix is Oly 0x0200, "SpcMode", NULL, NULL, "Olympus Special Mode", 0x0201, "Quality", OlyJpegQ, NULL, "Olympus JPG Quality", 0x0202, "Macro", OlyMacro, NULL, "Olympus Macro", 0x0204, "DigZoom", UnitsX, &PLExifTag::CnvRat, "Olympus Digital Zoom", 0x0207, "Software", NULL, NULL, "Olympus Software", 0x0209, "CameraID", NULL, NULL, "Olympus Camera ID", // E-10 fields (from dougho@niceties.com) 0x1004, "Flash", OlyFlash, NULL, "Olympus Flash Mode", 0x100F, "Sharp", OlySharp, NULL, "Olympus Sharpness Mode", 0x102a, "SharpScale", NULL, NULL, "Olympus Sharpness", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::CanonTags[] = { // Prefix is Canon 0x0001, "CnSet1", NULL, NULL, "Canon Settings 1", 0x0004, "CnSet2", NULL, NULL, "Canon Settings 2", 0x0006, "ImageType", NULL, NULL, "Canon Image Type", 0x0007, "Software", NULL, NULL, "Canon Firmware Version", 0x0008, "ImageNo", NULL, &PLExifTag::CnvCanINo,"Canon Image Number", 0x0009, "Owner", NULL, NULL, "Canon Owner Name", 0x000C, "SerialNo", NULL, &PLExifTag::CnvCanSNo,"Canon Serial Number", 0x000F, "CustomFnc", NULL, NULL, "Canon Custom Functions", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::CanonSet1[] = { 1, "Macro", CanMacro, NULL, "Canon Macro Mode", 4, "Flash", CanFlashMode, NULL, "Canon Flash Mode", 5, "Drive", CanDrvMode, NULL, "Canon Drive Mode", 7, "Focus", CanFocusMode, NULL, "Canon Focus Mode", 10, "ImgSize", CanImgSize, NULL, "Canon Image Size", 11, "Shoot", CanShootMode, NULL, "Canon Easy Shooting Mode", 13, "Contrast", CanLNH, NULL, "Canon Contrast Setting", 14, "Saturation", CanLNH, NULL, "Canon Saturation Setting", 15, "Sharpness", CanLNH, NULL, "Canon Sharpness Setting", 16, "ISO", CanIso, NULL, "Canon ISO", 17, "Metering", CanMeter, NULL, "Canon Metering mode", 19, "AFPoint", CanAFPoint, NULL, "Canon AutoFocus Point", 20, "ExpMode", NULL, NULL, "Canon Exposure Mode", 23, "LongFocal", NULL, NULL, "Canon Long Focal Length", 24, "ShortFocal", NULL, NULL, "Canon Short Focal Length", 25, "FocalUnits", NULL, NULL, "Canon Focal Units per mm", 29, "FlashDet", NULL, &PLExifTag::CnvCanFlash, "Canon Flash Details", 32, "FocusMode", NULL, NULL, "Canon Focus Mode", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::CanonSet2[] = { 7, "WhiteBal", CanWhiteBal, NULL, "Canon White Balance", 9, "SeqNo", NULL, NULL, "Canon Continuous Frame", 14, "AF Point", NULL, &PLExifTag::CnvCanAFPnt, "Canon Focus Point", 15, "FlashBias", CanFlashBias, NULL, "Canon Flash Bias", 19, "SubjectDist", NULL, NULL, "Canon Subject Distance (0.01m or 0.001m)", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::CanonCFn[] = { 0x01, "NoiseRed", OffOn, NULL, "Canon CFn 01 Long exp noise reduction", 0x02, "Lock", CFnLock, NULL, "Canon CFn 02 Shutter/AE lock buttons", 0x03, "MirrorLock", DisableEnable, NULL, "Canon CFn 03 Mirror lockup ", 0x04, "TVAVexp", CFnTVAV, NULL, "Canon CFn 04 Tv/Av & exposure level", 0x05, "AFassist", CFnAFAssist, NULL, "Canon CFn 05 AF-assist light", 0x06, "ShuttAv", CFnShutAv, NULL, "Canon CFn 06 Shutter speed in Av mode", 0x07, "AEBSeq", CFnAeb, NULL, "Canon CFn 07 AEB Sequence/auto cancel", 0x08, "ShuttSync", CFnShutSync, NULL, "Canon CFn 08 Shutter curtain sync", 0x09, "AFStop", CFnAFStop, NULL, "Canon CFn 09 Lens AF stop button switch", 0x0A, "FillReduce", EnableDisable, NULL, "Canon CFn 10 Fill flash auto reduction", 0x0B, "MenuButt", CFnMenu, NULL, "Canon CFn 11 Menu button return pos", 0x0C, "SetButt", CFnSet, NULL, "Canon CFn 12 SET button when shooting", 0x0D, "SensClean", DisableEnable, NULL, "Canon CFn 13 Sensor cleaning ", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::FujifilmTags[] = { // Prefix is Fuji 0x0000, "Version", NULL, NULL, "Fuji Version", 0x1000, "Quality", LowerStr, NULL, "Fuji Quality", 0x1001, "Sharpness", FujiSharp, NULL, "Fuji Sharpness", 0x1002, "WhiteBal", FujiWBal, NULL, "Fuji White Balance", 0x1003, "Color", FujiColor, NULL, "Fuji Color", 0x1004, "Contrast", FujiTone, NULL, "Fuji Tone", 0x1010, "FlashMode", FujiFlashMode, NULL, "Fuji Flash Mode", 0x1011, "FlashStrg", NULL, &PLExifTag::CnvRat, "Fuji Flash Strength APEX", 0x1020, "Macro", OffOn, NULL, "Fuji Macro Mode", 0x1021, "Focus", FujiFocus, NULL, "Fuji Focus Mode", 0x1022, "Unknown1022", NULL, NULL, "Fuji Unknown (1022)", 0x1023, "Unknown1023", NULL, NULL, "Fuji Unknown (1023)", 0x1030, "SlowSnyc", OffOn, NULL, "Fuji Slow Sync", 0x1031, "Picture", FujiPicture, NULL, "Fuji Exposure Mode", 0x1032, "Unknown1032", NULL, NULL, "Fuji Unknown (1032)", 0x1100, "Sequence", OffOn, NULL, "Fuji Sequence Mode", 0x1101, "Unknown1101", NULL, NULL, "Fuji Unknown (1101)", 0x1200, "Unknown1200", NULL, NULL, "Fuji Unknown (1200)", 0x1300, "BlurWarn", FujiBlurFocus, NULL, "Fuji Blur Warning", 0x1301, "FocusWarn", FujiBlurFocus, NULL, "Fuji Focus Warning", 0x1302, "AEWarn", FujiAE, NULL, "Fuji Exposure Warning", 0, NULL, NULL, NULL, NULL }; _PLExifTagValues PLExifTag::CasioTags[] = { // Prefix is Casio 0x0001, "RecordMode", CasioRecord, NULL, "Casio Recording Mode", 0x0002, "Quality", CasioQuality, NULL, "Casio Quality ", 0x0003, "FocusMode", CasioFocus, NULL, "Casio Focusing Mode", 0x0004, "FlashMode", CasioFlashMode, NULL, "Casio Flash Mode", 0x0005, "FlasIntense", CasioFlashInt, NULL, "Casio Flash Intensity", 0x0006, "ObjDist", UnitsMM, NULL, "Casio Object Distance", 0x0007, "WhiteBal", CasioWhite, NULL, "Casio White Balance", 0x0008, "Unknown0008", NULL, NULL, "Casio Unknown (0008)", 0x0009, "Unknown0009", NULL, NULL, "Casio Unknown (0009)", 0x000A, "DigZoom", CasioZoom, NULL, "Casio Digital Zoom", 0x000B, "Sharpness", CasioSharp, NULL, "Casio Sharpness", 0x000C, "Contrast", CasioLowHi, NULL, "Casio Contrast ", 0x000D, "Saturation", CasioLowHi, NULL, "Casio Saturation", 0x000E, "Unknown000E", NULL, NULL, "Casio Unknown (000E)", 0x000F, "Unknown000F", NULL, NULL, "Casio Unknown (000F)", 0x0010, "Unknown0010", NULL, NULL, "Casio Unknown (0010)", 0x0011, "Unknown0011", NULL, NULL, "Casio Unknown (0011)", 0x0012, "Unknown0012", NULL, NULL, "Casio Unknown (0012)", 0x0013, "Unknown0013", NULL, NULL, "Casio Unknown (0013)", 0x0014, "CCDSense", FujiAE, NULL, "Casio CCD Sensitivity", 0x0015, "Unknown0015", NULL, NULL, "Casio Unknown (0015)", 0x0016, "Enhancement", CasioEnhance, NULL, "Casio Enhancement", 0x0017, "Filter", CasioFilter, NULL, "Casio Filter ", 0x0018, "FocFrmLoc", CasioFFrmLoc, NULL, "Casio Focus Frame Locator", 0x0019, "CCDSense", CasioSens, NULL, "Casio CCD Sensitivity", 0x001A, "Unknown001A", NULL, NULL, "Casio Unknown (001A)", 0x0100, "MeterMode", CasioMeter, NULL, "Casio Metering Mode", 0x0101, "EVShift", CasioEVShift, NULL, "Casio EV Shift", 0x0102, "Unknown0102", NULL, NULL, "Casio Unknown (0102)", 0x0103, "Aperture", CasioAperture, NULL, "Casio Apertue", 0x0104, "RecordMode2", CasioRecord2, NULL, "Casio Recording Mode", 0x0105, "Composition", CasioComp, NULL, "Casio Composition Frame", 0x0106, "ExpMode", CasioExp, NULL, "Casio Exposure Mode", 0x0107, "Unknown0107", NULL, NULL, "Casio Unknown (0107)", 0, NULL, NULL, NULL, NULL }; // // Format types // static void Swiz16(PLBYTE *); static void Swiz32(PLBYTE *); static void SwizR64(PLBYTE *); _PLExifFormatter PLExifTag::rgExifFormat[] = { 1, NULL, &PLExifTag::RenUndef, // 0. undefined 1, NULL, &PLExifTag::RenUDef, // 1. unsigned byte 1, NULL, &PLExifTag::RenStr, // 2. ascii string 2, Swiz16, &PLExifTag::RenUDef, // 3. unsigned short 4, Swiz32, &PLExifTag::RenUDef, // 4. unsigned long 8, SwizR64,&PLExifTag::RenURat, // 5. unsigned rational 1, NULL, &PLExifTag::RenDef, // 6. signed byte 1, NULL, &PLExifTag::RenUndef, // 7. undefined 2, Swiz16, &PLExifTag::RenDef, // 8. signed short 4, Swiz32, &PLExifTag::RenDef, // 9. signed long 8, SwizR64,&PLExifTag::RenRat, // 10. signed rational 4, NULL, &PLExifTag::RenUndef, // 11. signed float (not used) 8, NULL, &PLExifTag::RenUndef, // 12. signed double (not used) }; // Swizzle functions to deal with different endian formats static void Swiz16(PLBYTE *Buffer) { PLBYTE l0; l0 = Buffer[0]; Buffer[0] = Buffer[1]; Buffer[1] = l0; } static void Swiz32(PLBYTE *Buffer) { PLBYTE l0, l1, l2; l0 = Buffer[0]; l1 = Buffer[1]; l2 = Buffer[2]; Buffer[0] = Buffer[3]; Buffer[1] = l2; Buffer[2] = l1; Buffer[3] = l0; } static void SwizR64(PLBYTE *Buffer) // Rationals are 2 32bit values in a row { Swiz32(Buffer); Swiz32(Buffer + 4); } // simple utility functions static void MakeLower(string & s) { for (string::iterator iter = s.begin(); iter != s.end(); ++iter) *iter = tolower(*iter); } static void TrimRight(string & s, char c) { for (string::reverse_iterator iter = s.rbegin(); iter != s.rend() && *iter == c; ) s.erase((++iter).base()); } static void TrimLeft(string & s, char c) { for (string::iterator iter = s.begin(); iter != s.end() && *iter == c; ) s.erase(iter++); } static double round(double value, unsigned int precision) { double factor = 1.0f; for (unsigned int i = 0; i < precision; ++i) factor *= 10.0f; value = value * factor; value += 0.5f; value = floor(value) / factor; return value; } /*************************************************************** * * PLExif implementation * ***************************************************************/ PLExif::PLExif() : m_Pos(0) , m_Endian(false) , m_IdfOffset(0) { } PLExif::~PLExif() { Clear(); } void PLExif::Clear() { m_Data = PLByteCPtr(0); m_DataSize = 0; // Free any tags we may have allocated while (!m_AllTags.empty()) m_AllTags.pop_back(); m_Tags.clear(); // Re-Initialize values m_IdfOffset = 0; m_Pos = 0; m_Endian = false; } void PLExif::ReadData(const jpeg_decompress_struct * pcinfo) { Clear(); jpeg_saved_marker_ptr marker; for (marker = pcinfo->marker_list; marker != NULL; marker = marker->next) { if (marker->marker == JPEG_APP0 + 1) // EXIF marker { m_DataSize = marker->data_length; m_Data = PLByteCPtr(new PLBYTE[m_DataSize]); PLBYTE * pSrc = marker->data; PLBYTE * pEnd = pSrc + marker->data_length; PLBYTE * pDst = m_Data.get(); while (pSrc != pEnd) // copy the data *pDst++ = *pSrc++; } /* code removed should probably be in PLBmpInfo else if (marker->marker == JPEG_COM) { m_Comment.reserve(marker->data_length); PLBYTE * pSrc = marker->data; PLBYTE * pEnd = pSrc + marker->data_length; while (pSrc != pEnd) // copy the data m_Comment += (char)*pSrc++; } */ } if (m_DataSize) // else there's no data to decode { // verify this is an Exif file PLBYTE text[6]; Read(&text, 6); if (memcmp(text, "Exif\0\0", 6) != 0) { Clear(); return; } // Determine the endian type PLWORD type; Read((PLBYTE *)&type, sizeof(type)); if (type == 256*'I'+'I') #ifdef WORDS_BIGENDIAN m_Endian = TRUE; #else m_Endian = FALSE; #endif else if (type == 256*'M'+'M') #ifdef WORDS_BIGENDIAN m_Endian = FALSE; #else m_Endian = TRUE; #endif else PLASSERT (FALSE); // Illegal data in file - should throw an exception. // Check for valid len PLWORD Len = GetU16(); if (Len != 0x2a) PLASSERT(false); // should maybe replace this with an exception later // all seems ok so lets decode decode(); } } void PLExif::WriteData(jpeg_compress_struct * pcinfo) { // this code copied from transupp.c from the JPEG library utilities #ifdef NEED_FAR_POINTERS /* We could use jpeg_write_marker if the data weren't FAR... */ unsigned int i; jpeg_write_m_header(pcinfo, JPEG_APP0 + 1, m_Data.size()); for (i = 0; i < m_Data.size(); ++i) jpeg_write_m_byte(pcinfo, m_Data[i]); /* code removed should probably be in PLBmpInfo jpeg_write_m_header(pcinfo, JPEG_COM, m_Comment.size()); for (i = 0; i < m_Comment.size(); ++i) jpeg_write_m_byte(pcinfo, m_Comment[i]); */ #else jpeg_write_marker(pcinfo, JPEG_APP0 + 1, m_Data.get(), m_DataSize); /* code removed should probably be in PLBmpInfo jpeg_write_marker(pcinfo, JPEG_COM, (PLBYTE *)&m_Comment[0], m_Comment.size()); */ #endif } /* code removed should probably be in PLBmpInfo const string & PLExif::GetComment() const { return m_Comment; } const char * PLExif::GetCommmentCStr() const { return m_Comment.c_str(); } void PLExif::SetComment(const std::string & s) { m_Comment = s; } void PLExif::SetComment(const char * s) { m_Comment = s; } */ PLBYTE * PLExif::GetRawData() { return m_Data.get(); } const PLBYTE * PLExif::GetRawData() const { return m_Data.get(); } size_t PLExif::GetRawDataSize() const { return m_DataSize; } const PLExifTagList & PLExif::GetAllTags() const { return m_AllTags; } const PLExifTagList & PLExif::GetMainTags() const { return m_MainTags; } const PLExifTagList & PLExif::GetSubTags() const { return m_SubTags; } const PLExifTagList & PLExif::GetManufacturerTags() const { return m_ManufacturerTags; } const PLExifTagCPtr * PLExif::GetAllTagsC(size_t & size) const { size = m_AllTags.size(); return &m_AllTags[0]; } const PLExifTagCPtr * PLExif::GetMainTagsC(size_t & size) const { size = m_AllTags.size(); return &m_MainTags[0]; } const PLExifTagCPtr * PLExif::GetSubTagsC(size_t & size) const { size = m_AllTags.size(); return &m_SubTags[0]; } const PLExifTagCPtr * PLExif::GetManufacturerTagsC(size_t & size) const { size = m_AllTags.size(); return &m_ManufacturerTags[0]; } PLExifTag * PLExif::GetTag(const char * TagName) const { string Str = TagName; MakeLower(Str); TagMap::const_iterator iter = m_Tags.find(Str); return iter != m_Tags.end() ? iter->second.get() : NULL; } PLExifTag * PLExif::GetTag(const char * TagName, string & Value) const { PLExifTag * Tag = GetTag(TagName); Value.erase(); if (Tag) Value = Tag->m_Value; return Tag; } PLExifTag * PLExif::GetTagCommon(const char * TagName, std::string & Value) const { PLExifTag * Tag = GetTag(TagName); Value.erase(); if (Tag) Value = Tag->m_Common; return Tag; } PLExifTag * PLExif::GetTag(const char * TagName, double & Value) const { PLExifTag * Tag = GetTag(TagName); Value = 0.0; if (Tag) Value = Tag->m_Double; return Tag; } const char * PLExif::TagCStr(const char * TagName) const { PLExifTag * Tag = GetTag(TagName); return Tag ? Tag->m_Value.c_str() : Nullstring.c_str(); } const char * PLExif::TagCStrCommon(const char * TagName) const { PLExifTag * Tag = GetTag(TagName); return Tag ? Tag->m_Common.c_str() : Nullstring.c_str(); } string & PLExif::TagStr(const char * TagName) const { PLExifTag * Tag = GetTag(TagName); return Tag ? Tag->m_Value : Nullstring; } string & PLExif::TagStrCommon(const char * TagName) const { PLExifTag * Tag = GetTag(TagName); return Tag ? Tag->m_Common : Nullstring; } // // private interface // // main EXIF decoding routine // void PLExif::decode() { try { PLExifTag * Tag; // crack the main Exif directory // m_IdfOffset = m_IdfOffset + 6; // 0xC; GetU32(); // junk ReadIFD(PLExifTag::MainTags, "Main.", m_MainTags); // // Crack the sub-Exif directory // Tag = GetTag("Main.ExifOffset"); if (Tag) { SetPos(Tag->m_UInt); ReadIFD(PLExifTag::SubTags, "Sub.", m_SubTags); } // // Crack camera specific information // string Str; GetTag("Main.Make", Str); Tag = GetTag("Sub.Maker"); MakeLower(Str); try // still sometimes get make notes that make no sense so just ignore the exception { // and at least we have the main and sub tags if (Tag) { if (Str.find("nikon") != string::npos) // Nikon { SetPos(Tag->m_Pos); char name[6]; Read((PLBYTE *)name, 5); name[5] = '\0'; if (strcmp(name, "Nikon")) // if not starting with string Nikon { // nikon format 1 SetPos(Tag->m_Pos); // reset to the start ReadIFD(PLExifTag::NikonTags, "Nikon.", m_ManufacturerTags); /* GetTag("Main.Model", Str); if (Str == "E990" || Str == "E880") m_FocalConvert = 0.209; if (Str.find("D1X") != string::npos) m_FocalConvert = 1 / 1.5; */ } else { SetPos(Tag->m_Pos + 8); ReadIFD(PLExifTag::Nikon2Tags, "Nikon2.", m_ManufacturerTags); } } else if (Str.find("olympus") != string::npos) // Olympus { SetPos(Tag->m_Pos + 8); ReadIFD(PLExifTag::OlympusTags, "Oly.", m_ManufacturerTags); /* GetTag("Main.Model", Str); if (Str == "E-10") m_FocalConvert = 0.2571; */ } else if (Str.find("canon") != string::npos) // Canon { SetPos(Tag->m_Pos); ReadIFD(PLExifTag::CanonTags, "Canon.", m_ManufacturerTags); ExpandBinaryTag("Canon.CnSet1", PLExifTag::CanonSet1, etUint16, m_ManufacturerTags); ExpandBinaryTag("Canon.CnSet2", PLExifTag::CanonSet2, etUint16, m_ManufacturerTags); if (PLExifTag * tagp = GetTag("Canon.CustomFnc")) DecodeCanCustomFncs(*tagp, PLExifTag::CanonCFn, m_ManufacturerTags); } else if (Str.find("fujifilm") != string::npos) { m_Endian = false; // for some bizarre reason Fuji switch to Intel for their maker data m_IdfOffset = Tag->m_Pos + 6; // All Fuji offsets from start of Fuji notes not EXIF SetPos(0x0C); ReadIFD(PLExifTag::FujifilmTags, "Fuji.", m_ManufacturerTags); } else if (Str.find("casio") != string::npos) // Nikon { SetPos(Tag->m_Pos); ReadIFD(PLExifTag::CasioTags, "Casio.", m_ManufacturerTags); // Now sort the Casio Flash Mode mess PLExifTag * model = GetTag("Main.Model"); PLExifTag * flash = GetTag("Casio.FlashMode"); if (flash && model && model->m_Common == "QV-2000UX") { if (flash->m_Int == 4) { flash->m_Value = "Red Eye Reduction"; flash->m_Common = "Red Eye Reduction"; } } } } } catch(...) // catch all and ignore (see note above on try) {} // // OK, we've cracked everything we know about. Now we will // build our default tags // //BuildDefaultTags(); PLExifTagList::iterator iter; for (iter = m_AllTags.begin(); iter != m_AllTags.end(); ++iter) (*iter)->CleanWorkingArea(); } catch(PLExifException & p) { throw PLTextException(PL_ERRBAD_EXIF, p.m_message.c_str()); } } void PLExif::CopyTag(const char * Src, const char * Dst) // N.B. this only copies the string value of the tag { PLExifTag *Tag = GetTag(Src); if (Tag) SetTag(Dst, Tag->m_Value); } void PLExif::SetTag(const char * Dst, const char * Value) { PLExifTag *Tag = GetTag(Dst); if (!Tag) { PLExifTagCPtr Tagcp(new PLExifTag(0, 2, 0)); Tag = Tagcp.get(); // Add to our list of tags Tag->m_ShortName = Dst; Tag->m_Lookup = Tag->m_ShortName; MakeLower(Tag->m_Lookup); m_AllTags.push_back(Tagcp); m_Tags[Tag->m_Lookup] = Tagcp; } Tag->m_Fmt = &PLExifTag::rgExifFormat[2]; Tag->m_Value = Value; } void PLExif::AddTag(const char * DstTag, const char * SrcTag, const char * Skip, const char * Sep) // Add SrcTag to DstTag if SrcTag is present and is not Skip // Put the string Sep between the tags { string Str; GetTag(SrcTag, Str); AddStr(DstTag, Str, Skip, Sep); } void PLExif::AddStr(const char * DstTag, const string & SrcStr, const char * Skip, const char * Sep) { string Str(SrcStr); string Str2; Str = SrcStr; MakeLower(Str); if (!(SrcStr.empty() || (Skip && Str == Skip))) { GetTag(DstTag, Str2); if (!Sep) Sep = " / "; if (Str2.empty()) Sep = ""; Str = Str2 + Sep + SrcStr; SetTag(DstTag, Str); } } void PLExif::FormatRange(double Low, double High, string & Str) { ostringstream oss; oss << ios::fixed << setprecision(1) << setw(4) << Low; string LStr(oss.str()); TrimRight(LStr, '0'); TrimRight(LStr, '.'); TrimLeft(LStr, ' '); oss.str(""); oss << setw(4) << High; string HStr(oss.str()); TrimRight(HStr, '0'); TrimRight(HStr, '.'); TrimLeft(HStr, ' '); Str = LStr; if (LStr != HStr) Str = LStr + '-' + HStr; } void PLExif::ReadIFD(const _PLExifTagValues *Tags, char * Prefix, PLExifTagList & sectionList) { size_t NoDir, DirPos, Index; size_t TagNo, Format, NoComp, Offset; NoDir = GetU16(); DirPos = GetPos(); while (NoDir) { ostringstream oss; // // Allocate a new tag // SetPos(DirPos); TagNo = GetU16(); Format = GetU16(); NoComp = GetU32(); if (NoComp != 0) // otherwise no components so no data { PLExifTagCPtr Tag(new PLExifTag(TagNo, Format, NoComp)); // // Create the tag's shortname (and initialize it's m_Tag) // oss << Prefix << setfill('0') << setw(4) << hex << Tag->m_TagNo; Tag->m_ShortName = oss.str(); for(Index=0; Tags[Index].ShortName; Index++) { if (Tags[Index].Tag == Tag->m_TagNo) { Tag->m_Tag = &Tags[Index]; Tag->m_ShortName = Prefix; Tag->m_ShortName += Tag->m_Tag->ShortName; break; } } // // Read the tag's data into its buffer // // If the data size is larger then 4, then we have a pointer to it if (Tag->m_Size > 4) { Offset = GetU32(); SetPos(Offset); } Tag->m_Pos = GetPos(); Read(Tag->m_Buffer.get(), Tag->m_Size); // // If we are the wrong endian, then swizzle the data buffer // if (m_Endian) Tag->Swizzle(); // // Print the value into a string // Tag->Render(); // // Add this tag to the database // Tag->m_Lookup = Tag->m_ShortName; MakeLower(Tag->m_Lookup); m_AllTags.push_back(Tag); // add to all tags list m_Tags[Tag->m_Lookup] = Tag; // add to the look up map sectionList.push_back(Tag); // add to the section list } // // Next DirEntry // NoDir = NoDir - 1; DirPos = DirPos + 12; } } /* void PLExif::BuildDefaultTags() { string Str;//, Str1, Str2, Model; string Model; PLExifTag * Tag; double w, d1, d2; PLBYTE c; ostringstream oss; oss << fixed; // set stream to fixed precision // // Once all the other tags are read in, we create a common set // of tags built up from the others. This allows the callers to // read the common stuff without needing to worry about the camera // specific fields, but if they want they can read those as well. // // Make - Camera Make // Model - Camera Model // Software - Camera Software // Date - Original Photo date/time // Res - X x Y size of original // Flash - Flash used & setting // Focal - Focal length // s - Exposure time // f - Aperture f/ // ISO - ISO Equiv // WhiteBal - WhiteBalance // Meter - Metering mode // ExpProg - Exposure program // ExpBias - Exposure Bias // Focus - Focus // qual - Compression quality // Lens - Info on the lens // ImageAdj - Image adjustment // Sharpness - Sharpness setting // // Model = TagStr("Main.Model"); // Many tags are just copies.. so make them CopyTag("Main.Make", "Make"); CopyTag("Main.Model", "Model"); CopyTag("Sub.Meter", "Meter"); CopyTag("Sub.Prog", "ExpProg"); // For Date use the last found of these time fields CopyTag("Sub.ModTime", "Date"); CopyTag("Sub.OrigTime", "Date"); CopyTag("Sub.DigTime", "Date"); CopyTag("Main.Software", "Software"); CopyTag("Canon.Software", "Software"); AddTag ("Software", "Oly.Software"); // Get the width & height for Res Str = TagStr("Sub.Width") + " x " + TagStr("Sub.Height"); SetTag("Res", Str); // Set the flash mode - append the camera specific setting if the flash fired GetTag("Sub.Flash", Str); SetTag("Flash", Str); if (Str.find("yes") != string::npos) { AddTag("Flash", "Nikon.Flash"); AddTag("Flash", "Canon.Flash"); AddTag("Flash", "Oly.Flash"); } // The D1x doesn't set Sub.Flash if (Model.find("D1X") != string::npos) { AddTag("Flash", "Nikon.Flash"); AddTag("Flash", "Nikon.FlashMode"); } // CCD width GetTag("Sub.Width", w); GetTag("Sub.FPXRes", d1); Tag = GetTag("Sub.FPResUnit"); d2 = 0; if (Tag) { switch(Tag->m_UInt) { case 1: d2 = 25.4; break; // inch case 2: d2 = 25.4; break; // inch case 3: d2 = 10; break; // centimeter case 4: d2 = 1; break; // milimeter case 5: d2 = 0.001; break; // micrometer } } if (w && d1 && d2) { d1 = w * d2 / d1; oss.str(""); oss << setprecision(2) << setw(4) << d1 << "mm"; // Str.Format("%4.2fmm", d1); SetTag("CCDWidth", oss.str()); // Set conversion if (!m_FocalConvert) m_FocalConvert = d1 / 35; } // Focal length Tag = GetTag("Sub.Focal"); if (Tag) { oss.str(""); oss << setprecision(1) << setw(4) << Tag->m_Double << "mm"; // Str.Format("%4.1fmm", ); if (m_FocalConvert > 0) { oss.str(""); oss << setprecision(1) << setw(4) << Tag->m_Double << "mm (35mm equivalent: " << (PLUINT) (Tag->m_Double / m_FocalConvert + 0.5) << "mm"; // Str.Format("%4.1fmm (35mm equivalent: %dmm)", ); } SetTag("Focal", oss.str()); } // shutter speed Tag = GetTag("Sub.s"); if (Tag) { oss.str(""); oss << setprecision(3) << setw(6) << Tag->m_Double << "fs"; // Str.Format("%6.3fs", ); if (Tag->m_Double <= 0.5) { oss.str(""); oss << setprecision(3) << setw(6) << Tag->m_Double << "fs (1/" << (PLUINT) (1 / Tag->m_Double + 0.5) << ')'; // Str.Format("%6.3fs (1/%d)", ); } SetTag("s", oss.str()); } // aperture Tag = GetTag("Sub.f"); if (Tag) { oss.str(""); oss << "f/" << setprecision(1) << setw(3) << Tag->m_Double; // Str.Format("f/%3.1f", ); SetTag("f", oss.str()); } // ISO CopyTag("Sub.ISO", "ISO"); if (!GetTag("ISO")) { // ISO set? Tag = GetTag("Nikon.ISO"); // No, get the Nikon.ISO setting if (Tag) { oss.str(""); oss << (int)Tag->m_Int; SetTag("ISO", oss.str()); // to the ISO info } } // Exposure Bias GetTag("Sub.eba", d1); if (d1 != 0) { c = '+'; if (d1 < 0) { d1 = -d1; c = '-'; } oss.str(""); oss << c << setprecision(2) << setw(3) << d1; // Str.Format("%c%3.2f", c, d1); SetTag("ExpBias", oss.str()); } // White balance CopyTag("Sub.ls", "WhiteBal"); CopyTag("Nikon.WhiteBal", "WhiteBal"); // // Focus // AddTag("Focus", "Nikon.Focus"); AddTag("Focus", "Oly.Macro", "normal"); Tag = GetTag("Sub.Dist", d1); // Kodak is expressing -1 as an unsigned in the numerator. So check for it. if (Tag && Tag->m_Num == 0xFFFFFFFF) d1 = -1; Tag = GetTag("Nikon.ManFocus"); if (Tag && Tag->m_Num != 0) { d1 = Tag->m_Double; // Nikon expresses INF as 1/0 if (Tag->m_Num == 1 && Tag->m_Den == 0) d1 = -1; } oss.str(""); if (d1 > 0) oss << setprecision(2) << setw(5) << d1 << "fm"; // Str1.Format("%5.2fm", d1); else if (d1 < 0) oss << "Infinite"; AddStr("Focus", oss.str()); // // Compression Quality // CopyTag("Sub.bpp", "Qual"); CopyTag("Nikon.Quality", "Qual"); CopyTag("Oly.Quality", "Qual"); // Lens, ImageAdj, Sharpness Tag = GetTag("Nikon.Lens"); if (Tag) { double Low, High; string Focal, Aperture; Low = Tag->GetDouble(0); High = Tag->GetDouble(1); FormatRange(Low, High, Focal); Low = Tag->GetDouble(2); High = Tag->GetDouble(3); FormatRange(Low, High, Aperture); oss.str(""); oss << Focal << "mm f/" << Aperture; // Str.Format("%smm f/%s", Focal, Aperture); SetTag("Lens", oss.str()); } AddTag("Lens", "Nikon.Adapter", "off"); // ImageAdj, Sharpness AddTag("ImageAdj", "Nikon.ImgAdjust", "auto"); AddTag("Sharpness","Nikon.Sharp", "auto"); AddTag("Sharpness","Oly.Sharp", "normal"); } */ void PLExif::ExpandBinaryTag(const string & Src, const _PLExifTagValues *Tags, PLUINT Type, PLExifTagList & sectionList) { PLUINT ElmSize, ElmFormat; PLExifTag * SrcTag; string Prefix; size_t Index; size_t Pos; ostringstream oss; SrcTag = GetTag(Src); if (!SrcTag || SrcTag->m_Fmt != &PLExifTag::rgExifFormat[Type]) { return ; } // Prefix is the original prefix Prefix = Src; Pos = Prefix.find("."); if (Pos != string::npos) { Prefix = Prefix.substr(Pos+1); Prefix += '.'; } // Tag should be items of all one size. For now we are assuming they // are type 3 (unsigned short) ElmSize = PLExifTag::rgExifFormat[Type].Size; ElmFormat = Type; for (Index=0; Tags[Index].ShortName && Tags[Index].Tag < SrcTag->m_NoComp; Index++) { // // Allocate a tag for this item // PLExifTagCPtr Tag(new PLExifTag(Tags[Index].Tag, ElmFormat, 1)); Tag->m_Tag = &Tags[Index]; Tag->m_ShortName = Prefix + Tag->m_Tag->ShortName; memcpy (Tag->m_Buffer.get(), SrcTag->m_Buffer.get() + Tag->m_TagNo * ElmSize, ElmSize); // // If we are the wrong endian, then swizzle the data buffer // if (m_Endian) { Tag->Swizzle(); } // // Print the value into a string // Tag->Render(); // // Add this tag to the database // Tag->m_Lookup = Tag->m_ShortName; MakeLower(Tag->m_Lookup); m_AllTags.push_back(Tag); // add to all tags list m_Tags[Tag->m_Lookup] = Tag; // add to the look up map sectionList.push_back(Tag); // add to the section list } } void PLExif::DecodeCanCustomFncs(const PLExifTag & rootTag, const _PLExifTagValues *Tags, PLExifTagList & sectionList) { string Prefix("CanonCFn."); const unsigned short * datap = (const unsigned short *)rootTag.m_Buffer.get(); unsigned short count = *datap++ / 2; // step over the count for (unsigned short i = 0; i < count; ++i, ++datap) { if (*datap & 0xFF00) // ignore a zero tag { ostringstream oss; PLExifTagCPtr Tag(new PLExifTag((*datap & 0xFF00) >> 8, 1, 0)); // use auto_ptr for exception safety // // Create the tag's shortname (and initialize it's m_Tag) // oss << Prefix << setfill('0') << setw(2) << Tag->m_TagNo; Tag->m_ShortName = oss.str(); for(size_t i = 0; Tags[i].ShortName; ++i) { if (Tags[i].Tag == Tag->m_TagNo) { Tag->m_Tag = &Tags[i]; Tag->m_ShortName = Prefix; Tag->m_ShortName += Tag->m_Tag->ShortName; break; } } Tag->m_Int = *datap & 0x00FF; Tag->m_UInt = Tag->m_Int; Tag->DoTranslation(); // // Add this tag to the database // Tag->m_Lookup = Tag->m_ShortName; MakeLower(Tag->m_Lookup); m_AllTags.push_back(Tag); // add to all tags list m_Tags[Tag->m_Lookup] = Tag; // add to the look up map sectionList.push_back(Tag); // add to the section list } } } // these functions were originally designed to read from a file // they have been kept but are now "reading" from the vector m_Data // I may remove at least the first couple later void PLExif::SetPos(size_t pos) { m_Pos = pos + m_IdfOffset; } size_t PLExif::GetPos() { return m_Pos - m_IdfOffset; } void PLExif::Read(void * buffer, size_t size) { memcpy(buffer, m_Data.get() + m_Pos, size); m_Pos += size; } PLWORD PLExif::GetU16() { PLWORD u16; Read(&u16, sizeof(u16)); if (m_Endian) { Swiz16((PLBYTE *) &u16); } return u16; } PLLONG PLExif::GetU32() { PLLONG u32; Read(&u32, sizeof(u32)); if (m_Endian) { Swiz32((PLBYTE *) &u32); } return u32; } /*************************************************************** * * PLExifTag implementation * ***************************************************************/ // true public interface // C Interface - returning const char * const char * PLExifTag::GetShortNameCStr() const { return m_ShortName.c_str(); } const char * PLExifTag::GetDescriptionCStr() const { return m_Tag ? m_Tag->Desc : Nullstring.c_str(); } // Format is basic as stored in the raw EXIF data // eg f4.0 is likely to be 40/10 const char * PLExifTag::GetValueCStr() const { return m_Value.c_str(); } // format is common form eg f4.0 is 4.0 rather than 40/10 const char * PLExifTag::GetValueCommonCStr() const { return m_Common.c_str(); } // STL Interface - returning const string & const string & PLExifTag::GetShortName() const { return m_ShortName; } const string PLExifTag::GetDescription() const { return m_Tag ? m_Tag->Desc : Nullstring; } // Format is basic as stored in the raw EXIF data // eg f4.0 is likely to be 40/10 const string & PLExifTag::GetValue() const { return m_Value; } // format is common form eg f4.0 is 4.0 rather than 40/10 const string & PLExifTag::GetValueCommon() const { return m_Common; } // interface only used by PLExif PLExifTag::PLExifTag(PLUINT TagNo, PLUINT Format, PLUINT NoComp) : m_Tag(NULL) , m_Fmt(NULL) , m_Buffer(NULL) , m_Size(0) , m_Num(0) , m_Den(1) , m_Int(0) , m_UInt(0) , m_Double(0) , m_TagNo(TagNo) , m_Format(Format) , m_NoComp(NoComp) { if (Format < 1 || Format > 12) throw PLExifException("EXIF Tag format field not understood"); // Allocate a buffer for this tags data m_Fmt = &rgExifFormat[Format]; m_Size = m_Fmt->Size * m_NoComp; if (m_Size > 64*1024 || m_NoComp > 64*1024) throw PLExifException("EXIF NoComp field not understood"); if (m_Size) { m_Buffer = PLByteCPtr(new PLBYTE [m_Size]); } } PLExifTag::~PLExifTag() { m_Buffer = PLByteCPtr(0); } void PLExifTag::CleanWorkingArea() { m_Buffer = PLByteCPtr(0); } void PLExifTag::Swizzle() // Data needs swizzled { if (m_Fmt->Swizzle) { PLBYTE * Buffer = m_Buffer.get(); for (size_t Index=0; Index < m_NoComp; ++Index) { m_Fmt->Swizzle(Buffer); Buffer = Buffer + m_Fmt->Size; } } } void PLExifTag::Render() { size_t Index, Count; PLBYTE * Buffer = m_Buffer.get(); m_Value.erase(); Index = 0; Count = 0; for (; ;) { Index += (this->*m_Fmt->Render)(Buffer); if (Index >= m_NoComp) break; Count += 1; if (Count >= 16) { m_Value += " ..."; break; } // Add a space between the array of values m_Value += " "; } // Normalize the value into different formats if (m_Den != 0) { m_Double = (float) m_Num / (float) m_Den; m_Int = (PLULONG) (m_Double + 0.5); m_UInt = (PLUINT) m_Int; } // set up the common string if (m_Tag) { if (m_Tag->Convert) (this->*m_Tag->Convert)(m_Common); else m_Common = m_Value; } DoTranslation(); // Trim trailing spaces from output TrimRight(m_Value, ' '); } void PLExifTag::DoTranslation() { // See if there is any translation if (m_Tag && m_Tag->Trans) { _PLExifTranslator *Trans; Trans = m_Tag->Trans; if (Trans[0].Value == VALUE_UNIT) { // Translator supplies units of value m_Value += " "; m_Common += " "; m_Value += Trans[0].Desc; m_Common += Trans[0].Desc; } else if (Trans[0].Value == VALUE_LOWER_STR) { MakeLower(m_Value); m_Value[0] = toupper(m_Value[0]); MakeLower(m_Common); m_Common[0] = toupper(m_Common[0]); } else { // Lookup value's verbose name and use that for (size_t Index=0; Trans[Index].Desc; Index++) { if (Trans[Index].Value == m_UInt) { m_Value = Trans[Index].Desc; m_Common = Trans[Index].Desc; break; } } } } } double PLExifTag::GetDouble(size_t Index) { Value(Index); return m_Double; } void PLExifTag::Value(size_t Index) { string HoldValue = m_Value; m_Num = 0; m_Den = 0; m_Int = 0; m_UInt = 0; m_Double = 0; // If Index is in range, let's get it if (Index <= m_NoComp) { PLBYTE * Buffer = m_Buffer.get(); // Render each element and stop at the one we want size_t Count = 0; while (Count <= Index) { m_Value = ""; Count += (this->*m_Fmt->Render)(Buffer); } } // Normalize the value into different formats if (m_Den != 0) { m_Double = (double) m_Num / (double) m_Den; m_Int = (PLULONG) (m_Double + 0.5); m_UInt = (PLUINT) m_Int; } // restore original string m_Value = HoldValue; } // // Render functions // size_t PLExifTag::RenUDef(PLBYTE * & Buffer) // Default unsigned number of m_Fmt->Size { PLULONG Accum, Size; ostringstream oss; // Pull the value from the stream Size = m_Fmt->Size; memcpy (&Accum, Buffer, Size); Accum = Accum & rgMask[Size]; // Append this value to the output oss << Accum; m_Value += oss.str(); // Remember last value m_Num = Accum; // Done - we handled 1 value of this type Buffer += Size; return 1; } size_t PLExifTag::RenDef(PLBYTE * &Buffer) // Default signed number of m_Fmt->Size { PLULONG Size, Mask; PLLONG Accum; ostringstream oss; // Pull the value from the stream Size = m_Fmt->Size; Mask = rgMask[Size]; memcpy (&Accum, Buffer, Size); Accum = Accum & Mask; // If the sign bit is set, sign extend the value if (Accum & (1 << (Size * 8 - 1))) Accum = Accum | (-1L ^ Mask); // Append this value to the output oss << Accum; m_Value += oss.str(); m_Num = Accum; // Done - we handled 1 value of this type Buffer += Size; return 1; } size_t PLExifTag::RenRat(PLBYTE * &Buffer) // Signed Rational { PLLONG Num, Den; ostringstream oss; memcpy (&Num, Buffer, 4); memcpy (&Den, Buffer+4, 4); // Append this value to the output oss << Num << '/' << Den; m_Value += oss.str(); m_Num = Num; m_Den = Den; // Done - we handle 1 value of this type Buffer += 8; return 1; } size_t PLExifTag::RenURat(PLBYTE * &Buffer) // Unsigned Rational { PLULONG Num, Den; ostringstream oss; memcpy (&Num, Buffer, 4); memcpy (&Den, Buffer+4, 4); // Append this value to the output oss << Num << '/' << Den; m_Value += oss.str(); m_Num = Num; m_Den = Den; // Done - we handle 1 value of this type Buffer += 8; return 1; } size_t PLExifTag::RenStr(PLBYTE * &Buffer) { size_t Index; PLBYTE c; char Str[20]; ostringstream oss; Index = 0; while (*Buffer && Index < m_Size) { c = *Buffer; if (c >= ' ' && c <= 127) { m_Value += c; } else { switch (c) { case '\n': strcpy(Str, "\\n"); break; case '\r': strcpy(Str, "\\r"); break; case '\t': strcpy(Str, "\\t"); break; case '\b': strcpy(Str, "\\b"); break; default: sprintf(Str, "\\0x%02x", c); } m_Value += Str; } // Next char Buffer += 1; Index += 1; } // Done - we handled the whole value return m_NoComp; } size_t PLExifTag::RenUndef(PLBYTE * &Buffer) { bool AscStr; size_t Index, Size; PLBYTE c; char Str[20]; AscStr = true; for(Index=0; Index < m_Size; Index++) { c = Buffer[Index]; if ((c < ' '|| c > 127) && c != 0 && c != '\n' && c != '\r' && c != '\t' && c != '\b') { AscStr = false; break; } } if (AscStr) { return RenStr(Buffer); } else { // Dump the leading bytes Size = m_Size; if (Size > 16) Size = 16; m_Value += "{ "; for(Index=0; Index < Size; Index++) { sprintf(Str, "%02x ", Buffer[Index]); m_Value += Str; } if (Size != m_Size) m_Value += "... "; m_Value += "}"; // Take the last byte as our default value m_Num = Buffer[m_Size-1]; // Done - we handled the whole value return m_NoComp; } } // evaluate nominator/denominator void PLExifTag::CnvRat(std::string & result) { if (m_Den) { ostringstream oss; oss << double(m_Num) / double(m_Den); result = oss.str(); } else result = m_Value; } // evaluate nominator/denominator for fstop void PLExifTag::CnvRatAp(std::string & result) { if (m_Den) { ostringstream oss; oss << 'f' << round(double(m_Num) / double(m_Den), 1); result = oss.str(); } else result = m_Value; } // evaluate nominator/denominator if greater than 1 // and reduce to 1/x void PLExifTag::CnvFrac(std::string & result) { ostringstream oss; if (m_Den && m_Den <= m_Num) { oss << double(m_Num) / double(m_Den); result = oss.str(); } else if (m_Num != 1 && m_Num != 0) { oss << "1/" << m_Den / m_Num; result = oss.str(); } else result = m_Value; } void PLExifTag::CnvApexShutter(std::string & result) { if (m_Den) { ostringstream oss; double d = 0.0; d = double(m_Num) / double(m_Den); d = pow(2.0, d); oss << 1 << '/' << round(d, 0) << " s"; result = oss.str(); } else result = m_Value; } void PLExifTag::CnvApexAp(std::string & result) { if (m_Den) { ostringstream oss; double d = 0.0; d = double(m_Num) / double(m_Den); d = pow(1.4142, d); oss << 'f' << round(d, 1); result = oss.str(); } else result = m_Value; } void PLExifTag::CnvCompCfg(std::string & result) { result.erase(); istringstream iss(m_Value.c_str()); string val; iss >> val; // reject leading { for (int i = 0; i < 4; ++i) // read four values { iss >> val; if (val == "00") ; // ignore else if (val == "01") result += 'Y'; else if (val == "02") result += "Cb"; else if (val == "03") result += "Cr"; else if (val == "04") result += 'R'; else if (val == "05") result += 'G'; else if (val == "06") result += 'B'; else { result += ' '; result += val; } } } // decode canon image number void PLExifTag::CnvCanINo(std::string & result) { ostringstream oss; oss << setfill('0') << setw(3) << m_Int / 10000 << '-' << setw(4) << m_Int % 10000; result = oss.str(); } // decode canon serial number void PLExifTag::CnvCanSNo(std::string & result) { ostringstream oss; unsigned short * low = (unsigned short *)&m_Int; unsigned short * high = low + 1; oss << setfill('0') << setw(4) << hex << *high; oss << setw(5) << dec << *low; result = oss.str(); } // decode canon flash details void PLExifTag::CnvCanFlash(std::string & result) { result.erase(); if (0x4000 & m_Int) result += "External E-TTL "; if (0x2000 & m_Int) result += "Internal Flash "; if (0x0800 & m_Int) result += "FP sync used "; if (0x0010 & m_Int) result += "FP sync enabled "; } // decode canon flash details void PLExifTag::CnvCanAFPnt(string & result) { result.erase(); if (0xF000 & m_Int) { switch(0x0FFF & m_Int) { case 0: result = "Right"; break; case 1: result = "Centre"; break; case 2: result = "Left"; break; } } }