//
//-----------------------------------------------------------------------------
// Foto Metatags extrahieren
//
//
// EXIF library von             https://github.com/exif-js/exif-js
//
// EXIF hat problemlos funktioniert, hab aber Anpassungen für EXIF 2.32 gemacht.
// IPTC hat schon erhebliche Korrekturen erfordert.
// XMP hab ich in einfacher Form, nur kompletten Header als XML-String,
// selber ergänzt.
// Ein primitiver nicht validierender XML-Parser ist hier:
//        metatags.readXmpTag
//        metatags.getAttr
// Beispiel für XMP-Bild-Titel:
//        xmp = EXIF.getXmpHeader (img);
//        XMPtitel = metatags.readXmpTag (xmp, "dc:title.rdf:Alt.rdf:li");
//
// Copyright                    peter@danninger.eu                12. 01. 2022
//-----------------------------------------------------------------------------
//
// Diverse globale Variable und Default-Einstellungen:
//
//-----------------------------------------------------------------------------
var metatags = metatags || {};                        // Namespace
//-----------------------------------------------------------------------------
metatags._version = "12.01.2022";                     // Version
//
metatags._EXIF = new Object ();                       // einige EXIF Metadaten des Fotos
metatags._IPTC = new Object ();                       // einige IPTC Metadaten des Fotos
metatags._XMP = new Object ();                        // einige XMP Metadaten des Fotos

//
// einfache Hilfsfunktion zum improvisierten Auswerten des XMP Headers (nicht validierend).
// Einen XML-DOM aufzubauen und auszuwerten halte ich nicht für angebracht.
// Die wenigen für uns brauchbaren Infos bekommen wir so einfacher extrahiert.
//
metatags.readXmpTag = function (xmp, tags, attname) {     // XMP-String, tag1[.tag2. ...], [Attributname]
  var i, j, k, l, t, u, av, an;
  var xmpH = new String (xmp);
  var tag = "";
  var nextTag = "";
  var aname = new String (attname).trim();
  if (aname == null || aname == "undefined")      aname = "";
  i = tags.indexOf (".");
  if (i > 0) {
    tag = tags.substring (0, i);
    nextTag = tags.substring (i+1);
  } else {
    tag = tags;
    nextTag = "";
  }
  i = xmpH.indexOf ("<" + tag + ">");
  j = xmpH.indexOf ("</" + tag + ">");
  l = tag.length + 2;
  //
  // tag ohne Attributen mit children   <tag>value</tag>
  //
  if (i >= 0 && j > 0) {
    t = xmpH.substring (i+l, j);
    if (nextTag != "")                  return (metatags.readXmpTag (t, nextTag, aname));
    return (t);
  }
  i = xmpH.indexOf ("<" + tag + "");
  //
  // tag mit Attributen ohne children   <tag attribute />
  //
  if (i >= 0 && j < 0) {
    if (aname == "")                    return ("");    // keine children :-(
    u = xmpH.substring (i+l);
    k = u.indexOf ("/>");
    t = u.substring (0, k);
    t = metatags.getAttr (t, aname);    // Atribut extrahieren
    return (t);
  }
  //
  // tag mit Attributen und children   <tag attribute>value</tag>
  //
  if (i >= 0 && j > 0) {
    t = xmpH.substring (i+l, j);
    if (nextTag != "") {
      return (metatags.readXmpTag (t, nextTag, aname));
    }
    if (aname != "") {
      u = xmpH.substring (i+l);
      k = u.indexOf ("/>");
      t = u.substring (0, k);
      t = metatags.getAttr (t, aname);  // Atribut extrahieren
      return (t);
    }
    k = t.indexOf (">");
    if (k > 0)                          t = t.substring (k+1);
    return (t);
  }
  return ("");
}

//
// Attribut value extrahieren (für metatags.readXmpTag)
//
metatags.getAttr = function (at, aname) {
  var i, l, an, av;
  var t = at;
  l = aname.length;
  i = t.indexOf (aname);
  if (i >= 0)     t = t.substring (i + l);
  i = t.indexOf ("=");
  if (i >= 0)     t = t.substring (i + 1);
  i = t.indexOf ("\"");
  if (i >= 0) {
    t = t.substring (i + 1);
    i = t.indexOf ("\"");
    if (i > 0)    t = t.substring (0, i);
    else          t = "";
  } else {
    i = t.indexOf ("\'");
    if (i >= 0) {
      t = t.substring (i + 1);
      i = t.indexOf ("\'");
      if (i > 0)    t = t.substring (0, i);
      else          t = "";
    } else {
      t = "";
    }
  }
  return (t);
}

