Guest User

Untitled

a guest
Feb 4th, 2019
293
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function() {
  2.  
  3.     var debug = false;
  4.  
  5.     var root = this;
  6.  
  7.     var EXIF = function(obj) {
  8.         if (obj instanceof EXIF) return obj;
  9.         if (!(this instanceof EXIF)) return new EXIF(obj);
  10.         this.EXIFwrapped = obj;
  11.     };
  12.  
  13.     if (typeof exports !== 'undefined') {
  14.         if (typeof module !== 'undefined' && module.exports) {
  15.             exports = module.exports = EXIF;
  16.         }
  17.         exports.EXIF = EXIF;
  18.     } else {
  19.         root.EXIF = EXIF;
  20.     }
  21.  
  22.     var ExifTags = EXIF.Tags = {
  23.  
  24.         // version tags
  25.         0x9000 : "ExifVersion",             // EXIF version
  26.         0xA000 : "FlashpixVersion",         // Flashpix format version
  27.  
  28.         // colorspace tags
  29.         0xA001 : "ColorSpace",              // Color space information tag
  30.  
  31.         // image configuration
  32.         0xA002 : "PixelXDimension",         // Valid width of meaningful image
  33.         0xA003 : "PixelYDimension",         // Valid height of meaningful image
  34.         0x9101 : "ComponentsConfiguration", // Information about channels
  35.         0x9102 : "CompressedBitsPerPixel",  // Compressed bits per pixel
  36.  
  37.         // user information
  38.         0x927C : "MakerNote",               // Any desired information written by the manufacturer
  39.         0x9286 : "UserComment",             // Comments by user
  40.  
  41.         // related file
  42.         0xA004 : "RelatedSoundFile",        // Name of related sound file
  43.  
  44.         // date and time
  45.         0x9003 : "DateTimeOriginal",        // Date and time when the original image was generated
  46.         0x9004 : "DateTimeDigitized",       // Date and time when the image was stored digitally
  47.         0x9290 : "SubsecTime",              // Fractions of seconds for DateTime
  48.         0x9291 : "SubsecTimeOriginal",      // Fractions of seconds for DateTimeOriginal
  49.         0x9292 : "SubsecTimeDigitized",     // Fractions of seconds for DateTimeDigitized
  50.  
  51.         // picture-taking conditions
  52.         0x829A : "ExposureTime",            // Exposure time (in seconds)
  53.         0x829D : "FNumber",                 // F number
  54.         0x8822 : "ExposureProgram",         // Exposure program
  55.         0x8824 : "SpectralSensitivity",     // Spectral sensitivity
  56.         0x8827 : "ISOSpeedRatings",         // ISO speed rating
  57.         0x8828 : "OECF",                    // Optoelectric conversion factor
  58.         0x9201 : "ShutterSpeedValue",       // Shutter speed
  59.         0x9202 : "ApertureValue",           // Lens aperture
  60.         0x9203 : "BrightnessValue",         // Value of brightness
  61.         0x9204 : "ExposureBias",            // Exposure bias
  62.         0x9205 : "MaxApertureValue",        // Smallest F number of lens
  63.         0x9206 : "SubjectDistance",         // Distance to subject in meters
  64.         0x9207 : "MeteringMode",            // Metering mode
  65.         0x9208 : "LightSource",             // Kind of light source
  66.         0x9209 : "Flash",                   // Flash status
  67.         0x9214 : "SubjectArea",             // Location and area of main subject
  68.         0x920A : "FocalLength",             // Focal length of the lens in mm
  69.         0xA20B : "FlashEnergy",             // Strobe energy in BCPS
  70.         0xA20C : "SpatialFrequencyResponse",    //
  71.         0xA20E : "FocalPlaneXResolution",   // Number of pixels in width direction per FocalPlaneResolutionUnit
  72.         0xA20F : "FocalPlaneYResolution",   // Number of pixels in height direction per FocalPlaneResolutionUnit
  73.         0xA210 : "FocalPlaneResolutionUnit",    // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
  74.         0xA214 : "SubjectLocation",         // Location of subject in image
  75.         0xA215 : "ExposureIndex",           // Exposure index selected on camera
  76.         0xA217 : "SensingMethod",           // Image sensor type
  77.         0xA300 : "FileSource",              // Image source (3 == DSC)
  78.         0xA301 : "SceneType",               // Scene type (1 == directly photographed)
  79.         0xA302 : "CFAPattern",              // Color filter array geometric pattern
  80.         0xA401 : "CustomRendered",          // Special processing
  81.         0xA402 : "ExposureMode",            // Exposure mode
  82.         0xA403 : "WhiteBalance",            // 1 = auto white balance, 2 = manual
  83.         0xA404 : "DigitalZoomRation",       // Digital zoom ratio
  84.         0xA405 : "FocalLengthIn35mmFilm",   // Equivalent foacl length assuming 35mm film camera (in mm)
  85.         0xA406 : "SceneCaptureType",        // Type of scene
  86.         0xA407 : "GainControl",             // Degree of overall image gain adjustment
  87.         0xA408 : "Contrast",                // Direction of contrast processing applied by camera
  88.         0xA409 : "Saturation",              // Direction of saturation processing applied by camera
  89.         0xA40A : "Sharpness",               // Direction of sharpness processing applied by camera
  90.         0xA40B : "DeviceSettingDescription",    //
  91.         0xA40C : "SubjectDistanceRange",    // Distance to subject
  92.  
  93.         // other tags
  94.         0xA005 : "InteroperabilityIFDPointer",
  95.         0xA420 : "ImageUniqueID"            // Identifier assigned uniquely to each image
  96.     };
  97.  
  98.     var TiffTags = EXIF.TiffTags = {
  99.         0x0100 : "ImageWidth",
  100.         0x0101 : "ImageHeight",
  101.         0x8769 : "ExifIFDPointer",
  102.         0x8825 : "GPSInfoIFDPointer",
  103.         0xA005 : "InteroperabilityIFDPointer",
  104.         0x0102 : "BitsPerSample",
  105.         0x0103 : "Compression",
  106.         0x0106 : "PhotometricInterpretation",
  107.         0x0112 : "Orientation",
  108.         0x0115 : "SamplesPerPixel",
  109.         0x011C : "PlanarConfiguration",
  110.         0x0212 : "YCbCrSubSampling",
  111.         0x0213 : "YCbCrPositioning",
  112.         0x011A : "XResolution",
  113.         0x011B : "YResolution",
  114.         0x0128 : "ResolutionUnit",
  115.         0x0111 : "StripOffsets",
  116.         0x0116 : "RowsPerStrip",
  117.         0x0117 : "StripByteCounts",
  118.         0x0201 : "JPEGInterchangeFormat",
  119.         0x0202 : "JPEGInterchangeFormatLength",
  120.         0x012D : "TransferFunction",
  121.         0x013E : "WhitePoint",
  122.         0x013F : "PrimaryChromaticities",
  123.         0x0211 : "YCbCrCoefficients",
  124.         0x0214 : "ReferenceBlackWhite",
  125.         0x0132 : "DateTime",
  126.         0x010E : "ImageDescription",
  127.         0x010F : "Make",
  128.         0x0110 : "Model",
  129.         0x0131 : "Software",
  130.         0x013B : "Artist",
  131.         0x8298 : "Copyright"
  132.     };
  133.  
  134.     var GPSTags = EXIF.GPSTags = {
  135.         0x0000 : "GPSVersionID",
  136.         0x0001 : "GPSLatitudeRef",
  137.         0x0002 : "GPSLatitude",
  138.         0x0003 : "GPSLongitudeRef",
  139.         0x0004 : "GPSLongitude",
  140.         0x0005 : "GPSAltitudeRef",
  141.         0x0006 : "GPSAltitude",
  142.         0x0007 : "GPSTimeStamp",
  143.         0x0008 : "GPSSatellites",
  144.         0x0009 : "GPSStatus",
  145.         0x000A : "GPSMeasureMode",
  146.         0x000B : "GPSDOP",
  147.         0x000C : "GPSSpeedRef",
  148.         0x000D : "GPSSpeed",
  149.         0x000E : "GPSTrackRef",
  150.         0x000F : "GPSTrack",
  151.         0x0010 : "GPSImgDirectionRef",
  152.         0x0011 : "GPSImgDirection",
  153.         0x0012 : "GPSMapDatum",
  154.         0x0013 : "GPSDestLatitudeRef",
  155.         0x0014 : "GPSDestLatitude",
  156.         0x0015 : "GPSDestLongitudeRef",
  157.         0x0016 : "GPSDestLongitude",
  158.         0x0017 : "GPSDestBearingRef",
  159.         0x0018 : "GPSDestBearing",
  160.         0x0019 : "GPSDestDistanceRef",
  161.         0x001A : "GPSDestDistance",
  162.         0x001B : "GPSProcessingMethod",
  163.         0x001C : "GPSAreaInformation",
  164.         0x001D : "GPSDateStamp",
  165.         0x001E : "GPSDifferential"
  166.     };
  167.  
  168.      // EXIF 2.3 Spec
  169.     var IFD1Tags = EXIF.IFD1Tags = {
  170.         0x0100: "ImageWidth",
  171.         0x0101: "ImageHeight",
  172.         0x0102: "BitsPerSample",
  173.         0x0103: "Compression",
  174.         0x0106: "PhotometricInterpretation",
  175.         0x0111: "StripOffsets",
  176.         0x0112: "Orientation",
  177.         0x0115: "SamplesPerPixel",
  178.         0x0116: "RowsPerStrip",
  179.         0x0117: "StripByteCounts",
  180.         0x011A: "XResolution",
  181.         0x011B: "YResolution",
  182.         0x011C: "PlanarConfiguration",
  183.         0x0128: "ResolutionUnit",
  184.         0x0201: "JpegIFOffset",    // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
  185.         0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
  186.         0x0211: "YCbCrCoefficients",
  187.         0x0212: "YCbCrSubSampling",
  188.         0x0213: "YCbCrPositioning",
  189.         0x0214: "ReferenceBlackWhite"
  190.     };
  191.  
  192.     var StringValues = EXIF.StringValues = {
  193.         ExposureProgram : {
  194.             0 : "Not defined",
  195.             1 : "Manual",
  196.             2 : "Normal program",
  197.             3 : "Aperture priority",
  198.             4 : "Shutter priority",
  199.             5 : "Creative program",
  200.             6 : "Action program",
  201.             7 : "Portrait mode",
  202.             8 : "Landscape mode"
  203.         },
  204.         MeteringMode : {
  205.             0 : "Unknown",
  206.             1 : "Average",
  207.             2 : "CenterWeightedAverage",
  208.             3 : "Spot",
  209.             4 : "MultiSpot",
  210.             5 : "Pattern",
  211.             6 : "Partial",
  212.             255 : "Other"
  213.         },
  214.         LightSource : {
  215.             0 : "Unknown",
  216.             1 : "Daylight",
  217.             2 : "Fluorescent",
  218.             3 : "Tungsten (incandescent light)",
  219.             4 : "Flash",
  220.             9 : "Fine weather",
  221.             10 : "Cloudy weather",
  222.             11 : "Shade",
  223.             12 : "Daylight fluorescent (D 5700 - 7100K)",
  224.             13 : "Day white fluorescent (N 4600 - 5400K)",
  225.             14 : "Cool white fluorescent (W 3900 - 4500K)",
  226.             15 : "White fluorescent (WW 3200 - 3700K)",
  227.             17 : "Standard light A",
  228.             18 : "Standard light B",
  229.             19 : "Standard light C",
  230.             20 : "D55",
  231.             21 : "D65",
  232.             22 : "D75",
  233.             23 : "D50",
  234.             24 : "ISO studio tungsten",
  235.             255 : "Other"
  236.         },
  237.         Flash : {
  238.             0x0000 : "Flash did not fire",
  239.             0x0001 : "Flash fired",
  240.             0x0005 : "Strobe return light not detected",
  241.             0x0007 : "Strobe return light detected",
  242.             0x0009 : "Flash fired, compulsory flash mode",
  243.             0x000D : "Flash fired, compulsory flash mode, return light not detected",
  244.             0x000F : "Flash fired, compulsory flash mode, return light detected",
  245.             0x0010 : "Flash did not fire, compulsory flash mode",
  246.             0x0018 : "Flash did not fire, auto mode",
  247.             0x0019 : "Flash fired, auto mode",
  248.             0x001D : "Flash fired, auto mode, return light not detected",
  249.             0x001F : "Flash fired, auto mode, return light detected",
  250.             0x0020 : "No flash function",
  251.             0x0041 : "Flash fired, red-eye reduction mode",
  252.             0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
  253.             0x0047 : "Flash fired, red-eye reduction mode, return light detected",
  254.             0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
  255.             0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
  256.             0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
  257.             0x0059 : "Flash fired, auto mode, red-eye reduction mode",
  258.             0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
  259.             0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
  260.         },
  261.         SensingMethod : {
  262.             1 : "Not defined",
  263.             2 : "One-chip color area sensor",
  264.             3 : "Two-chip color area sensor",
  265.             4 : "Three-chip color area sensor",
  266.             5 : "Color sequential area sensor",
  267.             7 : "Trilinear sensor",
  268.             8 : "Color sequential linear sensor"
  269.         },
  270.         SceneCaptureType : {
  271.             0 : "Standard",
  272.             1 : "Landscape",
  273.             2 : "Portrait",
  274.             3 : "Night scene"
  275.         },
  276.         SceneType : {
  277.             1 : "Directly photographed"
  278.         },
  279.         CustomRendered : {
  280.             0 : "Normal process",
  281.             1 : "Custom process"
  282.         },
  283.         WhiteBalance : {
  284.             0 : "Auto white balance",
  285.             1 : "Manual white balance"
  286.         },
  287.         GainControl : {
  288.             0 : "None",
  289.             1 : "Low gain up",
  290.             2 : "High gain up",
  291.             3 : "Low gain down",
  292.             4 : "High gain down"
  293.         },
  294.         Contrast : {
  295.             0 : "Normal",
  296.             1 : "Soft",
  297.             2 : "Hard"
  298.         },
  299.         Saturation : {
  300.             0 : "Normal",
  301.             1 : "Low saturation",
  302.             2 : "High saturation"
  303.         },
  304.         Sharpness : {
  305.             0 : "Normal",
  306.             1 : "Soft",
  307.             2 : "Hard"
  308.         },
  309.         SubjectDistanceRange : {
  310.             0 : "Unknown",
  311.             1 : "Macro",
  312.             2 : "Close view",
  313.             3 : "Distant view"
  314.         },
  315.         FileSource : {
  316.             3 : "DSC"
  317.         },
  318.  
  319.         Components : {
  320.             0 : "",
  321.             1 : "Y",
  322.             2 : "Cb",
  323.             3 : "Cr",
  324.             4 : "R",
  325.             5 : "G",
  326.             6 : "B"
  327.         }
  328.     };
  329.  
  330.     function addEvent(element, event, handler) {
  331.         if (element.addEventListener) {
  332.             element.addEventListener(event, handler, false);
  333.         } else if (element.attachEvent) {
  334.             element.attachEvent("on" + event, handler);
  335.         }
  336.     }
  337.  
  338.     function imageHasData(img) {
  339.         return !!(img.exifdata);
  340.     }
  341.  
  342.  
  343.     function base64ToArrayBuffer(base64, contentType) {
  344.         contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
  345.         base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
  346.         var binary = atob(base64);
  347.         var len = binary.length;
  348.         var buffer = new ArrayBuffer(len);
  349.         var view = new Uint8Array(buffer);
  350.         for (var i = 0; i < len; i++) {
  351.             view[i] = binary.charCodeAt(i);
  352.         }
  353.         return buffer;
  354.     }
  355.  
  356.     function objectURLToBlob(url, callback) {
  357.         var http = new XMLHttpRequest();
  358.         http.open("GET", url, true);
  359.         http.responseType = "blob";
  360.         http.onload = function(e) {
  361.             if (this.status == 200 || this.status === 0) {
  362.                 callback(this.response);
  363.             }
  364.         };
  365.         http.send();
  366.     }
  367.  
  368.     function getImageData(img, callback) {
  369.         function handleBinaryFile(binFile) {
  370.             var data = findEXIFinJPEG(binFile);
  371.             img.exifdata = data || {};
  372.             var iptcdata = findIPTCinJPEG(binFile);
  373.             img.iptcdata = iptcdata || {};
  374.             if (EXIF.isXmpEnabled) {
  375.                var xmpdata= findXMPinJPEG(binFile);
  376.                img.xmpdata = xmpdata || {};              
  377.             }
  378.             if (callback) {
  379.                 callback.call(img);
  380.             }
  381.         }
  382.  
  383.         if (img.src) {
  384.             if (/^data\:/i.test(img.src)) { // Data URI
  385.                 var arrayBuffer = base64ToArrayBuffer(img.src);
  386.                 handleBinaryFile(arrayBuffer);
  387.  
  388.             } else if (/^blob\:/i.test(img.src)) { // Object URL
  389.                 var fileReader = new FileReader();
  390.                 fileReader.onload = function(e) {
  391.                     handleBinaryFile(e.target.result);
  392.                 };
  393.                 objectURLToBlob(img.src, function (blob) {
  394.                     fileReader.readAsArrayBuffer(blob);
  395.                 });
  396.             } else {
  397.                 var http = new XMLHttpRequest();
  398.                 http.onload = function() {
  399.                     if (this.status == 200 || this.status === 0) {
  400.                         handleBinaryFile(http.response);
  401.                     } else {
  402.                         throw "Could not load image";
  403.                     }
  404.                     http = null;
  405.                 };
  406.                 http.open("GET", img.src, true);
  407.                 http.responseType = "arraybuffer";
  408.                 http.send(null);
  409.             }
  410.         } else if (self.FileReader && (img instanceof self.Blob || img instanceof self.File)) {
  411.             var fileReader = new FileReader();
  412.             fileReader.onload = function(e) {
  413.                 if (debug) console.log("Got file of length " + e.target.result.byteLength);
  414.                 handleBinaryFile(e.target.result);
  415.             };
  416.  
  417.             fileReader.readAsArrayBuffer(img);
  418.         }
  419.     }
  420.  
  421.     function findEXIFinJPEG(file) {
  422.         var dataView = new DataView(file);
  423.  
  424.         if (debug) console.log("Got file of length " + file.byteLength);
  425.         if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
  426.             if (debug) console.log("Not a valid JPEG");
  427.             return false; // not a valid jpeg
  428.         }
  429.  
  430.         var offset = 2,
  431.             length = file.byteLength,
  432.             marker;
  433.  
  434.         while (offset < length) {
  435.             if (dataView.getUint8(offset) != 0xFF) {
  436.                 if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
  437.                 return false; // not a valid marker, something is wrong
  438.             }
  439.  
  440.             marker = dataView.getUint8(offset + 1);
  441.             if (debug) console.log(marker);
  442.  
  443.             // we could implement handling for other markers here,
  444.             // but we're only looking for 0xFFE1 for EXIF data
  445.  
  446.             if (marker == 225) {
  447.                 if (debug) console.log("Found 0xFFE1 marker");
  448.  
  449.                 return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
  450.  
  451.                 // offset += 2 + file.getShortAt(offset+2, true);
  452.  
  453.             } else {
  454.                 offset += 2 + dataView.getUint16(offset+2);
  455.             }
  456.  
  457.         }
  458.  
  459.     }
  460.  
  461.     function findIPTCinJPEG(file) {
  462.         var dataView = new DataView(file);
  463.  
  464.         if (debug) console.log("Got file of length " + file.byteLength);
  465.         if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
  466.             if (debug) console.log("Not a valid JPEG");
  467.             return false; // not a valid jpeg
  468.         }
  469.  
  470.         var offset = 2,
  471.             length = file.byteLength;
  472.  
  473.  
  474.         var isFieldSegmentStart = function(dataView, offset){
  475.             return (
  476.                 dataView.getUint8(offset) === 0x38 &&
  477.                 dataView.getUint8(offset+1) === 0x42 &&
  478.                 dataView.getUint8(offset+2) === 0x49 &&
  479.                 dataView.getUint8(offset+3) === 0x4D &&
  480.                 dataView.getUint8(offset+4) === 0x04 &&
  481.                 dataView.getUint8(offset+5) === 0x04
  482.             );
  483.         };
  484.  
  485.         while (offset < length) {
  486.  
  487.             if ( isFieldSegmentStart(dataView, offset )){
  488.  
  489.                 // Get the length of the name header (which is padded to an even number of bytes)
  490.                 var nameHeaderLength = dataView.getUint8(offset+7);
  491.                 if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
  492.                 // Check for pre photoshop 6 format
  493.                 if(nameHeaderLength === 0) {
  494.                     // Always 4
  495.                     nameHeaderLength = 4;
  496.                 }
  497.  
  498.                 var startOffset = offset + 8 + nameHeaderLength;
  499.                 var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
  500.  
  501.                 return readIPTCData(file, startOffset, sectionLength);
  502.  
  503.                 break;
  504.  
  505.             }
  506.  
  507.  
  508.             // Not the marker, continue searching
  509.             offset++;
  510.  
  511.         }
  512.  
  513.     }
  514.     var IptcFieldMap = {
  515.         0x78 : 'caption',
  516.         0x6E : 'credit',
  517.         0x19 : 'keywords',
  518.         0x37 : 'dateCreated',
  519.         0x50 : 'byline',
  520.         0x55 : 'bylineTitle',
  521.         0x7A : 'captionWriter',
  522.         0x69 : 'headline',
  523.         0x74 : 'copyright',
  524.         0x0F : 'category'
  525.     };
  526.     function readIPTCData(file, startOffset, sectionLength){
  527.         var dataView = new DataView(file);
  528.         var data = {};
  529.         var fieldValue, fieldName, dataSize, segmentType, segmentSize;
  530.         var segmentStartPos = startOffset;
  531.         while(segmentStartPos < startOffset+sectionLength) {
  532.             if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
  533.                 segmentType = dataView.getUint8(segmentStartPos+2);
  534.                 if(segmentType in IptcFieldMap) {
  535.                     dataSize = dataView.getInt16(segmentStartPos+3);
  536.                     segmentSize = dataSize + 5;
  537.                     fieldName = IptcFieldMap[segmentType];
  538.                     fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
  539.                     // Check if we already stored a value with this name
  540.                     if(data.hasOwnProperty(fieldName)) {
  541.                         // Value already stored with this name, create multivalue field
  542.                         if(data[fieldName] instanceof Array) {
  543.                             data[fieldName].push(fieldValue);
  544.                         }
  545.                         else {
  546.                             data[fieldName] = [data[fieldName], fieldValue];
  547.                         }
  548.                     }
  549.                     else {
  550.                         data[fieldName] = fieldValue;
  551.                     }
  552.                 }
  553.  
  554.             }
  555.             segmentStartPos++;
  556.         }
  557.         return data;
  558.     }
  559.  
  560.  
  561.  
  562.     function readTags(file, tiffStart, dirStart, strings, bigEnd) {
  563.         var entries = file.getUint16(dirStart, !bigEnd),
  564.             tags = {},
  565.             entryOffset, tag,
  566.             i;
  567.  
  568.         for (i=0;i<entries;i++) {
  569.             entryOffset = dirStart + i*12 + 2;
  570.             tag = strings[file.getUint16(entryOffset, !bigEnd)];
  571.             if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
  572.             tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
  573.         }
  574.         return tags;
  575.     }
  576.  
  577.  
  578.     function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
  579.         var type = file.getUint16(entryOffset+2, !bigEnd),
  580.             numValues = file.getUint32(entryOffset+4, !bigEnd),
  581.             valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
  582.             offset,
  583.             vals, val, n,
  584.             numerator, denominator;
  585.  
  586.         switch (type) {
  587.             case 1: // byte, 8-bit unsigned int
  588.             case 7: // undefined, 8-bit byte, value depending on field
  589.                 if (numValues == 1) {
  590.                     return file.getUint8(entryOffset + 8, !bigEnd);
  591.                 } else {
  592.                     offset = numValues > 4 ? valueOffset : (entryOffset + 8);
  593.                     vals = [];
  594.                     for (n=0;n<numValues;n++) {
  595.                         vals[n] = file.getUint8(offset + n);
  596.                     }
  597.                     return vals;
  598.                 }
  599.  
  600.             case 2: // ascii, 8-bit byte
  601.                 offset = numValues > 4 ? valueOffset : (entryOffset + 8);
  602.                 return getStringFromDB(file, offset, numValues-1);
  603.  
  604.             case 3: // short, 16 bit int
  605.                 if (numValues == 1) {
  606.                     return file.getUint16(entryOffset + 8, !bigEnd);
  607.                 } else {
  608.                     offset = numValues > 2 ? valueOffset : (entryOffset + 8);
  609.                     vals = [];
  610.                     for (n=0;n<numValues;n++) {
  611.                         vals[n] = file.getUint16(offset + 2*n, !bigEnd);
  612.                     }
  613.                     return vals;
  614.                 }
  615.  
  616.             case 4: // long, 32 bit int
  617.                 if (numValues == 1) {
  618.                     return file.getUint32(entryOffset + 8, !bigEnd);
  619.                 } else {
  620.                     vals = [];
  621.                     for (n=0;n<numValues;n++) {
  622.                         vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
  623.                     }
  624.                     return vals;
  625.                 }
  626.  
  627.             case 5:    // rational = two long values, first is numerator, second is denominator
  628.                 if (numValues == 1) {
  629.                     numerator = file.getUint32(valueOffset, !bigEnd);
  630.                     denominator = file.getUint32(valueOffset+4, !bigEnd);
  631.                     val = new Number(numerator / denominator);
  632.                     val.numerator = numerator;
  633.                     val.denominator = denominator;
  634.                     return val;
  635.                 } else {
  636.                     vals = [];
  637.                     for (n=0;n<numValues;n++) {
  638.                         numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
  639.                         denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
  640.                         vals[n] = new Number(numerator / denominator);
  641.                         vals[n].numerator = numerator;
  642.                         vals[n].denominator = denominator;
  643.                     }
  644.                     return vals;
  645.                 }
  646.  
  647.             case 9: // slong, 32 bit signed int
  648.                 if (numValues == 1) {
  649.                     return file.getInt32(entryOffset + 8, !bigEnd);
  650.                 } else {
  651.                     vals = [];
  652.                     for (n=0;n<numValues;n++) {
  653.                         vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
  654.                     }
  655.                     return vals;
  656.                 }
  657.  
  658.             case 10: // signed rational, two slongs, first is numerator, second is denominator
  659.                 if (numValues == 1) {
  660.                     return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
  661.                 } else {
  662.                     vals = [];
  663.                     for (n=0;n<numValues;n++) {
  664.                         vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
  665.                     }
  666.                     return vals;
  667.                 }
  668.         }
  669.     }
  670.  
  671.     /**
  672.     * Given an IFD (Image File Directory) start offset
  673.     * returns an offset to next IFD or 0 if it's the last IFD.
  674.     */
  675.     function getNextIFDOffset(dataView, dirStart, bigEnd){
  676.         //the first 2bytes means the number of directory entries contains in this IFD
  677.         var entries = dataView.getUint16(dirStart, !bigEnd);
  678.  
  679.         // After last directory entry, there is a 4bytes of data,
  680.         // it means an offset to next IFD.
  681.         // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD.
  682.  
  683.         return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long
  684.     }
  685.  
  686.     function readThumbnailImage(dataView, tiffStart, firstIFDOffset, bigEnd){
  687.         // get the IFD1 offset
  688.         var IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart+firstIFDOffset, bigEnd);
  689.  
  690.         if (!IFD1OffsetPointer) {
  691.             // console.log('******** IFD1Offset is empty, image thumb not found ********');
  692.             return {};
  693.         }
  694.         else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen
  695.             // console.log('******** IFD1Offset is outside the bounds of the DataView ********');
  696.             return {};
  697.         }
  698.         // console.log('*******  thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
  699.  
  700.         var thumbTags = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd)
  701.  
  702.         // EXIF 2.3 specification for JPEG format thumbnail
  703.  
  704.         // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
  705.         // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
  706.         // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
  707.         // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
  708.         // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
  709.  
  710.         if (thumbTags['Compression']) {
  711.             // console.log('Thumbnail image found!');
  712.  
  713.             switch (thumbTags['Compression']) {
  714.                 case 6:
  715.                     // console.log('Thumbnail image format is JPEG');
  716.                     if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) {
  717.                     // extract the thumbnail
  718.                         var tOffset = tiffStart + thumbTags.JpegIFOffset;
  719.                         var tLength = thumbTags.JpegIFByteCount;
  720.                         thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], {
  721.                             type: 'image/jpeg'
  722.                         });
  723.                     }
  724.                 break;
  725.  
  726.             case 1:
  727.                 console.log("Thumbnail image format is TIFF, which is not implemented.");
  728.                 break;
  729.             default:
  730.                 console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']);
  731.             }
  732.         }
  733.         else if (thumbTags['PhotometricInterpretation'] == 2) {
  734.             console.log("Thumbnail image format is RGB, which is not implemented.");
  735.         }
  736.         return thumbTags;
  737.     }
  738.  
  739.     function getStringFromDB(buffer, start, length) {
  740.         var outstr = "";
  741.         for (var n = start; n < start+length; n++) {
  742.             outstr += String.fromCharCode(buffer.getUint8(n));
  743.         }
  744.         return outstr;
  745.     }
  746.  
  747.     function readEXIFData(file, start) {
  748.         if (getStringFromDB(file, start, 4) != "Exif") {
  749.             if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
  750.             return false;
  751.         }
  752.  
  753.         var bigEnd,
  754.             tags, tag,
  755.             exifData, gpsData,
  756.             tiffOffset = start + 6;
  757.  
  758.         // test for TIFF validity and endianness
  759.         if (file.getUint16(tiffOffset) == 0x4949) {
  760.             bigEnd = false;
  761.         } else if (file.getUint16(tiffOffset) == 0x4D4D) {
  762.             bigEnd = true;
  763.         } else {
  764.             if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
  765.             return false;
  766.         }
  767.  
  768.         if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
  769.             if (debug) console.log("Not valid TIFF data! (no 0x002A)");
  770.             return false;
  771.         }
  772.  
  773.         var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);
  774.  
  775.         if (firstIFDOffset < 0x00000008) {
  776.             if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
  777.             return false;
  778.         }
  779.  
  780.         tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
  781.  
  782.         if (tags.ExifIFDPointer) {
  783.             exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
  784.             for (tag in exifData) {
  785.                 switch (tag) {
  786.                     case "LightSource" :
  787.                     case "Flash" :
  788.                     case "MeteringMode" :
  789.                     case "ExposureProgram" :
  790.                     case "SensingMethod" :
  791.                     case "SceneCaptureType" :
  792.                     case "SceneType" :
  793.                     case "CustomRendered" :
  794.                     case "WhiteBalance" :
  795.                     case "GainControl" :
  796.                     case "Contrast" :
  797.                     case "Saturation" :
  798.                     case "Sharpness" :
  799.                     case "SubjectDistanceRange" :
  800.                     case "FileSource" :
  801.                         exifData[tag] = StringValues[tag][exifData[tag]];
  802.                         break;
  803.  
  804.                     case "ExifVersion" :
  805.                     case "FlashpixVersion" :
  806.                         exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
  807.                         break;
  808.  
  809.                     case "ComponentsConfiguration" :
  810.                         exifData[tag] =
  811.                             StringValues.Components[exifData[tag][0]] +
  812.                             StringValues.Components[exifData[tag][1]] +
  813.                             StringValues.Components[exifData[tag][2]] +
  814.                             StringValues.Components[exifData[tag][3]];
  815.                         break;
  816.                 }
  817.                 tags[tag] = exifData[tag];
  818.             }
  819.         }
  820.  
  821.         if (tags.GPSInfoIFDPointer) {
  822.             gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
  823.             for (tag in gpsData) {
  824.                 switch (tag) {
  825.                     case "GPSVersionID" :
  826.                         gpsData[tag] = gpsData[tag][0] +
  827.                             "." + gpsData[tag][1] +
  828.                             "." + gpsData[tag][2] +
  829.                             "." + gpsData[tag][3];
  830.                         break;
  831.                 }
  832.                 tags[tag] = gpsData[tag];
  833.             }
  834.         }
  835.  
  836.         // extract thumbnail
  837.         tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd);
  838.  
  839.         return tags;
  840.     }
  841.  
  842.    function findXMPinJPEG(file) {
  843.  
  844.         if (!('DOMParser' in self)) {
  845.             // console.warn('XML parsing not supported without DOMParser');
  846.             return;
  847.         }
  848.         var dataView = new DataView(file);
  849.  
  850.         if (debug) console.log("Got file of length " + file.byteLength);
  851.         if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
  852.            if (debug) console.log("Not a valid JPEG");
  853.            return false; // not a valid jpeg
  854.         }
  855.  
  856.         var offset = 2,
  857.             length = file.byteLength,
  858.             dom = new DOMParser();
  859.  
  860.         while (offset < (length-4)) {
  861.             if (getStringFromDB(dataView, offset, 4) == "http") {
  862.                 var startOffset = offset - 1;
  863.                 var sectionLength = dataView.getUint16(offset - 2) - 1;
  864.                 var xmpString = getStringFromDB(dataView, startOffset, sectionLength)
  865.                 var xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8;
  866.                 xmpString = xmpString.substring( xmpString.indexOf( '<x:xmpmeta' ), xmpEndIndex );
  867.  
  868.                 var indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10
  869.                 //Many custom written programs embed xmp/xml without any namespace. Following are some of them.
  870.                 //Without these namespaces, XML is thought to be invalid by parsers
  871.                 xmpString = xmpString.slice(0, indexOfXmp)
  872.                             + 'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" '
  873.                             + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
  874.                             + 'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" '
  875.                             + 'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" '
  876.                             + 'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" '
  877.                             + 'xmlns:exif="http://ns.adobe.com/exif/1.0/" '
  878.                             + 'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" '
  879.                             + 'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" '
  880.                             + 'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" '
  881.                             + 'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" '
  882.                             + 'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" '
  883.                             + xmpString.slice(indexOfXmp)
  884.  
  885.                 var domDocument = dom.parseFromString( xmpString, 'text/xml' );
  886.                 return xml2Object(domDocument);
  887.             } else{
  888.              offset++;
  889.             }
  890.         }
  891.     }
  892.  
  893.     function xml2json(xml) {
  894.         var json = {};
  895.      
  896.         if (xml.nodeType == 1) { // element node
  897.           if (xml.attributes.length > 0) {
  898.             json['@attributes'] = {};
  899.             for (var j = 0; j < xml.attributes.length; j++) {
  900.               var attribute = xml.attributes.item(j);
  901.               json['@attributes'][attribute.nodeName] = attribute.nodeValue;
  902.             }
  903.           }
  904.         } else if (xml.nodeType == 3) { // text node
  905.           return xml.nodeValue;
  906.         }
  907.      
  908.         // deal with children
  909.         if (xml.hasChildNodes()) {
  910.           for(var i = 0; i < xml.childNodes.length; i++) {
  911.             var child = xml.childNodes.item(i);
  912.             var nodeName = child.nodeName;
  913.             if (json[nodeName] == null) {
  914.               json[nodeName] = xml2json(child);
  915.             } else {
  916.               if (json[nodeName].push == null) {
  917.                 var old = json[nodeName];
  918.                 json[nodeName] = [];
  919.                 json[nodeName].push(old);
  920.               }
  921.               json[nodeName].push(xml2json(child));
  922.             }
  923.           }
  924.         }
  925.        
  926.         return json;
  927.     }
  928.  
  929.     function xml2Object(xml) {
  930.         try {
  931.             var obj = {};
  932.             if (xml.children.length > 0) {
  933.               for (var i = 0; i < xml.children.length; i++) {
  934.                 var item = xml.children.item(i);
  935.                 var attributes = item.attributes;
  936.                 for(var idx in attributes) {
  937.                     var itemAtt = attributes[idx];
  938.                     var dataKey = itemAtt.nodeName;
  939.                     var dataValue = itemAtt.nodeValue;
  940.  
  941.                     if(dataKey !== undefined) {
  942.                         obj[dataKey] = dataValue;
  943.                     }
  944.                 }
  945.                 var nodeName = item.nodeName;
  946.  
  947.                 if (typeof (obj[nodeName]) == "undefined") {
  948.                   obj[nodeName] = xml2json(item);
  949.                 } else {
  950.                   if (typeof (obj[nodeName].push) == "undefined") {
  951.                     var old = obj[nodeName];
  952.  
  953.                     obj[nodeName] = [];
  954.                     obj[nodeName].push(old);
  955.                   }
  956.                   obj[nodeName].push(xml2json(item));
  957.                 }
  958.               }
  959.             } else {
  960.               obj = xml.textContent;
  961.             }
  962.             return obj;
  963.           } catch (e) {
  964.               console.log(e.message);
  965.           }
  966.     }
  967.  
  968.     EXIF.enableXmp = function() {
  969.         EXIF.isXmpEnabled = true;
  970.     }
  971.  
  972.     EXIF.disableXmp = function() {
  973.         EXIF.isXmpEnabled = false;
  974.     }
  975.  
  976.     EXIF.getData = function(img, callback) {
  977.         if (((self.Image && img instanceof self.Image)
  978.             || (self.HTMLImageElement && img instanceof self.HTMLImageElement))
  979.             && !img.complete)
  980.             return false;
  981.  
  982.         if (!imageHasData(img)) {
  983.             getImageData(img, callback);
  984.         } else {
  985.             if (callback) {
  986.                 callback.call(img);
  987.             }
  988.         }
  989.         return true;
  990.     }
  991.  
  992.     EXIF.getTag = function(img, tag) {
  993.         if (!imageHasData(img)) return;
  994.         return img.exifdata[tag];
  995.     }
  996.    
  997.     EXIF.getIptcTag = function(img, tag) {
  998.         if (!imageHasData(img)) return;
  999.         return img.iptcdata[tag];
  1000.     }
  1001.  
  1002.     EXIF.getAllTags = function(img) {
  1003.         if (!imageHasData(img)) return {};
  1004.         var a,
  1005.             data = img.exifdata,
  1006.             tags = {};
  1007.         for (a in data) {
  1008.             if (data.hasOwnProperty(a)) {
  1009.                 tags[a] = data[a];
  1010.             }
  1011.         }
  1012.         return tags;
  1013.     }
  1014.    
  1015.     EXIF.getAllIptcTags = function(img) {
  1016.         if (!imageHasData(img)) return {};
  1017.         var a,
  1018.             data = img.iptcdata,
  1019.             tags = {};
  1020.         for (a in data) {
  1021.             if (data.hasOwnProperty(a)) {
  1022.                 tags[a] = data[a];
  1023.             }
  1024.         }
  1025.         return tags;
  1026.     }
  1027.  
  1028.     EXIF.pretty = function(img) {
  1029.         if (!imageHasData(img)) return "";
  1030.         var a,
  1031.             data = img.exifdata,
  1032.             strPretty = "";
  1033.         for (a in data) {
  1034.             if (data.hasOwnProperty(a)) {
  1035.                 if (typeof data[a] == "object") {
  1036.                     if (data[a] instanceof Number) {
  1037.                         strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
  1038.                     } else {
  1039.                         strPretty += a + " : [" + data[a].length + " values]\r\n";
  1040.                     }
  1041.                 } else {
  1042.                     strPretty += a + " : " + data[a] + "\r\n";
  1043.                 }
  1044.             }
  1045.         }
  1046.         return strPretty;
  1047.     }
  1048.  
  1049.     EXIF.readFromBinaryFile = function(file) {
  1050.         return findEXIFinJPEG(file);
  1051.     }
  1052.  
  1053.     if (typeof define === 'function' && define.amd) {
  1054.         define('exif-js', [], function() {
  1055.             return EXIF;
  1056.         });
  1057.     }
  1058. }.call(this));
Add Comment
Please, Sign In to add comment