From bc99de09f2cd54e03e88f08a2933ec236c42641e Mon Sep 17 00:00:00 2001 From: Toutouwai Date: Wed, 27 Apr 2022 10:57:00 -0400 Subject: [PATCH] Update PWImageResizer.js to replace exif.js with piexif.js which corrects an issue with client-side image resize orientation --- .../InputfieldImage/InputfieldImage.module | 2 +- .../InputfieldImage/PWImageResizer.js | 98 +- .../InputfieldImage/PWImageResizer.min.js | 2 +- .../Inputfield/InputfieldImage/piexif.js | 2482 +++++++++++++++++ .../Inputfield/InputfieldImage/piexif.min.js | 1 + 5 files changed, 2516 insertions(+), 69 deletions(-) create mode 100755 wire/modules/Inputfield/InputfieldImage/piexif.js create mode 100644 wire/modules/Inputfield/InputfieldImage/piexif.min.js diff --git a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module index 6358f0cc..16f4c15e 100755 --- a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module +++ b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module @@ -195,7 +195,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu if(!$this->resizeServer && ($this->maxWidth || $this->maxHeight || $this->maxSize)) { $thisURL = $config->urls->InputfieldImage; $jsExt = $config->debug ? "js" : "min.js"; - $config->scripts->add($thisURL . "exif.$jsExt"); + $config->scripts->add($thisURL . "piexif.$jsExt"); $config->scripts->add($thisURL . "PWImageResizer.$jsExt"); $maxSize = str_replace(',', '.', $this->maxSize); $quality = str_replace(',', '.', (float) ($this->clientQuality / 100)); diff --git a/wire/modules/Inputfield/InputfieldImage/PWImageResizer.js b/wire/modules/Inputfield/InputfieldImage/PWImageResizer.js index 40061242..65f5d89b 100644 --- a/wire/modules/Inputfield/InputfieldImage/PWImageResizer.js +++ b/wire/modules/Inputfield/InputfieldImage/PWImageResizer.js @@ -3,8 +3,9 @@ * * Code based on ImageUploader (c) Ross Turner (https://github.com/rossturner/HTML5-ImageUploader). * Adapted for ProcessWire by Ryan as a resizer-only libary with different behavior and some fixes. + * Updates by Robin S. (https://github.com/toutouwai) to replace exif.js with piexif.js library. * - * Requires exif.js (https://github.com/exif-js/exif-js) for JPEG autoRotate functions. + * Requires piexif.js (https://github.com/hMatoba/piexifjs) to retain EXIF data in resized JPG. * * Config settings: * @@ -106,28 +107,7 @@ PWImageResizer.prototype.resize = function(file, completionCallback) { completionCallback(false); return; } - - if(contentType == 'image/jpeg' && This.config.autoRotate) { - // jpeg with autoRotate - This.consoleLog('detecting JPEG image orientation...'); - - if((typeof EXIF.getData === "function") && (typeof EXIF.getTag === "function")) { - This.consoleLog('EXIF.getData starting'); - EXIF.getData(img, function() { - This.consoleLog('EXIF.getData done, orientation:'); - var orientation = EXIF.getTag(this, "Orientation"); - This.consoleLog('image orientation from EXIF tag: ' + orientation); - This.scaleImage(img, orientation, completionCallback); - }); - } else { - This.consoleLog("can't read EXIF data, the Exif.js library not found"); - This.scaleImage(img, 0, completionCallback); - } - - } else { - // png or gif (or jpeg with autoRotate==false) - This.scaleImage(img, 0, completionCallback); - } + This.scaleImage(img, completionCallback); } }; @@ -212,11 +192,10 @@ PWImageResizer.prototype.drawImage = function(context, img, x, y, width, height, * Scale an image * * @param img The element - * @param orientation Orientation number from Exif.js or 0 bypass * @param completionCallback Function to call upon completion * */ -PWImageResizer.prototype.scaleImage = function(img, orientation, completionCallback) { +PWImageResizer.prototype.scaleImage = function(img, completionCallback) { var canvas = document.createElement('canvas'); canvas.width = img.width; @@ -232,48 +211,7 @@ PWImageResizer.prototype.scaleImage = function(img, orientation, completionCallb var styleWidth = canvas.style.width; var height = canvas.height; var styleHeight = canvas.style.height; - - if(typeof orientation === 'undefined') orientation = 1; - - if(orientation) { - if(orientation > 4) { - canvas.width = height; - canvas.style.width = styleHeight; - canvas.height = width; - canvas.style.height = styleWidth; - } - switch(orientation) { - case 2: - ctx.translate(width, 0); - ctx.scale(-1, 1); - break; - case 3: - ctx.translate(width, height); - ctx.rotate(Math.PI); - break; - case 4: - ctx.translate(0, height); - ctx.scale(1, -1); - break; - case 5: - ctx.rotate(0.5 * Math.PI); - ctx.scale(1, -1); - break; - case 6: - ctx.rotate(0.5 * Math.PI); - ctx.translate(0, -height); - break; - case 7: - ctx.rotate(0.5 * Math.PI); - ctx.translate(width, -height); - ctx.scale(-1, 1); - break; - case 8: - ctx.rotate(-0.5 * Math.PI); - ctx.translate(-width, 0); - break; - } - } + ctx.drawImage(img, 0, 0); ctx.restore(); @@ -327,6 +265,32 @@ PWImageResizer.prototype.scaleImage = function(img, orientation, completionCallb this.config.onScale(imageData); } + /** Added by @Toutouwai for change to piexif **/ + if(this.currentFile.type === 'image/jpeg') { + try { + var exifObj = piexif.load(img.src); + // Detect if there is a high likelihood this image has already been rotated according to its EXIF orientation + var orientation = exifObj['0th'][piexif.ImageIFD.Orientation]; + if(orientation > 4 && img.height > img.width) { + // Set EXIF orientation to normal + exifObj['0th'][piexif.ImageIFD.Orientation] = 1; + } + try { + var exifStr = piexif.dump(exifObj); + try { + imageData = piexif.insert(exifStr, imageData); + } catch(error) { + console.error(error); + } + } catch(error) { + console.error(error); + } + } catch(error) { + console.error(error); + } + } + /** End added by @Toutouwai for piexif **/ + completionCallback(this.imageDataToBlob(imageData)); }; diff --git a/wire/modules/Inputfield/InputfieldImage/PWImageResizer.min.js b/wire/modules/Inputfield/InputfieldImage/PWImageResizer.min.js index 3115045b..0257ae6f 100644 --- a/wire/modules/Inputfield/InputfieldImage/PWImageResizer.min.js +++ b/wire/modules/Inputfield/InputfieldImage/PWImageResizer.min.js @@ -1 +1 @@ -var PWImageResizer=function(a){this.setConfig(a)};PWImageResizer.prototype.resize=function(c,d){var b=document.createElement("img");this.currentFile=c;var a=new FileReader();var e=this;var f=c.type.toString();a.onload=function(g){b.src=g.target.result;b.onload=function(){if(!e.needsResize(b,f)){d(false);return}if(f=="image/jpeg"&&e.config.autoRotate){e.consoleLog("detecting JPEG image orientation...");if((typeof EXIF.getData==="function")&&(typeof EXIF.getTag==="function")){e.consoleLog("EXIF.getData starting");EXIF.getData(b,function(){e.consoleLog("EXIF.getData done, orientation:");var h=EXIF.getTag(this,"Orientation");e.consoleLog("image orientation from EXIF tag: "+h);e.scaleImage(b,h,d)})}else{e.consoleLog("can't read EXIF data, the Exif.js library not found");e.scaleImage(b,0,d)}}else{e.scaleImage(b,0,d)}}};a.readAsDataURL(c)};PWImageResizer.prototype.needsResize=function(a,d){var c=false;var b="n/a";if(d!="image/jpeg"&&d!="image/png"&&d!="image/gif"){b="unsupported image content-type: "+d}else{if(this.config.scaleRatio>0){c=true;b="scaleRatio specified"}else{if(this.config.maxWidth>0||this.config.maxHeight>0){if(this.config.maxWidth>0&&a.width>this.config.maxWidth){c=true}if(this.config.maxHeight>0&&a.height>this.config.maxHeight){c=true}b=c?"dimensions exceed max allowed":"dimensions do not require resize"}}}if(!c&&this.config.maxSize>0){if(this.config.maxSize<(a.width*a.height)/1000000){c=true}b=(c?"megapixels exceeds ":"megapixels below ")+this.config.maxSize}if(this.config.debug){this.consoleLog("needsResize="+(c?"Yes":"No")+" ("+b+")")}return c};PWImageResizer.prototype.drawImage=function(d,f,j,i,c,k,b,g,e,a){d.save();if(typeof c==="undefined"){c=f.width}if(typeof k==="undefined"){k=f.height}if(typeof a==="undefined"){a=false}if(a){j-=c/2;i-=k/2}d.translate(j+c/2,i+k/2);var h=2*Math.PI-b*Math.PI/180;d.rotate(h);if(g){flipScale=-1}else{flipScale=1}if(e){flopScale=-1}else{flopScale=1}d.scale(flipScale,flopScale);d.drawImage(f,-c/2,-k/2,c,k);d.restore()};PWImageResizer.prototype.scaleImage=function(i,c,l){var d=document.createElement("canvas");d.width=i.width;d.height=i.height;var p=d.getContext("2d");p.save();var b=d.width;var f=d.style.width;var o=d.height;var e=d.style.height;if(typeof c==="undefined"){c=1}if(c){if(c>4){d.width=o;d.style.width=e;d.height=b;d.style.height=f}switch(c){case 2:p.translate(b,0);p.scale(-1,1);break;case 3:p.translate(b,o);p.rotate(Math.PI);break;case 4:p.translate(0,o);p.scale(1,-1);break;case 5:p.rotate(0.5*Math.PI);p.scale(1,-1);break;case 6:p.rotate(0.5*Math.PI);p.translate(0,-o);break;case 7:p.rotate(0.5*Math.PI);p.translate(b,-o);p.scale(-1,1);break;case 8:p.rotate(-0.5*Math.PI);p.translate(-b,0);break}}p.drawImage(i,0,0);p.restore();var k=d.width/d.height;var g=0;var h="";if(this.config.maxWidth>0||this.config.maxHeight>0){g=Math.min(this.config.maxWidth,k*this.config.maxHeight);h="max width/height of "+this.config.maxWidth+"x"+this.config.maxHeight}if(this.config.maxSize>0&&(this.config.maxSize<(d.width*d.height)/1000000)){var n=Math.floor(Math.sqrt(this.config.maxSize*k)*1000);g=g>0?Math.min(g,n):n;if(n===g){h="max megapixels of "+this.config.maxSize}}if(this.config.scaleRatio){var j=Math.floor(this.config.scaleRatio*d.width);g=g>0?Math.min(g,j):j;if(j==g){h="scale ratio of "+this.config.scaleRatio}}if(g<=0){this.consoleLog("image size is too small to resize");l(false);return}if(this.config.debug){this.consoleLog("original image size: "+d.width+"x"+d.height+" px");this.consoleLog("scaled image size: "+g+"x"+Math.floor(g/k)+" px via "+h)}while(d.width>=(2*g)){d=this.getHalfScaleCanvas(d)}if(d.width>g){d=this.scaleCanvasWithAlgorithm(d,g)}var m=this.config.quality;if(this.currentFile.type!="image/jpeg"){m=1}var a=d.toDataURL(this.currentFile.type,m);if(typeof this.config.onScale==="function"){this.config.onScale(a)}l(this.imageDataToBlob(a))};PWImageResizer.prototype.imageDataToBlob=function(h){var f=";base64,";if(h.indexOf(f)==-1){var e=h.split(",");var g=e[0].split(":")[1];var a=e[1];return new Blob([a],{type:g})}var e=h.split(f);var g=e[0].split(":")[1];var a=window.atob(e[1]);var d=a.length;var c=new Uint8Array(d);for(var b=0;b(y.height-1)?(y.height-1):Math.ceil(p));for(o=0;o(y.width-1)?(y.width-1):Math.ceil(f));e=(o+u.width*q)*4;n=(w+y.width*d)*4;C=(t+y.width*d)*4;m=(w+y.width*c)*4;B=(t+y.width*c)*4;k=f-w;h=p-d;l=z(y.data[n],y.data[C],y.data[m],y.data[B],k,h);u.data[e]=l;s=z(y.data[n+1],y.data[C+1],y.data[m+1],y.data[B+1],k,h);u.data[e+1]=s;v=z(y.data[n+2],y.data[C+2],y.data[m+2],y.data[B+2],k,h);u.data[e+2]=v;x=z(y.data[n+3],y.data[C+3],y.data[m+3],y.data[B+3],k,h);u.data[e+3]=x}}};PWImageResizer.prototype.setConfig=function(a){this.config=a;this.config.debug=this.config.debug||false;if(typeof a.quality=="undefined"){a.quality=1}if(a.quality<0.1){a.quality=0.1}if(a.quality>1){a.quality=1}this.config.quality=a.quality;if((!this.config.maxWidth)||(this.config.maxWidth<0)){this.config.maxWidth=0}if((!this.config.maxHeight)||(this.config.maxHeight<0)){this.config.maxHeight=0}if((!this.config.maxSize)||(this.config.maxSize<0)){this.config.maxSize=null}if((!this.config.scaleRatio)||(this.config.scaleRatio<=0)||(this.config.scaleRatio>=1)){this.config.scaleRatio=null}this.config.autoRotate=true;if(typeof a.autoRotate==="boolean"){this.config.autoRotate=a.autoRotate}if(this.config.maxWidth&&!this.config.maxHeight){this.config.maxHeight=this.config.maxWidth}else{if(this.config.maxHeight&&!this.config.maxWidth){this.config.maxWidth=this.config.maxHeight}else{if(!this.config.maxWidth&&!this.config.maxHeight){}}}};PWImageResizer.prototype.consoleLog=function(a){if(this.config.debug){console.log("PWImageResizer: "+a)}}; \ No newline at end of file +var PWImageResizer=function(config){this.setConfig(config)};PWImageResizer.prototype.resize=function(file,completionCallback){var img=document.createElement("img");this.currentFile=file;var reader=new FileReader;var This=this;var contentType=file.type.toString();reader.onload=function(e){img.src=e.target.result;img.onload=function(){if(!This.needsResize(img,contentType)){completionCallback(false);return}This.scaleImage(img,completionCallback)}};reader.readAsDataURL(file)};PWImageResizer.prototype.needsResize=function(img,contentType){var needsResize=false;var why="n/a";if(contentType!="image/jpeg"&&contentType!="image/png"&&contentType!="image/gif"){why="unsupported image content-type: "+contentType}else if(this.config.scaleRatio>0){needsResize=true;why="scaleRatio specified"}else if(this.config.maxWidth>0||this.config.maxHeight>0){if(this.config.maxWidth>0&&img.width>this.config.maxWidth)needsResize=true;if(this.config.maxHeight>0&&img.height>this.config.maxHeight)needsResize=true;why=needsResize?"dimensions exceed max allowed":"dimensions do not require resize"}if(!needsResize&&this.config.maxSize>0){if(this.config.maxSize0||this.config.maxHeight>0){mWidth=Math.min(this.config.maxWidth,ratio*this.config.maxHeight);resizeType="max width/height of "+this.config.maxWidth+"x"+this.config.maxHeight}if(this.config.maxSize>0&&this.config.maxSize0?Math.min(mWidth,mSize):mSize;if(mSize===mWidth)resizeType="max megapixels of "+this.config.maxSize}if(this.config.scaleRatio){var mScale=Math.floor(this.config.scaleRatio*canvas.width);mWidth=mWidth>0?Math.min(mWidth,mScale):mScale;if(mScale==mWidth)resizeType="scale ratio of "+this.config.scaleRatio}if(mWidth<=0){this.consoleLog("image size is too small to resize");completionCallback(false);return}if(this.config.debug){this.consoleLog("original image size: "+canvas.width+"x"+canvas.height+" px");this.consoleLog("scaled image size: "+mWidth+"x"+Math.floor(mWidth/ratio)+" px via "+resizeType)}while(canvas.width>=2*mWidth){canvas=this.getHalfScaleCanvas(canvas)}if(canvas.width>mWidth){canvas=this.scaleCanvasWithAlgorithm(canvas,mWidth)}var quality=this.config.quality;if(this.currentFile.type!="image/jpeg")quality=1;var imageData=canvas.toDataURL(this.currentFile.type,quality);if(typeof this.config.onScale==="function"){this.config.onScale(imageData)}if(this.currentFile.type==="image/jpeg"){try{var exifObj=piexif.load(img.src);var orientation=exifObj["0th"][piexif.ImageIFD.Orientation];if(orientation>4&&img.height>img.width){exifObj["0th"][piexif.ImageIFD.Orientation]=1}try{var exifStr=piexif.dump(exifObj);try{imageData=piexif.insert(exifStr,imageData)}catch(error){console.error(error)}}catch(error){console.error(error)}}catch(error){console.error(error)}}completionCallback(this.imageDataToBlob(imageData))};PWImageResizer.prototype.imageDataToBlob=function(imageData){var base64Marker=";base64,";if(imageData.indexOf(base64Marker)==-1){var parts=imageData.split(",");var contentType=parts[0].split(":")[1];var raw=parts[1];return new Blob([raw],{type:contentType})}var parts=imageData.split(base64Marker);var contentType=parts[0].split(":")[1];var raw=window.atob(parts[1]);var rawLength=raw.length;var uInt8Array=new Uint8Array(rawLength);for(var i=0;isrcCanvasData.height-1?srcCanvasData.height-1:Math.ceil(iyv);for(j=0;jsrcCanvasData.width-1?srcCanvasData.width-1:Math.ceil(ixv);idxD=(j+destCanvasData.width*i)*4;idxS00=(ix0+srcCanvasData.width*iy0)*4;idxS10=(ix1+srcCanvasData.width*iy0)*4;idxS01=(ix0+srcCanvasData.width*iy1)*4;idxS11=(ix1+srcCanvasData.width*iy1)*4;dx=ixv-ix0;dy=iyv-iy0;r=inner(srcCanvasData.data[idxS00],srcCanvasData.data[idxS10],srcCanvasData.data[idxS01],srcCanvasData.data[idxS11],dx,dy);destCanvasData.data[idxD]=r;g=inner(srcCanvasData.data[idxS00+1],srcCanvasData.data[idxS10+1],srcCanvasData.data[idxS01+1],srcCanvasData.data[idxS11+1],dx,dy);destCanvasData.data[idxD+1]=g;b=inner(srcCanvasData.data[idxS00+2],srcCanvasData.data[idxS10+2],srcCanvasData.data[idxS01+2],srcCanvasData.data[idxS11+2],dx,dy);destCanvasData.data[idxD+2]=b;a=inner(srcCanvasData.data[idxS00+3],srcCanvasData.data[idxS10+3],srcCanvasData.data[idxS01+3],srcCanvasData.data[idxS11+3],dx,dy);destCanvasData.data[idxD+3]=a}}};PWImageResizer.prototype.setConfig=function(customConfig){this.config=customConfig;this.config.debug=this.config.debug||false;if(typeof customConfig.quality=="undefined")customConfig.quality=1;if(customConfig.quality<.1)customConfig.quality=.1;if(customConfig.quality>1)customConfig.quality=1;this.config.quality=customConfig.quality;if(!this.config.maxWidth||this.config.maxWidth<0){this.config.maxWidth=0}if(!this.config.maxHeight||this.config.maxHeight<0){this.config.maxHeight=0}if(!this.config.maxSize||this.config.maxSize<0){this.config.maxSize=null}if(!this.config.scaleRatio||this.config.scaleRatio<=0||this.config.scaleRatio>=1){this.config.scaleRatio=null}this.config.autoRotate=true;if(typeof customConfig.autoRotate==="boolean")this.config.autoRotate=customConfig.autoRotate;if(this.config.maxWidth&&!this.config.maxHeight){this.config.maxHeight=this.config.maxWidth}else if(this.config.maxHeight&&!this.config.maxWidth){this.config.maxWidth=this.config.maxHeight}else if(!this.config.maxWidth&&!this.config.maxHeight){}};PWImageResizer.prototype.consoleLog=function(msg){if(this.config.debug)console.log("PWImageResizer: "+msg)}; \ No newline at end of file diff --git a/wire/modules/Inputfield/InputfieldImage/piexif.js b/wire/modules/Inputfield/InputfieldImage/piexif.js new file mode 100755 index 00000000..3d0bd12d --- /dev/null +++ b/wire/modules/Inputfield/InputfieldImage/piexif.js @@ -0,0 +1,2482 @@ +/* piexifjs + +The MIT License (MIT) + +Copyright (c) 2014, 2015 hMatoba(https://github.com/hMatoba) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +(function () { + "use strict"; + var that = {}; + that.version = "1.0.4"; + + that.remove = function (jpeg) { + var b64 = false; + if (jpeg.slice(0, 2) == "\xff\xd8") { + } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg.slice(0, 22) == "data:image/jpg;base64,") { + jpeg = atob(jpeg.split(",")[1]); + b64 = true; + } else { + throw new Error("Given data is not jpeg."); + } + + var segments = splitIntoSegments(jpeg); + var newSegments = segments.filter(function(seg){ + return !(seg.slice(0, 2) == "\xff\xe1" && + seg.slice(4, 10) == "Exif\x00\x00"); + }); + + var new_data = newSegments.join(""); + if (b64) { + new_data = "data:image/jpeg;base64," + btoa(new_data); + } + + return new_data; + }; + + + that.insert = function (exif, jpeg) { + var b64 = false; + if (exif.slice(0, 6) != "\x45\x78\x69\x66\x00\x00") { + throw new Error("Given data is not exif."); + } + if (jpeg.slice(0, 2) == "\xff\xd8") { + } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg.slice(0, 22) == "data:image/jpg;base64,") { + jpeg = atob(jpeg.split(",")[1]); + b64 = true; + } else { + throw new Error("Given data is not jpeg."); + } + + var exifStr = "\xff\xe1" + pack(">H", [exif.length + 2]) + exif; + var segments = splitIntoSegments(jpeg); + var new_data = mergeSegments(segments, exifStr); + if (b64) { + new_data = "data:image/jpeg;base64," + btoa(new_data); + } + + return new_data; + }; + + + that.load = function (data) { + var input_data; + if (typeof (data) == "string") { + if (data.slice(0, 2) == "\xff\xd8") { + input_data = data; + } else if (data.slice(0, 23) == "data:image/jpeg;base64," || data.slice(0, 22) == "data:image/jpg;base64,") { + input_data = atob(data.split(",")[1]); + } else if (data.slice(0, 4) == "Exif") { + input_data = data.slice(6); + } else { + throw new Error("'load' gots invalid file data."); + } + } else { + throw new Error("'load' gots invalid type argument."); + } + + var exifDict = {}; + var exif_dict = { + "0th": {}, + "Exif": {}, + "GPS": {}, + "Interop": {}, + "1st": {}, + "thumbnail": null + }; + var exifReader = new ExifReader(input_data); + if (exifReader.tiftag === null) { + return exif_dict; + } + + if (exifReader.tiftag.slice(0, 2) == "\x49\x49") { + exifReader.endian_mark = "<"; + } else { + exifReader.endian_mark = ">"; + } + + var pointer = unpack(exifReader.endian_mark + "L", + exifReader.tiftag.slice(4, 8))[0]; + exif_dict["0th"] = exifReader.get_ifd(pointer, "0th"); + + var first_ifd_pointer = exif_dict["0th"]["first_ifd_pointer"]; + delete exif_dict["0th"]["first_ifd_pointer"]; + + if (34665 in exif_dict["0th"]) { + pointer = exif_dict["0th"][34665]; + exif_dict["Exif"] = exifReader.get_ifd(pointer, "Exif"); + } + if (34853 in exif_dict["0th"]) { + pointer = exif_dict["0th"][34853]; + exif_dict["GPS"] = exifReader.get_ifd(pointer, "GPS"); + } + if (40965 in exif_dict["Exif"]) { + pointer = exif_dict["Exif"][40965]; + exif_dict["Interop"] = exifReader.get_ifd(pointer, "Interop"); + } + if (first_ifd_pointer != "\x00\x00\x00\x00") { + pointer = unpack(exifReader.endian_mark + "L", + first_ifd_pointer)[0]; + exif_dict["1st"] = exifReader.get_ifd(pointer, "1st"); + if ((513 in exif_dict["1st"]) && (514 in exif_dict["1st"])) { + var end = exif_dict["1st"][513] + exif_dict["1st"][514]; + var thumb = exifReader.tiftag.slice(exif_dict["1st"][513], end); + exif_dict["thumbnail"] = thumb; + } + } + + return exif_dict; + }; + + + that.dump = function (exif_dict_original) { + var TIFF_HEADER_LENGTH = 8; + + var exif_dict = copy(exif_dict_original); + var header = "Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08"; + var exif_is = false; + var gps_is = false; + var interop_is = false; + var first_is = false; + + var zeroth_ifd, + exif_ifd, + interop_ifd, + gps_ifd, + first_ifd; + + if ("0th" in exif_dict) { + zeroth_ifd = exif_dict["0th"]; + } else { + zeroth_ifd = {}; + } + + if ((("Exif" in exif_dict) && (Object.keys(exif_dict["Exif"]).length)) || + (("Interop" in exif_dict) && (Object.keys(exif_dict["Interop"]).length))) { + zeroth_ifd[34665] = 1; + exif_is = true; + exif_ifd = exif_dict["Exif"]; + if (("Interop" in exif_dict) && Object.keys(exif_dict["Interop"]).length) { + exif_ifd[40965] = 1; + interop_is = true; + interop_ifd = exif_dict["Interop"]; + } else if (Object.keys(exif_ifd).indexOf(that.ExifIFD.InteroperabilityTag.toString()) > -1) { + delete exif_ifd[40965]; + } + } else if (Object.keys(zeroth_ifd).indexOf(that.ImageIFD.ExifTag.toString()) > -1) { + delete zeroth_ifd[34665]; + } + + if (("GPS" in exif_dict) && (Object.keys(exif_dict["GPS"]).length)) { + zeroth_ifd[that.ImageIFD.GPSTag] = 1; + gps_is = true; + gps_ifd = exif_dict["GPS"]; + } else if (Object.keys(zeroth_ifd).indexOf(that.ImageIFD.GPSTag.toString()) > -1) { + delete zeroth_ifd[that.ImageIFD.GPSTag]; + } + + if (("1st" in exif_dict) && + ("thumbnail" in exif_dict) && + (exif_dict["thumbnail"] != null)) { + first_is = true; + exif_dict["1st"][513] = 1; + exif_dict["1st"][514] = 1; + first_ifd = exif_dict["1st"]; + } + + var zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0); + var zeroth_length = (zeroth_set[0].length + exif_is * 12 + gps_is * 12 + 4 + + zeroth_set[1].length); + + var exif_set, + exif_bytes = "", + exif_length = 0, + gps_set, + gps_bytes = "", + gps_length = 0, + interop_set, + interop_bytes = "", + interop_length = 0, + first_set, + first_bytes = "", + thumbnail; + if (exif_is) { + exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length); + exif_length = exif_set[0].length + interop_is * 12 + exif_set[1].length; + } + if (gps_is) { + gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length); + gps_bytes = gps_set.join(""); + gps_length = gps_bytes.length; + } + if (interop_is) { + var offset = zeroth_length + exif_length + gps_length; + interop_set = _dict_to_bytes(interop_ifd, "Interop", offset); + interop_bytes = interop_set.join(""); + interop_length = interop_bytes.length; + } + if (first_is) { + var offset = zeroth_length + exif_length + gps_length + interop_length; + first_set = _dict_to_bytes(first_ifd, "1st", offset); + thumbnail = _get_thumbnail(exif_dict["thumbnail"]); + if (thumbnail.length > 64000) { + throw new Error("Given thumbnail is too large. max 64kB"); + } + } + + var exif_pointer = "", + gps_pointer = "", + interop_pointer = "", + first_ifd_pointer = "\x00\x00\x00\x00"; + if (exif_is) { + var pointer_value = TIFF_HEADER_LENGTH + zeroth_length; + var pointer_str = pack(">L", [pointer_value]); + var key = 34665; + var key_str = pack(">H", [key]); + var type_str = pack(">H", [TYPES["Long"]]); + var length_str = pack(">L", [1]); + exif_pointer = key_str + type_str + length_str + pointer_str; + } + if (gps_is) { + var pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length; + var pointer_str = pack(">L", [pointer_value]); + var key = 34853; + var key_str = pack(">H", [key]); + var type_str = pack(">H", [TYPES["Long"]]); + var length_str = pack(">L", [1]); + gps_pointer = key_str + type_str + length_str + pointer_str; + } + if (interop_is) { + var pointer_value = (TIFF_HEADER_LENGTH + + zeroth_length + exif_length + gps_length); + var pointer_str = pack(">L", [pointer_value]); + var key = 40965; + var key_str = pack(">H", [key]); + var type_str = pack(">H", [TYPES["Long"]]); + var length_str = pack(">L", [1]); + interop_pointer = key_str + type_str + length_str + pointer_str; + } + if (first_is) { + var pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + + exif_length + gps_length + interop_length); + first_ifd_pointer = pack(">L", [pointer_value]); + var thumbnail_pointer = (pointer_value + first_set[0].length + 24 + + 4 + first_set[1].length); + var thumbnail_p_bytes = ("\x02\x01\x00\x04\x00\x00\x00\x01" + + pack(">L", [thumbnail_pointer])); + var thumbnail_length_bytes = ("\x02\x02\x00\x04\x00\x00\x00\x01" + + pack(">L", [thumbnail.length])); + first_bytes = (first_set[0] + thumbnail_p_bytes + + thumbnail_length_bytes + "\x00\x00\x00\x00" + + first_set[1] + thumbnail); + } + + var zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer + + first_ifd_pointer + zeroth_set[1]); + if (exif_is) { + exif_bytes = exif_set[0] + interop_pointer + exif_set[1]; + } + + return (header + zeroth_bytes + exif_bytes + gps_bytes + + interop_bytes + first_bytes); + }; + + + function copy(obj) { + return JSON.parse(JSON.stringify(obj)); + } + + + function _get_thumbnail(jpeg) { + var segments = splitIntoSegments(jpeg); + while (("\xff\xe0" <= segments[1].slice(0, 2)) && (segments[1].slice(0, 2) <= "\xff\xef")) { + segments = [segments[0]].concat(segments.slice(2)); + } + return segments.join(""); + } + + + function _pack_byte(array) { + return pack(">" + nStr("B", array.length), array); + } + + + function _pack_short(array) { + return pack(">" + nStr("H", array.length), array); + } + + + function _pack_long(array) { + return pack(">" + nStr("L", array.length), array); + } + + + function _value_to_bytes(raw_value, value_type, offset) { + var four_bytes_over = ""; + var value_str = ""; + var length, + new_value, + num, + den; + + if (value_type == "Byte") { + length = raw_value.length; + if (length <= 4) { + value_str = (_pack_byte(raw_value) + + nStr("\x00", 4 - length)); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_byte(raw_value); + } + } else if (value_type == "Short") { + length = raw_value.length; + if (length <= 2) { + value_str = (_pack_short(raw_value) + + nStr("\x00\x00", 2 - length)); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_short(raw_value); + } + } else if (value_type == "Long") { + length = raw_value.length; + if (length <= 1) { + value_str = _pack_long(raw_value); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_long(raw_value); + } + } else if (value_type == "Ascii") { + new_value = raw_value + "\x00"; + length = new_value.length; + if (length > 4) { + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else { + value_str = new_value + nStr("\x00", 4 - length); + } + } else if (value_type == "Rational") { + if (typeof (raw_value[0]) == "number") { + length = 1; + num = raw_value[0]; + den = raw_value[1]; + new_value = pack(">L", [num]) + pack(">L", [den]); + } else { + length = raw_value.length; + new_value = ""; + for (var n = 0; n < length; n++) { + num = raw_value[n][0]; + den = raw_value[n][1]; + new_value += (pack(">L", [num]) + + pack(">L", [den])); + } + } + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else if (value_type == "SRational") { + if (typeof (raw_value[0]) == "number") { + length = 1; + num = raw_value[0]; + den = raw_value[1]; + new_value = pack(">l", [num]) + pack(">l", [den]); + } else { + length = raw_value.length; + new_value = ""; + for (var n = 0; n < length; n++) { + num = raw_value[n][0]; + den = raw_value[n][1]; + new_value += (pack(">l", [num]) + + pack(">l", [den])); + } + } + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else if (value_type == "Undefined") { + length = raw_value.length; + if (length > 4) { + value_str = pack(">L", [offset]); + four_bytes_over = raw_value; + } else { + value_str = raw_value + nStr("\x00", 4 - length); + } + } + + var length_str = pack(">L", [length]); + + return [length_str, value_str, four_bytes_over]; + } + + function _dict_to_bytes(ifd_dict, ifd, ifd_offset) { + var TIFF_HEADER_LENGTH = 8; + var tag_count = Object.keys(ifd_dict).length; + var entry_header = pack(">H", [tag_count]); + var entries_length; + if (["0th", "1st"].indexOf(ifd) > -1) { + entries_length = 2 + tag_count * 12 + 4; + } else { + entries_length = 2 + tag_count * 12; + } + var entries = ""; + var values = ""; + var key; + + for (var key in ifd_dict) { + if (typeof (key) == "string") { + key = parseInt(key); + } + if ((ifd == "0th") && ([34665, 34853].indexOf(key) > -1)) { + continue; + } else if ((ifd == "Exif") && (key == 40965)) { + continue; + } else if ((ifd == "1st") && ([513, 514].indexOf(key) > -1)) { + continue; + } + + var raw_value = ifd_dict[key]; + var key_str = pack(">H", [key]); + var value_type = TAGS[ifd][key]["type"]; + var type_str = pack(">H", [TYPES[value_type]]); + + if (typeof (raw_value) == "number") { + raw_value = [raw_value]; + } + var offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + values.length; + var b = _value_to_bytes(raw_value, value_type, offset); + var length_str = b[0]; + var value_str = b[1]; + var four_bytes_over = b[2]; + + entries += key_str + type_str + length_str + value_str; + values += four_bytes_over; + } + + return [entry_header + entries, values]; + } + + + + function ExifReader(data) { + var segments, + app1; + if (data.slice(0, 2) == "\xff\xd8") { // JPEG + segments = splitIntoSegments(data); + app1 = getExifSeg(segments); + if (app1) { + this.tiftag = app1.slice(10); + } else { + this.tiftag = null; + } + } else if (["\x49\x49", "\x4d\x4d"].indexOf(data.slice(0, 2)) > -1) { // TIFF + this.tiftag = data; + } else if (data.slice(0, 4) == "Exif") { // Exif + this.tiftag = data.slice(6); + } else { + throw new Error("Given file is neither JPEG nor TIFF."); + } + } + + ExifReader.prototype = { + get_ifd: function (pointer, ifd_name) { + var ifd_dict = {}; + var tag_count = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer, pointer + 2))[0]; + var offset = pointer + 2; + var t; + if (["0th", "1st"].indexOf(ifd_name) > -1) { + t = "Image"; + } else { + t = ifd_name; + } + + for (var x = 0; x < tag_count; x++) { + pointer = offset + 12 * x; + var tag = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer, pointer + 2))[0]; + var value_type = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer + 2, pointer + 4))[0]; + var value_num = unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4, pointer + 8))[0]; + var value = this.tiftag.slice(pointer + 8, pointer + 12); + + var v_set = [value_type, value_num, value]; + if (tag in TAGS[t]) { + ifd_dict[tag] = this.convert_value(v_set); + } + } + + if (ifd_name == "0th") { + pointer = offset + 12 * tag_count; + ifd_dict["first_ifd_pointer"] = this.tiftag.slice(pointer, pointer + 4); + } + + return ifd_dict; + }, + + convert_value: function (val) { + var data = null; + var t = val[0]; + var length = val[1]; + var value = val[2]; + var pointer; + + if (t == 1) { // BYTE + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("B", length), + this.tiftag.slice(pointer, pointer + length)); + } else { + data = unpack(this.endian_mark + nStr("B", length), value.slice(0, length)); + } + } else if (t == 2) { // ASCII + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = this.tiftag.slice(pointer, pointer + length - 1); + } else { + data = value.slice(0, length - 1); + } + } else if (t == 3) { // SHORT + if (length > 2) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("H", length), + this.tiftag.slice(pointer, pointer + length * 2)); + } else { + data = unpack(this.endian_mark + nStr("H", length), + value.slice(0, length * 2)); + } + } else if (t == 4) { // LONG + if (length > 1) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("L", length), + this.tiftag.slice(pointer, pointer + length * 4)); + } else { + data = unpack(this.endian_mark + nStr("L", length), + value); + } + } else if (t == 5) { // RATIONAL + pointer = unpack(this.endian_mark + "L", value)[0]; + if (length > 1) { + data = []; + for (var x = 0; x < length; x++) { + data.push([unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], + unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] + ]); + } + } else { + data = [unpack(this.endian_mark + "L", + this.tiftag.slice(pointer, pointer + 4))[0], + unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4, pointer + 8))[0] + ]; + } + } else if (t == 7) { // UNDEFINED BYTES + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = this.tiftag.slice(pointer, pointer + length); + } else { + data = value.slice(0, length); + } + } else if (t == 9) { // SLONG + if (length > 1) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("l", length), + this.tiftag.slice(pointer, pointer + length * 4)); + } else { + data = unpack(this.endian_mark + nStr("l", length), + value); + } + } else if (t == 10) { // SRATIONAL + pointer = unpack(this.endian_mark + "L", value)[0]; + if (length > 1) { + data = []; + for (var x = 0; x < length; x++) { + data.push([unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], + unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] + ]); + } + } else { + data = [unpack(this.endian_mark + "l", + this.tiftag.slice(pointer, pointer + 4))[0], + unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + 4, pointer + 8))[0] + ]; + } + } else { + throw new Error("Exif might be wrong. Got incorrect value " + + "type to decode. type:" + t); + } + + if ((data instanceof Array) && (data.length == 1)) { + return data[0]; + } else { + return data; + } + }, + }; + + + if (typeof window !== "undefined" && typeof window.btoa === "function") { + var btoa = window.btoa; + } + if (typeof btoa === "undefined") { + var btoa = function (input) { var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + keyStr.charAt(enc1) + keyStr.charAt(enc2) + + keyStr.charAt(enc3) + keyStr.charAt(enc4); + + } + + return output; + }; + } + + + if (typeof window !== "undefined" && typeof window.atob === "function") { + var atob = window.atob; + } + if (typeof atob === "undefined") { + var atob = function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + return output; + }; + } + + + function getImageSize(imageArray) { + var segments = slice2Segments(imageArray); + var seg, + width, + height, + SOF = [192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207]; + + for (var x = 0; x < segments.length; x++) { + seg = segments[x]; + if (SOF.indexOf(seg[1]) >= 0) { + height = seg[5] * 256 + seg[6]; + width = seg[7] * 256 + seg[8]; + break; + } + } + return [width, height]; + } + + + function pack(mark, array) { + if (!(array instanceof Array)) { + throw new Error("'pack' error. Got invalid type argument."); + } + if ((mark.length - 1) != array.length) { + throw new Error("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements."); + } + + var littleEndian; + if (mark[0] == "<") { + littleEndian = true; + } else if (mark[0] == ">") { + littleEndian = false; + } else { + throw new Error(""); + } + var packed = ""; + var p = 1; + var val = null; + var c = null; + var valStr = null; + + while (c = mark[p]) { + if (c.toLowerCase() == "b") { + val = array[p - 1]; + if ((c == "b") && (val < 0)) { + val += 0x100; + } + if ((val > 0xff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(val); + } + } else if (c == "H") { + val = array[p - 1]; + if ((val > 0xffff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + + String.fromCharCode(val % 0x100); + if (littleEndian) { + valStr = valStr.split("").reverse().join(""); + } + } + } else if (c.toLowerCase() == "l") { + val = array[p - 1]; + if ((c == "l") && (val < 0)) { + val += 0x100000000; + } + if ((val > 0xffffffff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(Math.floor(val / 0x1000000)) + + String.fromCharCode(Math.floor((val % 0x1000000) / 0x10000)) + + String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + + String.fromCharCode(val % 0x100); + if (littleEndian) { + valStr = valStr.split("").reverse().join(""); + } + } + } else { + throw new Error("'pack' error."); + } + + packed += valStr; + p += 1; + } + + return packed; + } + + function unpack(mark, str) { + if (typeof (str) != "string") { + throw new Error("'unpack' error. Got invalid type argument."); + } + var l = 0; + for (var markPointer = 1; markPointer < mark.length; markPointer++) { + if (mark[markPointer].toLowerCase() == "b") { + l += 1; + } else if (mark[markPointer].toLowerCase() == "h") { + l += 2; + } else if (mark[markPointer].toLowerCase() == "l") { + l += 4; + } else { + throw new Error("'unpack' error. Got invalid mark."); + } + } + + if (l != str.length) { + throw new Error("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length); + } + + var littleEndian; + if (mark[0] == "<") { + littleEndian = true; + } else if (mark[0] == ">") { + littleEndian = false; + } else { + throw new Error("'unpack' error."); + } + var unpacked = []; + var strPointer = 0; + var p = 1; + var val = null; + var c = null; + var length = null; + var sliced = ""; + + while (c = mark[p]) { + if (c.toLowerCase() == "b") { + length = 1; + sliced = str.slice(strPointer, strPointer + length); + val = sliced.charCodeAt(0); + if ((c == "b") && (val >= 0x80)) { + val -= 0x100; + } + } else if (c == "H") { + length = 2; + sliced = str.slice(strPointer, strPointer + length); + if (littleEndian) { + sliced = sliced.split("").reverse().join(""); + } + val = sliced.charCodeAt(0) * 0x100 + + sliced.charCodeAt(1); + } else if (c.toLowerCase() == "l") { + length = 4; + sliced = str.slice(strPointer, strPointer + length); + if (littleEndian) { + sliced = sliced.split("").reverse().join(""); + } + val = sliced.charCodeAt(0) * 0x1000000 + + sliced.charCodeAt(1) * 0x10000 + + sliced.charCodeAt(2) * 0x100 + + sliced.charCodeAt(3); + if ((c == "l") && (val >= 0x80000000)) { + val -= 0x100000000; + } + } else { + throw new Error("'unpack' error. " + c); + } + + unpacked.push(val); + strPointer += length; + p += 1; + } + + return unpacked; + } + + function nStr(ch, num) { + var str = ""; + for (var i = 0; i < num; i++) { + str += ch; + } + return str; + } + + function splitIntoSegments(data) { + if (data.slice(0, 2) != "\xff\xd8") { + throw new Error("Given data isn't JPEG."); + } + + var head = 2; + var segments = ["\xff\xd8"]; + while (true) { + if (data.slice(head, head + 2) == "\xff\xda") { + segments.push(data.slice(head)); + break; + } else { + var length = unpack(">H", data.slice(head + 2, head + 4))[0]; + var endPoint = head + length + 2; + segments.push(data.slice(head, endPoint)); + head = endPoint; + } + + if (head >= data.length) { + throw new Error("Wrong JPEG data."); + } + } + return segments; + } + + + function getExifSeg(segments) { + var seg; + for (var i = 0; i < segments.length; i++) { + seg = segments[i]; + if (seg.slice(0, 2) == "\xff\xe1" && + seg.slice(4, 10) == "Exif\x00\x00") { + return seg; + } + } + return null; + } + + + function mergeSegments(segments, exif) { + var hasExifSegment = false; + var additionalAPP1ExifSegments = []; + + segments.forEach(function(segment, i) { + // Replace first occurence of APP1:Exif segment + if (segment.slice(0, 2) == "\xff\xe1" && + segment.slice(4, 10) == "Exif\x00\x00" + ) { + if (!hasExifSegment) { + segments[i] = exif; + hasExifSegment = true; + } else { + additionalAPP1ExifSegments.unshift(i); + } + } + }); + + // Remove additional occurences of APP1:Exif segment + additionalAPP1ExifSegments.forEach(function(segmentIndex) { + segments.splice(segmentIndex, 1); + }); + + if (!hasExifSegment && exif) { + segments = [segments[0], exif].concat(segments.slice(1)); + } + + return segments.join(""); + } + + + function toHex(str) { + var hexStr = ""; + for (var i = 0; i < str.length; i++) { + var h = str.charCodeAt(i); + var hex = ((h < 10) ? "0" : "") + h.toString(16); + hexStr += hex + " "; + } + return hexStr; + } + + + var TYPES = { + "Byte": 1, + "Ascii": 2, + "Short": 3, + "Long": 4, + "Rational": 5, + "Undefined": 7, + "SLong": 9, + "SRational": 10 + }; + + + var TAGS = { + 'Image': { + 11: { + 'name': 'ProcessingSoftware', + 'type': 'Ascii' + }, + 254: { + 'name': 'NewSubfileType', + 'type': 'Long' + }, + 255: { + 'name': 'SubfileType', + 'type': 'Short' + }, + 256: { + 'name': 'ImageWidth', + 'type': 'Long' + }, + 257: { + 'name': 'ImageLength', + 'type': 'Long' + }, + 258: { + 'name': 'BitsPerSample', + 'type': 'Short' + }, + 259: { + 'name': 'Compression', + 'type': 'Short' + }, + 262: { + 'name': 'PhotometricInterpretation', + 'type': 'Short' + }, + 263: { + 'name': 'Threshholding', + 'type': 'Short' + }, + 264: { + 'name': 'CellWidth', + 'type': 'Short' + }, + 265: { + 'name': 'CellLength', + 'type': 'Short' + }, + 266: { + 'name': 'FillOrder', + 'type': 'Short' + }, + 269: { + 'name': 'DocumentName', + 'type': 'Ascii' + }, + 270: { + 'name': 'ImageDescription', + 'type': 'Ascii' + }, + 271: { + 'name': 'Make', + 'type': 'Ascii' + }, + 272: { + 'name': 'Model', + 'type': 'Ascii' + }, + 273: { + 'name': 'StripOffsets', + 'type': 'Long' + }, + 274: { + 'name': 'Orientation', + 'type': 'Short' + }, + 277: { + 'name': 'SamplesPerPixel', + 'type': 'Short' + }, + 278: { + 'name': 'RowsPerStrip', + 'type': 'Long' + }, + 279: { + 'name': 'StripByteCounts', + 'type': 'Long' + }, + 282: { + 'name': 'XResolution', + 'type': 'Rational' + }, + 283: { + 'name': 'YResolution', + 'type': 'Rational' + }, + 284: { + 'name': 'PlanarConfiguration', + 'type': 'Short' + }, + 290: { + 'name': 'GrayResponseUnit', + 'type': 'Short' + }, + 291: { + 'name': 'GrayResponseCurve', + 'type': 'Short' + }, + 292: { + 'name': 'T4Options', + 'type': 'Long' + }, + 293: { + 'name': 'T6Options', + 'type': 'Long' + }, + 296: { + 'name': 'ResolutionUnit', + 'type': 'Short' + }, + 301: { + 'name': 'TransferFunction', + 'type': 'Short' + }, + 305: { + 'name': 'Software', + 'type': 'Ascii' + }, + 306: { + 'name': 'DateTime', + 'type': 'Ascii' + }, + 315: { + 'name': 'Artist', + 'type': 'Ascii' + }, + 316: { + 'name': 'HostComputer', + 'type': 'Ascii' + }, + 317: { + 'name': 'Predictor', + 'type': 'Short' + }, + 318: { + 'name': 'WhitePoint', + 'type': 'Rational' + }, + 319: { + 'name': 'PrimaryChromaticities', + 'type': 'Rational' + }, + 320: { + 'name': 'ColorMap', + 'type': 'Short' + }, + 321: { + 'name': 'HalftoneHints', + 'type': 'Short' + }, + 322: { + 'name': 'TileWidth', + 'type': 'Short' + }, + 323: { + 'name': 'TileLength', + 'type': 'Short' + }, + 324: { + 'name': 'TileOffsets', + 'type': 'Short' + }, + 325: { + 'name': 'TileByteCounts', + 'type': 'Short' + }, + 330: { + 'name': 'SubIFDs', + 'type': 'Long' + }, + 332: { + 'name': 'InkSet', + 'type': 'Short' + }, + 333: { + 'name': 'InkNames', + 'type': 'Ascii' + }, + 334: { + 'name': 'NumberOfInks', + 'type': 'Short' + }, + 336: { + 'name': 'DotRange', + 'type': 'Byte' + }, + 337: { + 'name': 'TargetPrinter', + 'type': 'Ascii' + }, + 338: { + 'name': 'ExtraSamples', + 'type': 'Short' + }, + 339: { + 'name': 'SampleFormat', + 'type': 'Short' + }, + 340: { + 'name': 'SMinSampleValue', + 'type': 'Short' + }, + 341: { + 'name': 'SMaxSampleValue', + 'type': 'Short' + }, + 342: { + 'name': 'TransferRange', + 'type': 'Short' + }, + 343: { + 'name': 'ClipPath', + 'type': 'Byte' + }, + 344: { + 'name': 'XClipPathUnits', + 'type': 'Long' + }, + 345: { + 'name': 'YClipPathUnits', + 'type': 'Long' + }, + 346: { + 'name': 'Indexed', + 'type': 'Short' + }, + 347: { + 'name': 'JPEGTables', + 'type': 'Undefined' + }, + 351: { + 'name': 'OPIProxy', + 'type': 'Short' + }, + 512: { + 'name': 'JPEGProc', + 'type': 'Long' + }, + 513: { + 'name': 'JPEGInterchangeFormat', + 'type': 'Long' + }, + 514: { + 'name': 'JPEGInterchangeFormatLength', + 'type': 'Long' + }, + 515: { + 'name': 'JPEGRestartInterval', + 'type': 'Short' + }, + 517: { + 'name': 'JPEGLosslessPredictors', + 'type': 'Short' + }, + 518: { + 'name': 'JPEGPointTransforms', + 'type': 'Short' + }, + 519: { + 'name': 'JPEGQTables', + 'type': 'Long' + }, + 520: { + 'name': 'JPEGDCTables', + 'type': 'Long' + }, + 521: { + 'name': 'JPEGACTables', + 'type': 'Long' + }, + 529: { + 'name': 'YCbCrCoefficients', + 'type': 'Rational' + }, + 530: { + 'name': 'YCbCrSubSampling', + 'type': 'Short' + }, + 531: { + 'name': 'YCbCrPositioning', + 'type': 'Short' + }, + 532: { + 'name': 'ReferenceBlackWhite', + 'type': 'Rational' + }, + 700: { + 'name': 'XMLPacket', + 'type': 'Byte' + }, + 18246: { + 'name': 'Rating', + 'type': 'Short' + }, + 18249: { + 'name': 'RatingPercent', + 'type': 'Short' + }, + 32781: { + 'name': 'ImageID', + 'type': 'Ascii' + }, + 33421: { + 'name': 'CFARepeatPatternDim', + 'type': 'Short' + }, + 33422: { + 'name': 'CFAPattern', + 'type': 'Byte' + }, + 33423: { + 'name': 'BatteryLevel', + 'type': 'Rational' + }, + 33432: { + 'name': 'Copyright', + 'type': 'Ascii' + }, + 33434: { + 'name': 'ExposureTime', + 'type': 'Rational' + }, + 34377: { + 'name': 'ImageResources', + 'type': 'Byte' + }, + 34665: { + 'name': 'ExifTag', + 'type': 'Long' + }, + 34675: { + 'name': 'InterColorProfile', + 'type': 'Undefined' + }, + 34853: { + 'name': 'GPSTag', + 'type': 'Long' + }, + 34857: { + 'name': 'Interlace', + 'type': 'Short' + }, + 34858: { + 'name': 'TimeZoneOffset', + 'type': 'Long' + }, + 34859: { + 'name': 'SelfTimerMode', + 'type': 'Short' + }, + 37387: { + 'name': 'FlashEnergy', + 'type': 'Rational' + }, + 37388: { + 'name': 'SpatialFrequencyResponse', + 'type': 'Undefined' + }, + 37389: { + 'name': 'Noise', + 'type': 'Undefined' + }, + 37390: { + 'name': 'FocalPlaneXResolution', + 'type': 'Rational' + }, + 37391: { + 'name': 'FocalPlaneYResolution', + 'type': 'Rational' + }, + 37392: { + 'name': 'FocalPlaneResolutionUnit', + 'type': 'Short' + }, + 37393: { + 'name': 'ImageNumber', + 'type': 'Long' + }, + 37394: { + 'name': 'SecurityClassification', + 'type': 'Ascii' + }, + 37395: { + 'name': 'ImageHistory', + 'type': 'Ascii' + }, + 37397: { + 'name': 'ExposureIndex', + 'type': 'Rational' + }, + 37398: { + 'name': 'TIFFEPStandardID', + 'type': 'Byte' + }, + 37399: { + 'name': 'SensingMethod', + 'type': 'Short' + }, + 40091: { + 'name': 'XPTitle', + 'type': 'Byte' + }, + 40092: { + 'name': 'XPComment', + 'type': 'Byte' + }, + 40093: { + 'name': 'XPAuthor', + 'type': 'Byte' + }, + 40094: { + 'name': 'XPKeywords', + 'type': 'Byte' + }, + 40095: { + 'name': 'XPSubject', + 'type': 'Byte' + }, + 50341: { + 'name': 'PrintImageMatching', + 'type': 'Undefined' + }, + 50706: { + 'name': 'DNGVersion', + 'type': 'Byte' + }, + 50707: { + 'name': 'DNGBackwardVersion', + 'type': 'Byte' + }, + 50708: { + 'name': 'UniqueCameraModel', + 'type': 'Ascii' + }, + 50709: { + 'name': 'LocalizedCameraModel', + 'type': 'Byte' + }, + 50710: { + 'name': 'CFAPlaneColor', + 'type': 'Byte' + }, + 50711: { + 'name': 'CFALayout', + 'type': 'Short' + }, + 50712: { + 'name': 'LinearizationTable', + 'type': 'Short' + }, + 50713: { + 'name': 'BlackLevelRepeatDim', + 'type': 'Short' + }, + 50714: { + 'name': 'BlackLevel', + 'type': 'Rational' + }, + 50715: { + 'name': 'BlackLevelDeltaH', + 'type': 'SRational' + }, + 50716: { + 'name': 'BlackLevelDeltaV', + 'type': 'SRational' + }, + 50717: { + 'name': 'WhiteLevel', + 'type': 'Short' + }, + 50718: { + 'name': 'DefaultScale', + 'type': 'Rational' + }, + 50719: { + 'name': 'DefaultCropOrigin', + 'type': 'Short' + }, + 50720: { + 'name': 'DefaultCropSize', + 'type': 'Short' + }, + 50721: { + 'name': 'ColorMatrix1', + 'type': 'SRational' + }, + 50722: { + 'name': 'ColorMatrix2', + 'type': 'SRational' + }, + 50723: { + 'name': 'CameraCalibration1', + 'type': 'SRational' + }, + 50724: { + 'name': 'CameraCalibration2', + 'type': 'SRational' + }, + 50725: { + 'name': 'ReductionMatrix1', + 'type': 'SRational' + }, + 50726: { + 'name': 'ReductionMatrix2', + 'type': 'SRational' + }, + 50727: { + 'name': 'AnalogBalance', + 'type': 'Rational' + }, + 50728: { + 'name': 'AsShotNeutral', + 'type': 'Short' + }, + 50729: { + 'name': 'AsShotWhiteXY', + 'type': 'Rational' + }, + 50730: { + 'name': 'BaselineExposure', + 'type': 'SRational' + }, + 50731: { + 'name': 'BaselineNoise', + 'type': 'Rational' + }, + 50732: { + 'name': 'BaselineSharpness', + 'type': 'Rational' + }, + 50733: { + 'name': 'BayerGreenSplit', + 'type': 'Long' + }, + 50734: { + 'name': 'LinearResponseLimit', + 'type': 'Rational' + }, + 50735: { + 'name': 'CameraSerialNumber', + 'type': 'Ascii' + }, + 50736: { + 'name': 'LensInfo', + 'type': 'Rational' + }, + 50737: { + 'name': 'ChromaBlurRadius', + 'type': 'Rational' + }, + 50738: { + 'name': 'AntiAliasStrength', + 'type': 'Rational' + }, + 50739: { + 'name': 'ShadowScale', + 'type': 'SRational' + }, + 50740: { + 'name': 'DNGPrivateData', + 'type': 'Byte' + }, + 50741: { + 'name': 'MakerNoteSafety', + 'type': 'Short' + }, + 50778: { + 'name': 'CalibrationIlluminant1', + 'type': 'Short' + }, + 50779: { + 'name': 'CalibrationIlluminant2', + 'type': 'Short' + }, + 50780: { + 'name': 'BestQualityScale', + 'type': 'Rational' + }, + 50781: { + 'name': 'RawDataUniqueID', + 'type': 'Byte' + }, + 50827: { + 'name': 'OriginalRawFileName', + 'type': 'Byte' + }, + 50828: { + 'name': 'OriginalRawFileData', + 'type': 'Undefined' + }, + 50829: { + 'name': 'ActiveArea', + 'type': 'Short' + }, + 50830: { + 'name': 'MaskedAreas', + 'type': 'Short' + }, + 50831: { + 'name': 'AsShotICCProfile', + 'type': 'Undefined' + }, + 50832: { + 'name': 'AsShotPreProfileMatrix', + 'type': 'SRational' + }, + 50833: { + 'name': 'CurrentICCProfile', + 'type': 'Undefined' + }, + 50834: { + 'name': 'CurrentPreProfileMatrix', + 'type': 'SRational' + }, + 50879: { + 'name': 'ColorimetricReference', + 'type': 'Short' + }, + 50931: { + 'name': 'CameraCalibrationSignature', + 'type': 'Byte' + }, + 50932: { + 'name': 'ProfileCalibrationSignature', + 'type': 'Byte' + }, + 50934: { + 'name': 'AsShotProfileName', + 'type': 'Byte' + }, + 50935: { + 'name': 'NoiseReductionApplied', + 'type': 'Rational' + }, + 50936: { + 'name': 'ProfileName', + 'type': 'Byte' + }, + 50937: { + 'name': 'ProfileHueSatMapDims', + 'type': 'Long' + }, + 50938: { + 'name': 'ProfileHueSatMapData1', + 'type': 'Float' + }, + 50939: { + 'name': 'ProfileHueSatMapData2', + 'type': 'Float' + }, + 50940: { + 'name': 'ProfileToneCurve', + 'type': 'Float' + }, + 50941: { + 'name': 'ProfileEmbedPolicy', + 'type': 'Long' + }, + 50942: { + 'name': 'ProfileCopyright', + 'type': 'Byte' + }, + 50964: { + 'name': 'ForwardMatrix1', + 'type': 'SRational' + }, + 50965: { + 'name': 'ForwardMatrix2', + 'type': 'SRational' + }, + 50966: { + 'name': 'PreviewApplicationName', + 'type': 'Byte' + }, + 50967: { + 'name': 'PreviewApplicationVersion', + 'type': 'Byte' + }, + 50968: { + 'name': 'PreviewSettingsName', + 'type': 'Byte' + }, + 50969: { + 'name': 'PreviewSettingsDigest', + 'type': 'Byte' + }, + 50970: { + 'name': 'PreviewColorSpace', + 'type': 'Long' + }, + 50971: { + 'name': 'PreviewDateTime', + 'type': 'Ascii' + }, + 50972: { + 'name': 'RawImageDigest', + 'type': 'Undefined' + }, + 50973: { + 'name': 'OriginalRawFileDigest', + 'type': 'Undefined' + }, + 50974: { + 'name': 'SubTileBlockSize', + 'type': 'Long' + }, + 50975: { + 'name': 'RowInterleaveFactor', + 'type': 'Long' + }, + 50981: { + 'name': 'ProfileLookTableDims', + 'type': 'Long' + }, + 50982: { + 'name': 'ProfileLookTableData', + 'type': 'Float' + }, + 51008: { + 'name': 'OpcodeList1', + 'type': 'Undefined' + }, + 51009: { + 'name': 'OpcodeList2', + 'type': 'Undefined' + }, + 51022: { + 'name': 'OpcodeList3', + 'type': 'Undefined' + } + }, + 'Exif': { + 33434: { + 'name': 'ExposureTime', + 'type': 'Rational' + }, + 33437: { + 'name': 'FNumber', + 'type': 'Rational' + }, + 34850: { + 'name': 'ExposureProgram', + 'type': 'Short' + }, + 34852: { + 'name': 'SpectralSensitivity', + 'type': 'Ascii' + }, + 34855: { + 'name': 'ISOSpeedRatings', + 'type': 'Short' + }, + 34856: { + 'name': 'OECF', + 'type': 'Undefined' + }, + 34864: { + 'name': 'SensitivityType', + 'type': 'Short' + }, + 34865: { + 'name': 'StandardOutputSensitivity', + 'type': 'Long' + }, + 34866: { + 'name': 'RecommendedExposureIndex', + 'type': 'Long' + }, + 34867: { + 'name': 'ISOSpeed', + 'type': 'Long' + }, + 34868: { + 'name': 'ISOSpeedLatitudeyyy', + 'type': 'Long' + }, + 34869: { + 'name': 'ISOSpeedLatitudezzz', + 'type': 'Long' + }, + 36864: { + 'name': 'ExifVersion', + 'type': 'Undefined' + }, + 36867: { + 'name': 'DateTimeOriginal', + 'type': 'Ascii' + }, + 36868: { + 'name': 'DateTimeDigitized', + 'type': 'Ascii' + }, + 37121: { + 'name': 'ComponentsConfiguration', + 'type': 'Undefined' + }, + 37122: { + 'name': 'CompressedBitsPerPixel', + 'type': 'Rational' + }, + 37377: { + 'name': 'ShutterSpeedValue', + 'type': 'SRational' + }, + 37378: { + 'name': 'ApertureValue', + 'type': 'Rational' + }, + 37379: { + 'name': 'BrightnessValue', + 'type': 'SRational' + }, + 37380: { + 'name': 'ExposureBiasValue', + 'type': 'SRational' + }, + 37381: { + 'name': 'MaxApertureValue', + 'type': 'Rational' + }, + 37382: { + 'name': 'SubjectDistance', + 'type': 'Rational' + }, + 37383: { + 'name': 'MeteringMode', + 'type': 'Short' + }, + 37384: { + 'name': 'LightSource', + 'type': 'Short' + }, + 37385: { + 'name': 'Flash', + 'type': 'Short' + }, + 37386: { + 'name': 'FocalLength', + 'type': 'Rational' + }, + 37396: { + 'name': 'SubjectArea', + 'type': 'Short' + }, + 37500: { + 'name': 'MakerNote', + 'type': 'Undefined' + }, + 37510: { + 'name': 'UserComment', + 'type': 'Ascii' + }, + 37520: { + 'name': 'SubSecTime', + 'type': 'Ascii' + }, + 37521: { + 'name': 'SubSecTimeOriginal', + 'type': 'Ascii' + }, + 37522: { + 'name': 'SubSecTimeDigitized', + 'type': 'Ascii' + }, + 40960: { + 'name': 'FlashpixVersion', + 'type': 'Undefined' + }, + 40961: { + 'name': 'ColorSpace', + 'type': 'Short' + }, + 40962: { + 'name': 'PixelXDimension', + 'type': 'Long' + }, + 40963: { + 'name': 'PixelYDimension', + 'type': 'Long' + }, + 40964: { + 'name': 'RelatedSoundFile', + 'type': 'Ascii' + }, + 40965: { + 'name': 'InteroperabilityTag', + 'type': 'Long' + }, + 41483: { + 'name': 'FlashEnergy', + 'type': 'Rational' + }, + 41484: { + 'name': 'SpatialFrequencyResponse', + 'type': 'Undefined' + }, + 41486: { + 'name': 'FocalPlaneXResolution', + 'type': 'Rational' + }, + 41487: { + 'name': 'FocalPlaneYResolution', + 'type': 'Rational' + }, + 41488: { + 'name': 'FocalPlaneResolutionUnit', + 'type': 'Short' + }, + 41492: { + 'name': 'SubjectLocation', + 'type': 'Short' + }, + 41493: { + 'name': 'ExposureIndex', + 'type': 'Rational' + }, + 41495: { + 'name': 'SensingMethod', + 'type': 'Short' + }, + 41728: { + 'name': 'FileSource', + 'type': 'Undefined' + }, + 41729: { + 'name': 'SceneType', + 'type': 'Undefined' + }, + 41730: { + 'name': 'CFAPattern', + 'type': 'Undefined' + }, + 41985: { + 'name': 'CustomRendered', + 'type': 'Short' + }, + 41986: { + 'name': 'ExposureMode', + 'type': 'Short' + }, + 41987: { + 'name': 'WhiteBalance', + 'type': 'Short' + }, + 41988: { + 'name': 'DigitalZoomRatio', + 'type': 'Rational' + }, + 41989: { + 'name': 'FocalLengthIn35mmFilm', + 'type': 'Short' + }, + 41990: { + 'name': 'SceneCaptureType', + 'type': 'Short' + }, + 41991: { + 'name': 'GainControl', + 'type': 'Short' + }, + 41992: { + 'name': 'Contrast', + 'type': 'Short' + }, + 41993: { + 'name': 'Saturation', + 'type': 'Short' + }, + 41994: { + 'name': 'Sharpness', + 'type': 'Short' + }, + 41995: { + 'name': 'DeviceSettingDescription', + 'type': 'Undefined' + }, + 41996: { + 'name': 'SubjectDistanceRange', + 'type': 'Short' + }, + 42016: { + 'name': 'ImageUniqueID', + 'type': 'Ascii' + }, + 42032: { + 'name': 'CameraOwnerName', + 'type': 'Ascii' + }, + 42033: { + 'name': 'BodySerialNumber', + 'type': 'Ascii' + }, + 42034: { + 'name': 'LensSpecification', + 'type': 'Rational' + }, + 42035: { + 'name': 'LensMake', + 'type': 'Ascii' + }, + 42036: { + 'name': 'LensModel', + 'type': 'Ascii' + }, + 42037: { + 'name': 'LensSerialNumber', + 'type': 'Ascii' + }, + 42240: { + 'name': 'Gamma', + 'type': 'Rational' + } + }, + 'GPS': { + 0: { + 'name': 'GPSVersionID', + 'type': 'Byte' + }, + 1: { + 'name': 'GPSLatitudeRef', + 'type': 'Ascii' + }, + 2: { + 'name': 'GPSLatitude', + 'type': 'Rational' + }, + 3: { + 'name': 'GPSLongitudeRef', + 'type': 'Ascii' + }, + 4: { + 'name': 'GPSLongitude', + 'type': 'Rational' + }, + 5: { + 'name': 'GPSAltitudeRef', + 'type': 'Byte' + }, + 6: { + 'name': 'GPSAltitude', + 'type': 'Rational' + }, + 7: { + 'name': 'GPSTimeStamp', + 'type': 'Rational' + }, + 8: { + 'name': 'GPSSatellites', + 'type': 'Ascii' + }, + 9: { + 'name': 'GPSStatus', + 'type': 'Ascii' + }, + 10: { + 'name': 'GPSMeasureMode', + 'type': 'Ascii' + }, + 11: { + 'name': 'GPSDOP', + 'type': 'Rational' + }, + 12: { + 'name': 'GPSSpeedRef', + 'type': 'Ascii' + }, + 13: { + 'name': 'GPSSpeed', + 'type': 'Rational' + }, + 14: { + 'name': 'GPSTrackRef', + 'type': 'Ascii' + }, + 15: { + 'name': 'GPSTrack', + 'type': 'Rational' + }, + 16: { + 'name': 'GPSImgDirectionRef', + 'type': 'Ascii' + }, + 17: { + 'name': 'GPSImgDirection', + 'type': 'Rational' + }, + 18: { + 'name': 'GPSMapDatum', + 'type': 'Ascii' + }, + 19: { + 'name': 'GPSDestLatitudeRef', + 'type': 'Ascii' + }, + 20: { + 'name': 'GPSDestLatitude', + 'type': 'Rational' + }, + 21: { + 'name': 'GPSDestLongitudeRef', + 'type': 'Ascii' + }, + 22: { + 'name': 'GPSDestLongitude', + 'type': 'Rational' + }, + 23: { + 'name': 'GPSDestBearingRef', + 'type': 'Ascii' + }, + 24: { + 'name': 'GPSDestBearing', + 'type': 'Rational' + }, + 25: { + 'name': 'GPSDestDistanceRef', + 'type': 'Ascii' + }, + 26: { + 'name': 'GPSDestDistance', + 'type': 'Rational' + }, + 27: { + 'name': 'GPSProcessingMethod', + 'type': 'Undefined' + }, + 28: { + 'name': 'GPSAreaInformation', + 'type': 'Undefined' + }, + 29: { + 'name': 'GPSDateStamp', + 'type': 'Ascii' + }, + 30: { + 'name': 'GPSDifferential', + 'type': 'Short' + }, + 31: { + 'name': 'GPSHPositioningError', + 'type': 'Rational' + } + }, + 'Interop': { + 1: { + 'name': 'InteroperabilityIndex', + 'type': 'Ascii' + } + }, + }; + TAGS["0th"] = TAGS["Image"]; + TAGS["1st"] = TAGS["Image"]; + that.TAGS = TAGS; + + + that.ImageIFD = { + ProcessingSoftware:11, + NewSubfileType:254, + SubfileType:255, + ImageWidth:256, + ImageLength:257, + BitsPerSample:258, + Compression:259, + PhotometricInterpretation:262, + Threshholding:263, + CellWidth:264, + CellLength:265, + FillOrder:266, + DocumentName:269, + ImageDescription:270, + Make:271, + Model:272, + StripOffsets:273, + Orientation:274, + SamplesPerPixel:277, + RowsPerStrip:278, + StripByteCounts:279, + XResolution:282, + YResolution:283, + PlanarConfiguration:284, + GrayResponseUnit:290, + GrayResponseCurve:291, + T4Options:292, + T6Options:293, + ResolutionUnit:296, + TransferFunction:301, + Software:305, + DateTime:306, + Artist:315, + HostComputer:316, + Predictor:317, + WhitePoint:318, + PrimaryChromaticities:319, + ColorMap:320, + HalftoneHints:321, + TileWidth:322, + TileLength:323, + TileOffsets:324, + TileByteCounts:325, + SubIFDs:330, + InkSet:332, + InkNames:333, + NumberOfInks:334, + DotRange:336, + TargetPrinter:337, + ExtraSamples:338, + SampleFormat:339, + SMinSampleValue:340, + SMaxSampleValue:341, + TransferRange:342, + ClipPath:343, + XClipPathUnits:344, + YClipPathUnits:345, + Indexed:346, + JPEGTables:347, + OPIProxy:351, + JPEGProc:512, + JPEGInterchangeFormat:513, + JPEGInterchangeFormatLength:514, + JPEGRestartInterval:515, + JPEGLosslessPredictors:517, + JPEGPointTransforms:518, + JPEGQTables:519, + JPEGDCTables:520, + JPEGACTables:521, + YCbCrCoefficients:529, + YCbCrSubSampling:530, + YCbCrPositioning:531, + ReferenceBlackWhite:532, + XMLPacket:700, + Rating:18246, + RatingPercent:18249, + ImageID:32781, + CFARepeatPatternDim:33421, + CFAPattern:33422, + BatteryLevel:33423, + Copyright:33432, + ExposureTime:33434, + ImageResources:34377, + ExifTag:34665, + InterColorProfile:34675, + GPSTag:34853, + Interlace:34857, + TimeZoneOffset:34858, + SelfTimerMode:34859, + FlashEnergy:37387, + SpatialFrequencyResponse:37388, + Noise:37389, + FocalPlaneXResolution:37390, + FocalPlaneYResolution:37391, + FocalPlaneResolutionUnit:37392, + ImageNumber:37393, + SecurityClassification:37394, + ImageHistory:37395, + ExposureIndex:37397, + TIFFEPStandardID:37398, + SensingMethod:37399, + XPTitle:40091, + XPComment:40092, + XPAuthor:40093, + XPKeywords:40094, + XPSubject:40095, + PrintImageMatching:50341, + DNGVersion:50706, + DNGBackwardVersion:50707, + UniqueCameraModel:50708, + LocalizedCameraModel:50709, + CFAPlaneColor:50710, + CFALayout:50711, + LinearizationTable:50712, + BlackLevelRepeatDim:50713, + BlackLevel:50714, + BlackLevelDeltaH:50715, + BlackLevelDeltaV:50716, + WhiteLevel:50717, + DefaultScale:50718, + DefaultCropOrigin:50719, + DefaultCropSize:50720, + ColorMatrix1:50721, + ColorMatrix2:50722, + CameraCalibration1:50723, + CameraCalibration2:50724, + ReductionMatrix1:50725, + ReductionMatrix2:50726, + AnalogBalance:50727, + AsShotNeutral:50728, + AsShotWhiteXY:50729, + BaselineExposure:50730, + BaselineNoise:50731, + BaselineSharpness:50732, + BayerGreenSplit:50733, + LinearResponseLimit:50734, + CameraSerialNumber:50735, + LensInfo:50736, + ChromaBlurRadius:50737, + AntiAliasStrength:50738, + ShadowScale:50739, + DNGPrivateData:50740, + MakerNoteSafety:50741, + CalibrationIlluminant1:50778, + CalibrationIlluminant2:50779, + BestQualityScale:50780, + RawDataUniqueID:50781, + OriginalRawFileName:50827, + OriginalRawFileData:50828, + ActiveArea:50829, + MaskedAreas:50830, + AsShotICCProfile:50831, + AsShotPreProfileMatrix:50832, + CurrentICCProfile:50833, + CurrentPreProfileMatrix:50834, + ColorimetricReference:50879, + CameraCalibrationSignature:50931, + ProfileCalibrationSignature:50932, + AsShotProfileName:50934, + NoiseReductionApplied:50935, + ProfileName:50936, + ProfileHueSatMapDims:50937, + ProfileHueSatMapData1:50938, + ProfileHueSatMapData2:50939, + ProfileToneCurve:50940, + ProfileEmbedPolicy:50941, + ProfileCopyright:50942, + ForwardMatrix1:50964, + ForwardMatrix2:50965, + PreviewApplicationName:50966, + PreviewApplicationVersion:50967, + PreviewSettingsName:50968, + PreviewSettingsDigest:50969, + PreviewColorSpace:50970, + PreviewDateTime:50971, + RawImageDigest:50972, + OriginalRawFileDigest:50973, + SubTileBlockSize:50974, + RowInterleaveFactor:50975, + ProfileLookTableDims:50981, + ProfileLookTableData:50982, + OpcodeList1:51008, + OpcodeList2:51009, + OpcodeList3:51022, + NoiseProfile:51041, + }; + + + that.ExifIFD = { + ExposureTime:33434, + FNumber:33437, + ExposureProgram:34850, + SpectralSensitivity:34852, + ISOSpeedRatings:34855, + OECF:34856, + SensitivityType:34864, + StandardOutputSensitivity:34865, + RecommendedExposureIndex:34866, + ISOSpeed:34867, + ISOSpeedLatitudeyyy:34868, + ISOSpeedLatitudezzz:34869, + ExifVersion:36864, + DateTimeOriginal:36867, + DateTimeDigitized:36868, + ComponentsConfiguration:37121, + CompressedBitsPerPixel:37122, + ShutterSpeedValue:37377, + ApertureValue:37378, + BrightnessValue:37379, + ExposureBiasValue:37380, + MaxApertureValue:37381, + SubjectDistance:37382, + MeteringMode:37383, + LightSource:37384, + Flash:37385, + FocalLength:37386, + SubjectArea:37396, + MakerNote:37500, + UserComment:37510, + SubSecTime:37520, + SubSecTimeOriginal:37521, + SubSecTimeDigitized:37522, + FlashpixVersion:40960, + ColorSpace:40961, + PixelXDimension:40962, + PixelYDimension:40963, + RelatedSoundFile:40964, + InteroperabilityTag:40965, + FlashEnergy:41483, + SpatialFrequencyResponse:41484, + FocalPlaneXResolution:41486, + FocalPlaneYResolution:41487, + FocalPlaneResolutionUnit:41488, + SubjectLocation:41492, + ExposureIndex:41493, + SensingMethod:41495, + FileSource:41728, + SceneType:41729, + CFAPattern:41730, + CustomRendered:41985, + ExposureMode:41986, + WhiteBalance:41987, + DigitalZoomRatio:41988, + FocalLengthIn35mmFilm:41989, + SceneCaptureType:41990, + GainControl:41991, + Contrast:41992, + Saturation:41993, + Sharpness:41994, + DeviceSettingDescription:41995, + SubjectDistanceRange:41996, + ImageUniqueID:42016, + CameraOwnerName:42032, + BodySerialNumber:42033, + LensSpecification:42034, + LensMake:42035, + LensModel:42036, + LensSerialNumber:42037, + Gamma:42240, + }; + + + that.GPSIFD = { + GPSVersionID:0, + GPSLatitudeRef:1, + GPSLatitude:2, + GPSLongitudeRef:3, + GPSLongitude:4, + GPSAltitudeRef:5, + GPSAltitude:6, + GPSTimeStamp:7, + GPSSatellites:8, + GPSStatus:9, + GPSMeasureMode:10, + GPSDOP:11, + GPSSpeedRef:12, + GPSSpeed:13, + GPSTrackRef:14, + GPSTrack:15, + GPSImgDirectionRef:16, + GPSImgDirection:17, + GPSMapDatum:18, + GPSDestLatitudeRef:19, + GPSDestLatitude:20, + GPSDestLongitudeRef:21, + GPSDestLongitude:22, + GPSDestBearingRef:23, + GPSDestBearing:24, + GPSDestDistanceRef:25, + GPSDestDistance:26, + GPSProcessingMethod:27, + GPSAreaInformation:28, + GPSDateStamp:29, + GPSDifferential:30, + GPSHPositioningError:31, + }; + + + that.InteropIFD = { + InteroperabilityIndex:1, + }; + + that.GPSHelper = { + degToDmsRational:function (degFloat) { + var degAbs = Math.abs(degFloat); + var minFloat = degAbs % 1 * 60; + var secFloat = minFloat % 1 * 60; + var deg = Math.floor(degAbs); + var min = Math.floor(minFloat); + var sec = Math.round(secFloat * 100); + + return [[deg, 1], [min, 1], [sec, 100]]; + }, + + dmsRationalToDeg:function (dmsArray, ref) { + var sign = (ref === 'S' || ref === 'W') ? -1.0 : 1.0; + var deg = dmsArray[0][0] / dmsArray[0][1] + + dmsArray[1][0] / dmsArray[1][1] / 60.0 + + dmsArray[2][0] / dmsArray[2][1] / 3600.0; + + return deg * sign; + } + }; + + + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = that; + } + exports.piexif = that; + } else { + window.piexif = that; + } + +})(); diff --git a/wire/modules/Inputfield/InputfieldImage/piexif.min.js b/wire/modules/Inputfield/InputfieldImage/piexif.min.js new file mode 100644 index 00000000..eb016550 --- /dev/null +++ b/wire/modules/Inputfield/InputfieldImage/piexif.min.js @@ -0,0 +1 @@ +(function(){"use strict";var that={};that.version="1.0.4";that.remove=function(jpeg){var b64=false;if(jpeg.slice(0,2)=="ÿØ"){}else if(jpeg.slice(0,23)=="data:image/jpeg;base64,"||jpeg.slice(0,22)=="data:image/jpg;base64,"){jpeg=atob(jpeg.split(",")[1]);b64=true}else{throw new Error("Given data is not jpeg.")}var segments=splitIntoSegments(jpeg);var newSegments=segments.filter(function(seg){return!(seg.slice(0,2)=="ÿá"&&seg.slice(4,10)=="Exif\0\0")});var new_data=newSegments.join("");if(b64){new_data="data:image/jpeg;base64,"+btoa(new_data)}return new_data};that.insert=function(exif,jpeg){var b64=false;if(exif.slice(0,6)!="Exif\0\0"){throw new Error("Given data is not exif.")}if(jpeg.slice(0,2)=="ÿØ"){}else if(jpeg.slice(0,23)=="data:image/jpeg;base64,"||jpeg.slice(0,22)=="data:image/jpg;base64,"){jpeg=atob(jpeg.split(",")[1]);b64=true}else{throw new Error("Given data is not jpeg.")}var exifStr="ÿá"+pack(">H",[exif.length+2])+exif;var segments=splitIntoSegments(jpeg);var new_data=mergeSegments(segments,exifStr);if(b64){new_data="data:image/jpeg;base64,"+btoa(new_data)}return new_data};that.load=function(data){var input_data;if(typeof data=="string"){if(data.slice(0,2)=="ÿØ"){input_data=data}else if(data.slice(0,23)=="data:image/jpeg;base64,"||data.slice(0,22)=="data:image/jpg;base64,"){input_data=atob(data.split(",")[1])}else if(data.slice(0,4)=="Exif"){input_data=data.slice(6)}else{throw new Error("'load' gots invalid file data.")}}else{throw new Error("'load' gots invalid type argument.")}var exifDict={};var exif_dict={"0th":{},Exif:{},GPS:{},Interop:{},"1st":{},thumbnail:null};var exifReader=new ExifReader(input_data);if(exifReader.tiftag===null){return exif_dict}if(exifReader.tiftag.slice(0,2)=="II"){exifReader.endian_mark="<"}else{exifReader.endian_mark=">"}var pointer=unpack(exifReader.endian_mark+"L",exifReader.tiftag.slice(4,8))[0];exif_dict["0th"]=exifReader.get_ifd(pointer,"0th");var first_ifd_pointer=exif_dict["0th"]["first_ifd_pointer"];delete exif_dict["0th"]["first_ifd_pointer"];if(34665 in exif_dict["0th"]){pointer=exif_dict["0th"][34665];exif_dict["Exif"]=exifReader.get_ifd(pointer,"Exif")}if(34853 in exif_dict["0th"]){pointer=exif_dict["0th"][34853];exif_dict["GPS"]=exifReader.get_ifd(pointer,"GPS")}if(40965 in exif_dict["Exif"]){pointer=exif_dict["Exif"][40965];exif_dict["Interop"]=exifReader.get_ifd(pointer,"Interop")}if(first_ifd_pointer!="\0\0\0\0"){pointer=unpack(exifReader.endian_mark+"L",first_ifd_pointer)[0];exif_dict["1st"]=exifReader.get_ifd(pointer,"1st");if(513 in exif_dict["1st"]&&514 in exif_dict["1st"]){var end=exif_dict["1st"][513]+exif_dict["1st"][514];var thumb=exifReader.tiftag.slice(exif_dict["1st"][513],end);exif_dict["thumbnail"]=thumb}}return exif_dict};that.dump=function(exif_dict_original){var TIFF_HEADER_LENGTH=8;var exif_dict=copy(exif_dict_original);var header="Exif\0\0MM\0*\0\0\0\b";var exif_is=false;var gps_is=false;var interop_is=false;var first_is=false;var zeroth_ifd,exif_ifd,interop_ifd,gps_ifd,first_ifd;if("0th"in exif_dict){zeroth_ifd=exif_dict["0th"]}else{zeroth_ifd={}}if("Exif"in exif_dict&&Object.keys(exif_dict["Exif"]).length||"Interop"in exif_dict&&Object.keys(exif_dict["Interop"]).length){zeroth_ifd[34665]=1;exif_is=true;exif_ifd=exif_dict["Exif"];if("Interop"in exif_dict&&Object.keys(exif_dict["Interop"]).length){exif_ifd[40965]=1;interop_is=true;interop_ifd=exif_dict["Interop"]}else if(Object.keys(exif_ifd).indexOf(that.ExifIFD.InteroperabilityTag.toString())>-1){delete exif_ifd[40965]}}else if(Object.keys(zeroth_ifd).indexOf(that.ImageIFD.ExifTag.toString())>-1){delete zeroth_ifd[34665]}if("GPS"in exif_dict&&Object.keys(exif_dict["GPS"]).length){zeroth_ifd[that.ImageIFD.GPSTag]=1;gps_is=true;gps_ifd=exif_dict["GPS"]}else if(Object.keys(zeroth_ifd).indexOf(that.ImageIFD.GPSTag.toString())>-1){delete zeroth_ifd[that.ImageIFD.GPSTag]}if("1st"in exif_dict&&"thumbnail"in exif_dict&&exif_dict["thumbnail"]!=null){first_is=true;exif_dict["1st"][513]=1;exif_dict["1st"][514]=1;first_ifd=exif_dict["1st"]}var zeroth_set=_dict_to_bytes(zeroth_ifd,"0th",0);var zeroth_length=zeroth_set[0].length+exif_is*12+gps_is*12+4+zeroth_set[1].length;var exif_set,exif_bytes="",exif_length=0,gps_set,gps_bytes="",gps_length=0,interop_set,interop_bytes="",interop_length=0,first_set,first_bytes="",thumbnail;if(exif_is){exif_set=_dict_to_bytes(exif_ifd,"Exif",zeroth_length);exif_length=exif_set[0].length+interop_is*12+exif_set[1].length}if(gps_is){gps_set=_dict_to_bytes(gps_ifd,"GPS",zeroth_length+exif_length);gps_bytes=gps_set.join("");gps_length=gps_bytes.length}if(interop_is){var offset=zeroth_length+exif_length+gps_length;interop_set=_dict_to_bytes(interop_ifd,"Interop",offset);interop_bytes=interop_set.join("");interop_length=interop_bytes.length}if(first_is){var offset=zeroth_length+exif_length+gps_length+interop_length;first_set=_dict_to_bytes(first_ifd,"1st",offset);thumbnail=_get_thumbnail(exif_dict["thumbnail"]);if(thumbnail.length>64e3){throw new Error("Given thumbnail is too large. max 64kB")}}var exif_pointer="",gps_pointer="",interop_pointer="",first_ifd_pointer="\0\0\0\0";if(exif_is){var pointer_value=TIFF_HEADER_LENGTH+zeroth_length;var pointer_str=pack(">L",[pointer_value]);var key=34665;var key_str=pack(">H",[key]);var type_str=pack(">H",[TYPES["Long"]]);var length_str=pack(">L",[1]);exif_pointer=key_str+type_str+length_str+pointer_str}if(gps_is){var pointer_value=TIFF_HEADER_LENGTH+zeroth_length+exif_length;var pointer_str=pack(">L",[pointer_value]);var key=34853;var key_str=pack(">H",[key]);var type_str=pack(">H",[TYPES["Long"]]);var length_str=pack(">L",[1]);gps_pointer=key_str+type_str+length_str+pointer_str}if(interop_is){var pointer_value=TIFF_HEADER_LENGTH+zeroth_length+exif_length+gps_length;var pointer_str=pack(">L",[pointer_value]);var key=40965;var key_str=pack(">H",[key]);var type_str=pack(">H",[TYPES["Long"]]);var length_str=pack(">L",[1]);interop_pointer=key_str+type_str+length_str+pointer_str}if(first_is){var pointer_value=TIFF_HEADER_LENGTH+zeroth_length+exif_length+gps_length+interop_length;first_ifd_pointer=pack(">L",[pointer_value]);var thumbnail_pointer=pointer_value+first_set[0].length+24+4+first_set[1].length;var thumbnail_p_bytes="\0\0\0\0"+pack(">L",[thumbnail_pointer]);var thumbnail_length_bytes="\0\0\0\0"+pack(">L",[thumbnail.length]);first_bytes=first_set[0]+thumbnail_p_bytes+thumbnail_length_bytes+"\0\0\0\0"+first_set[1]+thumbnail}var zeroth_bytes=zeroth_set[0]+exif_pointer+gps_pointer+first_ifd_pointer+zeroth_set[1];if(exif_is){exif_bytes=exif_set[0]+interop_pointer+exif_set[1]}return header+zeroth_bytes+exif_bytes+gps_bytes+interop_bytes+first_bytes};function copy(obj){return JSON.parse(JSON.stringify(obj))}function _get_thumbnail(jpeg){var segments=splitIntoSegments(jpeg);while("ÿà"<=segments[1].slice(0,2)&&segments[1].slice(0,2)<="ÿï"){segments=[segments[0]].concat(segments.slice(2))}return segments.join("")}function _pack_byte(array){return pack(">"+nStr("B",array.length),array)}function _pack_short(array){return pack(">"+nStr("H",array.length),array)}function _pack_long(array){return pack(">"+nStr("L",array.length),array)}function _value_to_bytes(raw_value,value_type,offset){var four_bytes_over="";var value_str="";var length,new_value,num,den;if(value_type=="Byte"){length=raw_value.length;if(length<=4){value_str=_pack_byte(raw_value)+nStr("\0",4-length)}else{value_str=pack(">L",[offset]);four_bytes_over=_pack_byte(raw_value)}}else if(value_type=="Short"){length=raw_value.length;if(length<=2){value_str=_pack_short(raw_value)+nStr("\0\0",2-length)}else{value_str=pack(">L",[offset]);four_bytes_over=_pack_short(raw_value)}}else if(value_type=="Long"){length=raw_value.length;if(length<=1){value_str=_pack_long(raw_value)}else{value_str=pack(">L",[offset]);four_bytes_over=_pack_long(raw_value)}}else if(value_type=="Ascii"){new_value=raw_value+"\0";length=new_value.length;if(length>4){value_str=pack(">L",[offset]);four_bytes_over=new_value}else{value_str=new_value+nStr("\0",4-length)}}else if(value_type=="Rational"){if(typeof raw_value[0]=="number"){length=1;num=raw_value[0];den=raw_value[1];new_value=pack(">L",[num])+pack(">L",[den])}else{length=raw_value.length;new_value="";for(var n=0;nL",[num])+pack(">L",[den])}}value_str=pack(">L",[offset]);four_bytes_over=new_value}else if(value_type=="SRational"){if(typeof raw_value[0]=="number"){length=1;num=raw_value[0];den=raw_value[1];new_value=pack(">l",[num])+pack(">l",[den])}else{length=raw_value.length;new_value="";for(var n=0;nl",[num])+pack(">l",[den])}}value_str=pack(">L",[offset]);four_bytes_over=new_value}else if(value_type=="Undefined"){length=raw_value.length;if(length>4){value_str=pack(">L",[offset]);four_bytes_over=raw_value}else{value_str=raw_value+nStr("\0",4-length)}}var length_str=pack(">L",[length]);return[length_str,value_str,four_bytes_over]}function _dict_to_bytes(ifd_dict,ifd,ifd_offset){var TIFF_HEADER_LENGTH=8;var tag_count=Object.keys(ifd_dict).length;var entry_header=pack(">H",[tag_count]);var entries_length;if(["0th","1st"].indexOf(ifd)>-1){entries_length=2+tag_count*12+4}else{entries_length=2+tag_count*12}var entries="";var values="";var key;for(var key in ifd_dict){if(typeof key=="string"){key=parseInt(key)}if(ifd=="0th"&&[34665,34853].indexOf(key)>-1){continue}else if(ifd=="Exif"&&key==40965){continue}else if(ifd=="1st"&&[513,514].indexOf(key)>-1){continue}var raw_value=ifd_dict[key];var key_str=pack(">H",[key]);var value_type=TAGS[ifd][key]["type"];var type_str=pack(">H",[TYPES[value_type]]);if(typeof raw_value=="number"){raw_value=[raw_value]}var offset=TIFF_HEADER_LENGTH+entries_length+ifd_offset+values.length;var b=_value_to_bytes(raw_value,value_type,offset);var length_str=b[0];var value_str=b[1];var four_bytes_over=b[2];entries+=key_str+type_str+length_str+value_str;values+=four_bytes_over}return[entry_header+entries,values]}function ExifReader(data){var segments,app1;if(data.slice(0,2)=="ÿØ"){segments=splitIntoSegments(data);app1=getExifSeg(segments);if(app1){this.tiftag=app1.slice(10)}else{this.tiftag=null}}else if(["II","MM"].indexOf(data.slice(0,2))>-1){this.tiftag=data}else if(data.slice(0,4)=="Exif"){this.tiftag=data.slice(6)}else{throw new Error("Given file is neither JPEG nor TIFF.")}}ExifReader.prototype={get_ifd:function(pointer,ifd_name){var ifd_dict={};var tag_count=unpack(this.endian_mark+"H",this.tiftag.slice(pointer,pointer+2))[0];var offset=pointer+2;var t;if(["0th","1st"].indexOf(ifd_name)>-1){t="Image"}else{t=ifd_name}for(var x=0;x4){pointer=unpack(this.endian_mark+"L",value)[0];data=unpack(this.endian_mark+nStr("B",length),this.tiftag.slice(pointer,pointer+length))}else{data=unpack(this.endian_mark+nStr("B",length),value.slice(0,length))}}else if(t==2){if(length>4){pointer=unpack(this.endian_mark+"L",value)[0];data=this.tiftag.slice(pointer,pointer+length-1)}else{data=value.slice(0,length-1)}}else if(t==3){if(length>2){pointer=unpack(this.endian_mark+"L",value)[0];data=unpack(this.endian_mark+nStr("H",length),this.tiftag.slice(pointer,pointer+length*2))}else{data=unpack(this.endian_mark+nStr("H",length),value.slice(0,length*2))}}else if(t==4){if(length>1){pointer=unpack(this.endian_mark+"L",value)[0];data=unpack(this.endian_mark+nStr("L",length),this.tiftag.slice(pointer,pointer+length*4))}else{data=unpack(this.endian_mark+nStr("L",length),value)}}else if(t==5){pointer=unpack(this.endian_mark+"L",value)[0];if(length>1){data=[];for(var x=0;x4){pointer=unpack(this.endian_mark+"L",value)[0];data=this.tiftag.slice(pointer,pointer+length)}else{data=value.slice(0,length)}}else if(t==9){if(length>1){pointer=unpack(this.endian_mark+"L",value)[0];data=unpack(this.endian_mark+nStr("l",length),this.tiftag.slice(pointer,pointer+length*4))}else{data=unpack(this.endian_mark+nStr("l",length),value)}}else if(t==10){pointer=unpack(this.endian_mark+"L",value)[0];if(length>1){data=[];for(var x=0;x>2;enc2=(chr1&3)<<4|chr2>>4;enc3=(chr2&15)<<2|chr3>>6;enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3)){enc4=64}output=output+keyStr.charAt(enc1)+keyStr.charAt(enc2)+keyStr.charAt(enc3)+keyStr.charAt(enc4)}return output}}if(typeof window!=="undefined"&&typeof window.atob==="function"){var atob=window.atob}if(typeof atob==="undefined"){var atob=function(input){var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;var keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!=64){output=output+String.fromCharCode(chr2)}if(enc4!=64){output=output+String.fromCharCode(chr3)}}return output}}function getImageSize(imageArray){var segments=slice2Segments(imageArray);var seg,width,height,SOF=[192,193,194,195,197,198,199,201,202,203,205,206,207];for(var x=0;x=0){height=seg[5]*256+seg[6];width=seg[7]*256+seg[8];break}}return[width,height]}function pack(mark,array){if(!(array instanceof Array)){throw new Error("'pack' error. Got invalid type argument.")}if(mark.length-1!=array.length){throw new Error("'pack' error. "+(mark.length-1)+" marks, "+array.length+" elements.")}var littleEndian;if(mark[0]=="<"){littleEndian=true}else if(mark[0]==">"){littleEndian=false}else{throw new Error("")}var packed="";var p=1;var val=null;var c=null;var valStr=null;while(c=mark[p]){if(c.toLowerCase()=="b"){val=array[p-1];if(c=="b"&&val<0){val+=256}if(val>255||val<0){throw new Error("'pack' error.")}else{valStr=String.fromCharCode(val)}}else if(c=="H"){val=array[p-1];if(val>65535||val<0){throw new Error("'pack' error.")}else{valStr=String.fromCharCode(Math.floor(val%65536/256))+String.fromCharCode(val%256);if(littleEndian){valStr=valStr.split("").reverse().join("")}}}else if(c.toLowerCase()=="l"){val=array[p-1];if(c=="l"&&val<0){val+=4294967296}if(val>4294967295||val<0){throw new Error("'pack' error.")}else{valStr=String.fromCharCode(Math.floor(val/16777216))+String.fromCharCode(Math.floor(val%16777216/65536))+String.fromCharCode(Math.floor(val%65536/256))+String.fromCharCode(val%256);if(littleEndian){valStr=valStr.split("").reverse().join("")}}}else{throw new Error("'pack' error.")}packed+=valStr;p+=1}return packed}function unpack(mark,str){if(typeof str!="string"){throw new Error("'unpack' error. Got invalid type argument.")}var l=0;for(var markPointer=1;markPointer"){littleEndian=false}else{throw new Error("'unpack' error.")}var unpacked=[];var strPointer=0;var p=1;var val=null;var c=null;var length=null;var sliced="";while(c=mark[p]){if(c.toLowerCase()=="b"){length=1;sliced=str.slice(strPointer,strPointer+length);val=sliced.charCodeAt(0);if(c=="b"&&val>=128){val-=256}}else if(c=="H"){length=2;sliced=str.slice(strPointer,strPointer+length);if(littleEndian){sliced=sliced.split("").reverse().join("")}val=sliced.charCodeAt(0)*256+sliced.charCodeAt(1)}else if(c.toLowerCase()=="l"){length=4;sliced=str.slice(strPointer,strPointer+length);if(littleEndian){sliced=sliced.split("").reverse().join("")}val=sliced.charCodeAt(0)*16777216+sliced.charCodeAt(1)*65536+sliced.charCodeAt(2)*256+sliced.charCodeAt(3);if(c=="l"&&val>=2147483648){val-=4294967296}}else{throw new Error("'unpack' error. "+c)}unpacked.push(val);strPointer+=length;p+=1}return unpacked}function nStr(ch,num){var str="";for(var i=0;iH",data.slice(head+2,head+4))[0];var endPoint=head+length+2;segments.push(data.slice(head,endPoint));head=endPoint}if(head>=data.length){throw new Error("Wrong JPEG data.")}}return segments}function getExifSeg(segments){var seg;for(var i=0;i