// 
// EXIF dateTime String in Sekunden seit 1970 wandeln, negativ wenn ungültig
// z.B.   2015:08:11 10:58:08
//
metatags.date2sec = function (dateTime) {
  var date, sec;
  var timeA = new Array ();
  var un = "undefined";
  if (typeof dateTime == un || dateTime == un)    return (-1);      // ungültiger Datum-Zeit-String
  if (dateTime.length != 19)                      return (-2);      // ungültiger Datum-Zeit-String
  dateTime = dateTime.replace (' ', ':');
  timeA = dateTime.split (':');
  if (timeA.length != 6)                          return (-3);      // ungültiger Datum-Zeit-String
  date = new Date (timeA[0], timeA[1], timeA[2], timeA[3], timeA[4], timeA[5]);
  sec = Math.floor (date.getTime () / 1000);                        // Sekunden (seit 1.1.1970)
  if (sec <= 0)                                   return (-4);      // ungültiger Datum-Zeit-String
  return (sec);
}

//
// EXIF, IPTC und XMP Daten extrahieren
//
metatags.readMetaData = function (img, callback) {
  if (typeof (img) != "object") {
    alert (strings.imgErr);                 // problem with image
    return;
  }
  //
  // EXIF library from              https://github.com/exif-js/exif-js
  // ergänzt für IPTC & XMP         peter@danninger.eu
  //
  // IPTC konnte einfach ergänzt werden
  // XMP wird komplett gelesen und selbst ausgewertet
  //
  EXIF.getData (img, function () {          // callback function from exif.js
    //
    var s;
    s = EXIF.getExifAll (img);
    s = escape (s);
    if (s == "ChromeErr")     alert (strings.ChromeErr);
    else if (s == "")         alert (strings.noMeta);
    metatags._EXIF.exifAll = decodeURIComponent (s);
    s = EXIF.getIptcAll (img);
    s = escape (s);
    metatags._EXIF.iptcAll = decodeURIComponent (s);
    s = EXIF.getXmpHeader (img);
    s = escape (s);
    metatags._EXIF.xmpAll = decodeURIComponent (s);
    var un = "undefined";
    //
    // diverse EXIF Header (Strings)
    // kann beliebig erweitert werden (siehe exif.js)
    //
    metatags._EXIF.exifVers = new String (EXIF.getExifTag (img, "ExifVersion"));                  // EXIF Version
    metatags._EXIF.make =  new String (EXIF.getExifTag (img, "Make"));                            // EXIF Hersteller
    metatags._EXIF.model = new String (EXIF.getExifTag (img, "Model"));                           // EXIF Kamera
    metatags._EXIF.artist = new String (EXIF.getExifTag (img, "Artist"));                         // EXIF Fotograf (Lightroom)
    //
    metatags._EXIF.expTime = new String (EXIF.getExifTag (img, "ExposureTime"));                  // EXIF Kamera Belichtungszeit
    if (typeof metatags._EXIF.expTime != un && metatags._EXIF.expTime != un) {
      metatags._EXIF.expTime = Math.floor (metatags._EXIF.expTime * 10000000000) / 10000000000;
    }
    metatags._EXIF.expBias = new String (EXIF.getExifTag (img, "ExposureBias"));                  // EXIF Kamera Belichtungskorrektur
    metatags._EXIF.ISOSpeed = new String (EXIF.getExifTag (img, "PhotographicSensitivity"));      // EXIF Kamera ISO Empfindlichkeit
    metatags._EXIF.pixelX = new String (EXIF.getExifTag (img, "PixelXDimension"));                // EXIF Kamera Bildbreite
    metatags._EXIF.pixelY = new String (EXIF.getExifTag (img, "PixelYDimension"));                // EXIF Kamera Bildhöhe
    //
    metatags._EXIF.desc = new String (EXIF.getExifTag (img, "ImageDescription"));                 // EXIF Bildunterschrift (Lightroom)
    metatags._EXIF.rights = new String (EXIF.getExifTag (img, "Copyright"));                      // EXIF Copyrightinfo (Lightroom)
    metatags._EXIF.userComment = new String (EXIF.getExifTag (img, "UserComment"));               // EXIF UserComment (Lightroom)
    //
    metatags._EXIF.dateTimeOriginal = new String (EXIF.getExifTag (img, "DateTimeOriginal"));     // EXIF Aufnahmezeit         (Original)
    metatags._EXIF.dateTimeDigital = new String (EXIF.getExifTag (img, "DateTimeDigitized"));     // EXIF Digitalisierungszeit (CreateDate)
    metatags._EXIF.dateTime = new String (EXIF.getExifTag (img, "DateTime"));                     // EXIF Bearbeitungszeit     (ModifyDate)
    // nicht verwendet, da durch Lightroom derzeit nicht unterstützt
    metatags._EXIF.offsetTimeOriginal = new String (EXIF.getExifTag (img, "OffsetTimeOriginal")); // EXIF Zeitverschiebung (Original)
    metatags._EXIF.offsetTimeDigital = new String (EXIF.getExifTag (img, "OffsetTimeDigitized")); // EXIF Zeitverschiebung (CreateDate)
    metatags._EXIF.offsetTime = new String (EXIF.getExifTag (img, "OffsetTime"));                 // EXIF Zeitverschiebung (ModifyDate)    
    //
    metatags._EXIF.focalLen = new String (EXIF.getExifTag (img, "FocalLength"));                  // EXIF Objektiv Brennweite
    metatags._EXIF.fNumber = new String (EXIF.getExifTag (img, "FNumber"));                       // EXIF Objektiv Blende
    metatags._EXIF.subjDist = new String (EXIF.getExifTag (img, "SubjectDistance"));              // EXIF Objektiv Entfernung
    //
    // EXIF lat nach Grad konvertieren
    //
    metatags._EXIF.latGrad = "";
    var lat = EXIF.getExifTag (img, "GPSLatitude");                             // EXIF lat (Array, 3 RATIONAL's)
    if (typeof lat == "object" && lat.length == 3) {                            // schon gewandelt durch library
      //alert (lat[0]+"     "+lat[1]+"     "+lat[2]);
      //alert (lat[0].numerator+"     "+lat[1].numerator+"     "+lat[2].numerator);
      //alert (lat[0].denominator+"     "+lat[1].denominator+"     "+lat[2].denominator);
      metatags._EXIF.latGrad = parseFloat (lat[0]) + parseFloat (lat[1] / 60) + parseFloat (lat[2] / 60 / 60);
      var latR =  EXIF.getExifTag (img, "GPSLatitudeRef");
      switch (latR) {
        case ('N'): break;
        case ('S'): metatags._EXIF.latGrad = -metatags._EXIF.latGrad; 
                    break;
        default:    metatags._EXIF.latGrad = "";
      }
    } else {
      metatags._EXIF.latGrad = "";
    }
    if (isNaN (metatags._EXIF.latGrad) || metatags._EXIF.latGrad < -90 || metatags._EXIF.latGrad > 90) {
      metatags._EXIF.latGrad = "";
    }
    metatags._EXIF.latGrad = Math.floor (metatags._EXIF.latGrad * 10000000000) / 10000000000;
    //
    // EXIF lon nach Grad konvertieren
    //
    metatags._EXIF.lonGrad = "";
    var lon = EXIF.getExifTag (img, "GPSLongitude");                            // EXIF lon (Array, 3 RATIONAL's)
    if (typeof lon == "object" && lon.length == 3) {                            // schon gewandelt durch library
      //alert (lon[0]+"     "+lon[1]+"     "+lon[2]);
      //alert (lon[0].numerator+"     "+lon[1].numerator+"     "+lon[2].numerator);
      //alert (lon[0].denominator+"     "+lon[1].denominator+"     "+lon[2].denominator);
      metatags._EXIF.lonGrad = parseFloat (lon[0]) + parseFloat (lon[1] / 60) + parseFloat (lon[2] / 60 / 60);
      var lonR =  EXIF.getExifTag (img, "GPSLongitudeRef");
      switch (lonR) {
        case ('E'): break;
        case ('W'): metatags._EXIF.lonGrad = -metatags._EXIF.lonGrad; break;
        default:    metatags._EXIF.lonGrad = "";
      }
    } else {
      metatags._EXIF.lonGrad = "";
    }
    if (isNaN (metatags._EXIF.lonGrad) || metatags._EXIF.lonGrad < -180 || metatags._EXIF.lonGrad > 180) {
      metatags._EXIF.lonGrad = "";
    }
    metatags._EXIF.lonGrad = Math.floor (metatags._EXIF.lonGrad * 10000000000) / 10000000000;
    //
    // EXIF ele auswerten (eigentlich unwichtig)
    //
    var ele =   new String (EXIF.getExifTag (img, "GPSAltitude"));              // EXIF ele
    var eleR =  EXIF.getExifTag (img, "GPSAltitudeRef");                        // Byte
    if (eleR == 1)        ele = -ele;
    if (isNaN (ele))      ele = "";
    else                  ele = Math.floor (ele);
    metatags._EXIF.ele = ele;
    //
    // diverse IPTC Header
    //
    metatags._IPTC.title = new String (EXIF.getIptcTag (img, "ObjectName"));            // IPTC Titel (Lightroom)
    metatags._IPTC.headline = new String (EXIF.getIptcTag (img, "Headline"));           // IPTC Überschrift (Lightroom)
    metatags._IPTC.desc = new String (EXIF.getIptcTag (img, "Caption"));                // IPTC Bildunterschrift (Lightroom)
    metatags._IPTC.byline = new String (EXIF.getIptcTag (img, "Byline"));               // IPTC Künstler (Lightroom)
    metatags._IPTC.rights = new String (EXIF.getIptcTag (img, "Copyright"));            // IPTC Copyrightinfo (Lightroom)
    metatags._IPTC.country = new String (EXIF.getIptcTag (img, "CountryName"));         // IPTC Country (Lightroom)
    metatags._IPTC.province = new String (EXIF.getIptcTag (img, "ProvinceState"));      // IPTC Province (Lightroom)
    metatags._IPTC.city = new String (EXIF.getIptcTag (img, "City"));                   // IPTC City (Lightroom)
    metatags._IPTC.location = new String (EXIF.getIptcTag (img, "SubLocation"));        // IPTC Location (Lightroom)
    //alert ("IPTC: rights: "+metatags._IPTC.rights+"   title: "+metatags._IPTC.title+"   headline: "+metatags._IPTC.headline+"   desc: "+metatags._IPTC.desc);
    //
    // diverse XMP Header
    //
    var xmp = EXIF.getXmpHeader (img);
    //alert (xmp);                      // show xmp header
    metatags._XMP.title = metatags.readXmpTag (xmp, "dc:title.rdf:Alt.rdf:li");                   // XMP Titel (Lightroom)
    metatags._XMP.headline = metatags.readXmpTag (xmp, "rdf:Description", "photoshop:Headline");  // XMP Bildüberschrift (Lightroom)
    metatags._XMP.desc = metatags.readXmpTag (xmp, "dc:description.rdf:Alt.rdf:li");              // XMP Bildunterschrift (Lightroom)
    metatags._XMP.creator = metatags.readXmpTag (xmp, "dc:creator.rdf:Seq.rdf:li");               // XMP Ersteller (Lightroom)
    metatags._XMP.rights = metatags.readXmpTag (xmp, "dc:rights.rdf:Alt.rdf:li");                 // XMP Copyrightinfo (Lightroom)
    metatags._XMP.creaTool = metatags.readXmpTag (xmp, "rdf:Description", "xmp:CreatorTool");     // XMP Foto-Software, z.B: Lightroom
    metatags._XMP.rating = metatags.readXmpTag (xmp, "rdf:Description", "xmp:Rating");            // XMP Rating, z.B.: "4"
    metatags._XMP.label = metatags.readXmpTag (xmp, "rdf:Description", "xmp:Label");              // XMP Label, z.B.: "Grün"
    metatags._XMP.country = metatags.readXmpTag (xmp, "rdf:Description", "photoshop:Country");    // XMP Country (Lightroom)
    metatags._XMP.province = metatags.readXmpTag (xmp, "rdf:Description", "photoshop:State");     // XMP State, Province (Lightroom)
    metatags._XMP.city = metatags.readXmpTag (xmp, "rdf:Description", "photoshop:City");          // XMP City (Lightroom)
    metatags._XMP.location = metatags.readXmpTag (xmp, "rdf:Description", "photoshop:Location");  // XMP City (Lightroom)
    //alert ("XMP: rights: "+metatags._XMP.rights+"   title: "+metatags._XMP.title+"   headline: "+metatags._XMP.headline+"   desc: "+metatags._XMP.desc);
    if (callback) {
      callback ();                    // callback
    }
  });
}

//
// the end
//
