diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc
index a46e07c33..869fd0775 100644
--- a/rtengine/imagedata.cc
+++ b/rtengine/imagedata.cc
@@ -119,7 +119,7 @@ void ImageData::extractInfo () {
static const char *corp[] =
{ "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
"MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
- "SAMSUNG", "Mamiya", "MOTOROLA" };
+ "SAMSUNG", "Mamiya", "MOTOROLA", "Leaf" };
for (size_t i=0; i < (sizeof(corp)/sizeof(*corp)); i++)
if ( make.find( corp[i] ) != std::string::npos ){ /* Simplify company names */
make = corp[i];
@@ -175,6 +175,7 @@ void ImageData::extractInfo () {
if (sscanf ((const char*)exif->getTag("DateTimeOriginal")->getValue(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) {
time.tm_year -= 1900;
time.tm_mon -= 1;
+ time.tm_isdst = -1;
timeStamp = mktime(&time);
}
}
diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc
index 0c8061a28..1aed31061 100644
--- a/rtengine/imageio.cc
+++ b/rtengine/imageio.cc
@@ -644,7 +644,7 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) {
jpeg_start_compress(&cinfo, TRUE);
// buffer for exif and iptc markers
- unsigned char* buffer = new unsigned char[165535]; //TODO: Is it really 165535... or 65535 ?
+ unsigned char* buffer = new unsigned char[165535]; //FIXME: no buffer size check so it can be overflowed in createJPEGMarker() for large tags, and then software will crash
unsigned int size;
// assemble and write exif marker
if (exifRoot) {
diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc
index 2de29d2b7..bf000b23d 100644
--- a/rtexif/rtexif.cc
+++ b/rtexif/rtexif.cc
@@ -1308,6 +1308,249 @@ void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root)
}
}
+static void
+parse_leafdata(TagDirectory* root, ByteOrder order) {
+
+ Tag *leafdata = root->getTag("LeafData");
+ if (!leafdata) {
+ return;
+ }
+ unsigned char *value = leafdata->getValue();
+ int valuesize = leafdata->getValueSize();
+
+ // parse LeafData tag, a tag specific to Leaf digital backs, and has a custom
+ // format with 52 byte tag headers starting with "PKTS".
+ char *hdr;
+ int pos = 0;
+
+ // There are lots of sub-tags in here, but for now we only care about those directly
+ // useful to RT, which is ISO and rotation. Shutter speed and aperture is not
+ // available here.
+ int iso_speed = 0;
+ int rotation_angle = 0;
+ int found_count = 0;
+ while (pos + sizeof(hdr) <= valuesize && found_count < 2) {
+ hdr = (char *)&value[pos];
+ if (strncmp(hdr, "PKTS", 4) != 0) {
+ // in a few cases the header can be offset a few bytes, don't know why
+ // it does not seem to be some sort of alignment, it appears random,
+ // this check takes care of it, restart if we find an offset match.
+ int offset = 1;
+ for (; offset <= 3; offset++) {
+ if (strncmp(&hdr[offset], "PKTS", 4) == 0) {
+ pos += offset;
+ break;
+ }
+ }
+ if (offset <= 3) {
+ continue;
+ }
+ break;
+ }
+ int size = sget4((unsigned char *)&hdr[48], order);
+ if (pos + size > valuesize) {
+ break;
+ }
+ pos += 52;
+ char *val = (char *)&value[pos];
+ if (strncmp(&hdr[8], "CameraObj_ISO_speed", 19) == 0) {
+ iso_speed = 25 * (1 << (atoi(val) - 1));
+ found_count++;
+ } else if (strncmp(&hdr[8], "ImgProf_rotation_angle", 22) == 0) {
+ rotation_angle = atoi(val);
+ found_count++;
+ } else {
+ // check if this is a sub-directory, include test for that strange offset of next header
+ if (size >= 8 &&
+ (strncmp(val, "PKTS", 4) == 0 ||
+ strncmp(&val[1], "PKTS", 4) == 0 ||
+ strncmp(&val[2], "PKTS", 4) == 0 ||
+ strncmp(&val[3], "PKTS", 4) == 0))
+ {
+ // start of next hdr, this is a sub-directory, we skip those for now.
+ size = 0;
+ }
+ }
+ pos += size;
+ }
+
+ // create standard tags from the custom Leaf tags
+ Tag* exif = root->getTag ("Exif");
+ if (!exif) {
+ exif = new Tag (root, root->getAttrib ("Exif"));
+ exif->initSubDir();
+ root->addTagFront (exif);
+ }
+ if (!exif->getDirectory()->getTag("ISOSpeedRatings")) {
+ Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
+ t->initInt (iso_speed, LONG);
+ exif->getDirectory()->addTagFront (t);
+ }
+ if (!root->getTag("Orientation")) {
+ int orientation;
+ switch (rotation_angle) {
+ case 0: orientation = 1; break;
+ case 90: orientation = 6; break;
+ case 180: orientation = 3; break;
+ case 270: orientation = 8; break;
+ default: orientation = 1; break;
+ }
+ Tag *t = new Tag (root, root->getAttrib ("Orientation"));
+ t->initInt (orientation, SHORT);
+ root->addTagFront (t);
+ }
+
+ // now look in ApplicationNotes tag for additional information
+ Tag *appnotes = root->getTag("ApplicationNotes");
+ if (!appnotes) {
+ return;
+ }
+ char *xmp = (char *)appnotes->getValue();
+ char *end, *p;
+ // Quick-and-dirty value extractor, no real xml parsing.
+ // We could make it more generic, but we just get most important
+ // values we know use to be in there.
+ if ((p = strstr(xmp, "xmlns:tiff")) != NULL &&
+ (end = strstr(p, "")) != NULL)
+ {
+ *end = '\0';
+ while ((p = strstr(p, "')) == NULL) {
+ break;
+ }
+ *tagend = '\0';
+ char *val = &tagend[1];
+ if ((p = strstr(val, "';
+ break;
+ }
+ *p = '\0';
+ if (root->getAttrib (tag) && !root->getTag (tag)) {
+ Tag *t = new Tag (root, root->getAttrib (tag));
+ if (strcmp(tag, "Make") == 0 ||
+ strcmp(tag, "Model") == 0)
+ {
+ t->initString (val);
+ root->addTagFront (t);
+ } else {
+ delete t;
+ }
+ }
+ *p = '<';
+ *tagend = '>';
+ }
+ *end = '<';
+ }
+ if ((p = strstr(xmp, "xmlns:exif")) != NULL &&
+ (end = strstr(p, "")) != NULL)
+ {
+ *end = '\0';
+ while ((p = strstr(p, "')) == NULL) {
+ break;
+ }
+ *tagend = '\0';
+ char *val = &tagend[1];
+ if ((p = strstr(val, "';
+ break;
+ }
+ *p = '\0';
+ if (exif->getDirectory()->getAttrib (tag) && !exif->getDirectory()->getTag (tag)) {
+ Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib (tag));
+ int num, denom;
+ struct tm tm;
+ if (strcmp(tag, "ApertureValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
+ t->initRational (num, denom);
+ exif->getDirectory()->addTagFront (t);
+ // we also make an "FNumber" tag since many tools don't interpret ApertureValue
+ // according to Exif standard
+ t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"FNumber"));
+ double f = pow(sqrt(2.0), ((double)num / denom));
+ if (f > 10.0) {
+ t->initRational ((int)floor(f), 1);
+ } else {
+ t->initRational ((int)floor(f * 10.0), 10);
+ }
+ exif->getDirectory()->addTagFront (t);
+ } else if (strcmp(tag, "ShutterSpeedValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
+ t->initRational (num, denom);
+ exif->getDirectory()->addTagFront (t);
+ // we also make an "ExposureTime" tag since many tools don't interpret ShutterSpeedValue
+ // according to Exif standard
+ t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"ExposureTime"));
+ double f = 1.0 / pow(2.0, ((double)num / denom));
+ if (f > 10.0) {
+ t->initRational ((int)floor(f), 1);
+ } else if (f > 1.0) {
+ t->initRational ((int)floor(f * 10.0), 10);
+ } else if (f == 1.0) {
+ t->initRational (1, 1);
+ } else {
+ f = 1.0 / f;
+ static const double etimes[] = {
+ 10000, 8000, 6400, 6000, 5000,
+ 4000, 3200, 3000, 2500,
+ 2000, 1600, 1500, 1250,
+ 1000, 800, 750, 640,
+ 500, 400, 350, 320,
+ 250, 200, 180, 160,
+ 125, 100, 90, 80,
+ 60, 50, 45, 40,
+ 30, 25, 22, 20,
+ 15, 13, 11, 10,
+ 8, 6, 5,
+ 4, 3, 2.5,
+ 2, 1.6, 1.5, 1.3,
+ 1, -1 };
+ double diff = etimes[0];
+ int idx = -1;
+ for (int i = 1; etimes[i] > 0; i++) {
+ if (abs(etimes[i] - f) < diff) {
+ idx = i;
+ diff = abs(etimes[i] - f);
+ }
+ }
+ if (idx != -1 && f < etimes[0]) {
+ f = etimes[idx];
+ }
+ if (f < 2) {
+ t->initRational (10, (int)(10 * f));
+ } else {
+ t->initRational (1, (int)f);
+ }
+ }
+ exif->getDirectory()->addTagFront (t);
+ } else if (strcmp(tag, "FocalLength") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
+ t->initRational (num, denom);
+ exif->getDirectory()->addTagFront (t);
+ } else if (strcmp(tag, "ISOSpeedRatings") == 0) {
+ t->initInt (atoi(val), LONG);
+ exif->getDirectory()->addTagFront (t);
+ } else if (strcmp(tag, "DateTimeOriginal") == 0 &&
+ sscanf(val, "%d-%d-%dT%d:%d:%dZ",
+ &tm.tm_year, &tm.tm_mon,
+ &tm.tm_mday, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec) == 6)
+ {
+ char tstr[64];
+ sprintf(tstr, "%02d:%02d:%02d %02d:%02d:%02d", tm.tm_year, tm.tm_mon,
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ t->initString (tstr);
+ exif->getDirectory()->addTagFront (t);
+ } else {
+ delete t;
+ }
+ }
+ *p = '<';
+ *tagend = '>';
+ }
+ *end = '<';
+ }
+}
+
TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf
// read tiff header
@@ -1325,9 +1568,9 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order, skipIgnored);
// fix ISO issue with nikon and panasonic cameras
+ Tag* make = root->getTag ("Make");
Tag* exif = root->getTag ("Exif");
if (exif && !exif->getDirectory()->getTag("ISOSpeedRatings")) {
- Tag* make = root->getTag ("Make");
if (make && !strncmp((char*)make->getValue(), "NIKON", 5)) {
Tag* mn = exif->getDirectory()->getTag("MakerNote");
if (mn) {
@@ -1351,6 +1594,22 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
}
}
+ parse_leafdata(root, order);
+
+ if (!root->getTag("Orientation")) {
+ if (make && !strncmp((char*)make->getValue(), "Phase One", 9)) {
+ int orientation = 0;
+ Tag *iw = root->getTag("ImageWidth");
+ if (iw) {
+ // from dcraw, derive orientation from image width
+ orientation = "0653"[iw->toInt()&3]-'0';
+ }
+ Tag *t = new Tag (root, root->getAttrib ("Orientation"));
+ t->initInt (orientation, SHORT);
+ root->addTagFront (t);
+ }
+ }
+
// root->printAll ();
return root;
diff --git a/rtexif/stdattribs.cc b/rtexif/stdattribs.cc
index 64c7d8249..06aacf9fd 100644
--- a/rtexif/stdattribs.cc
+++ b/rtexif/stdattribs.cc
@@ -540,6 +540,7 @@ const TagAttrib iopAttribs[] = {
{0, 1, 0, 0, 0x828d, "CFAPatternDim", &stdInterpreter},
{0, 1, 0, 0, 0x828e, "CFAPattern", &cfaInterpreter},
{0, 1, 1, 0, 0x8298, "Copyright", &stdInterpreter},
+ {0, 0, 0, 0, 0x8606, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format
{0, 1, 0, exifAttribs, 0x8769, "Exif", &stdInterpreter},
{0, 2, 0, 0, 0x8773, "ICCProfile", &stdInterpreter},
{0, 2, 0, 0, 0x83BB, "IPTCData", &stdInterpreter},