1 2/* 3 * Javascript EXIF Reader - jQuery plugin 0.1.3 4 * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ 5 * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt] 6 */ 7 8(function() { 9 10 11var BinaryFile = function(strData, iDataOffset, iDataLength) { 12 var data = strData; 13 var dataOffset = iDataOffset || 0; 14 var dataLength = 0; 15 16 this.getRawData = function() { 17 return data; 18 } 19 20 if (typeof strData == "string") { 21 dataLength = iDataLength || data.length; 22 23 this.getByteAt = function(iOffset) { 24 return data.charCodeAt(iOffset + dataOffset) & 0xFF; 25 } 26 } else if (typeof strData == "unknown") { 27 dataLength = iDataLength || IEBinary_getLength(data); 28 29 this.getByteAt = function(iOffset) { 30 return IEBinary_getByteAt(data, iOffset + dataOffset); 31 } 32 } 33 34 this.getLength = function() { 35 return dataLength; 36 } 37 38 this.getSByteAt = function(iOffset) { 39 var iByte = this.getByteAt(iOffset); 40 if (iByte > 127) 41 return iByte - 256; 42 else 43 return iByte; 44 } 45 46 this.getShortAt = function(iOffset, bBigEndian) { 47 var iShort = bBigEndian ? 48 (this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1) 49 : (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) 50 if (iShort < 0) iShort += 65536; 51 return iShort; 52 } 53 this.getSShortAt = function(iOffset, bBigEndian) { 54 var iUShort = this.getShortAt(iOffset, bBigEndian); 55 if (iUShort > 32767) 56 return iUShort - 65536; 57 else 58 return iUShort; 59 } 60 this.getLongAt = function(iOffset, bBigEndian) { 61 var iByte1 = this.getByteAt(iOffset), 62 iByte2 = this.getByteAt(iOffset + 1), 63 iByte3 = this.getByteAt(iOffset + 2), 64 iByte4 = this.getByteAt(iOffset + 3); 65 66 var iLong = bBigEndian ? 67 (((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4 68 : (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; 69 if (iLong < 0) iLong += 4294967296; 70 return iLong; 71 } 72 this.getSLongAt = function(iOffset, bBigEndian) { 73 var iULong = this.getLongAt(iOffset, bBigEndian); 74 if (iULong > 2147483647) 75 return iULong - 4294967296; 76 else 77 return iULong; 78 } 79 this.getStringAt = function(iOffset, iLength) { 80 var aStr = []; 81 for (var i=iOffset,j=0;i<iOffset+iLength;i++,j++) { 82 aStr[j] = String.fromCharCode(this.getByteAt(i)); 83 } 84 return aStr.join(""); 85 } 86 87 this.getCharAt = function(iOffset) { 88 return String.fromCharCode(this.getByteAt(iOffset)); 89 } 90 this.toBase64 = function() { 91 return window.btoa(data); 92 } 93 this.fromBase64 = function(strBase64) { 94 data = window.atob(strBase64); 95 } 96} 97 98 99var BinaryAjax = (function() { 100 101 function createRequest() { 102 var oHTTP = null; 103 if (window.XMLHttpRequest) { 104 oHTTP = new XMLHttpRequest(); 105 } else if (window.ActiveXObject) { 106 oHTTP = new ActiveXObject("Microsoft.XMLHTTP"); 107 } 108 return oHTTP; 109 } 110 111 function getHead(strURL, fncCallback, fncError) { 112 var oHTTP = createRequest(); 113 if (oHTTP) { 114 if (fncCallback) { 115 if (typeof(oHTTP.onload) != "undefined") { 116 oHTTP.onload = function() { 117 if (oHTTP.status == "200") { 118 fncCallback(this); 119 } else { 120 if (fncError) fncError(); 121 } 122 oHTTP = null; 123 }; 124 } else { 125 oHTTP.onreadystatechange = function() { 126 if (oHTTP.readyState == 4) { 127 if (oHTTP.status == "200") { 128 fncCallback(this); 129 } else { 130 if (fncError) fncError(); 131 } 132 oHTTP = null; 133 } 134 }; 135 } 136 } 137 oHTTP.open("HEAD", strURL, true); 138 oHTTP.send(null); 139 } else { 140 if (fncError) fncError(); 141 } 142 } 143 144 function sendRequest(strURL, fncCallback, fncError, aRange, bAcceptRanges, iFileSize) { 145 var oHTTP = createRequest(); 146 if (oHTTP) { 147 148 var iDataOffset = 0; 149 if (aRange && !bAcceptRanges) { 150 iDataOffset = aRange[0]; 151 } 152 var iDataLen = 0; 153 if (aRange) { 154 iDataLen = aRange[1]-aRange[0]+1; 155 } 156 157 if (fncCallback) { 158 if (typeof(oHTTP.onload) != "undefined") { 159 oHTTP.onload = function() { 160 if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") { 161 this.binaryResponse = new BinaryFile(this.responseText, iDataOffset, iDataLen); 162 this.fileSize = iFileSize || this.getResponseHeader("Content-Length"); 163 fncCallback(this); 164 } else { 165 if (fncError) fncError(); 166 } 167 oHTTP = null; 168 }; 169 } else { 170 oHTTP.onreadystatechange = function() { 171 if (oHTTP.readyState == 4) { 172 if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") { 173 this.binaryResponse = new BinaryFile(oHTTP.responseBody, iDataOffset, iDataLen); 174 this.fileSize = iFileSize || this.getResponseHeader("Content-Length"); 175 fncCallback(this); 176 } else { 177 if (fncError) fncError(); 178 } 179 oHTTP = null; 180 } 181 }; 182 } 183 } 184 oHTTP.open("GET", strURL, true); 185 186 if (oHTTP.overrideMimeType) oHTTP.overrideMimeType('text/plain; charset=x-user-defined'); 187 188 if (aRange && bAcceptRanges) { 189 oHTTP.setRequestHeader("Range", "bytes=" + aRange[0] + "-" + aRange[1]); 190 } 191 192 oHTTP.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 1970 00:00:00 GMT"); 193 194 oHTTP.send(null); 195 } else { 196 if (fncError) fncError(); 197 } 198 } 199 200 return function(strURL, fncCallback, fncError, aRange) { 201 202 if (aRange) { 203 getHead( 204 strURL, 205 function(oHTTP) { 206 var iLength = parseInt(oHTTP.getResponseHeader("Content-Length"),10); 207 var strAcceptRanges = oHTTP.getResponseHeader("Accept-Ranges"); 208 209 var iStart, iEnd; 210 iStart = aRange[0]; 211 if (aRange[0] < 0) 212 iStart += iLength; 213 iEnd = iStart + aRange[1] - 1; 214 215 sendRequest(strURL, fncCallback, fncError, [iStart, iEnd], (strAcceptRanges == "bytes"), iLength); 216 } 217 ); 218 219 } else { 220 sendRequest(strURL, fncCallback, fncError); 221 } 222 } 223 224}()); 225 226/* 227document.write( 228 "<script type='text/vbscript'>\r\n" 229 + "Function IEBinary_getByteAt(strBinary, iOffset)\r\n" 230 + " IEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\n" 231 + "End Function\r\n" 232 + "Function IEBinary_getLength(strBinary)\r\n" 233 + " IEBinary_getLength = LenB(strBinary)\r\n" 234 + "End Function\r\n" 235 + "</script>\r\n" 236); 237*/ 238document.write( 239 "<script type='text/vbscript'>\r\n" 240 + "Function IEBinary_getByteAt(strBinary, iOffset)\r\n" 241 + " IEBinary_getByteAt = AscB(MidB(strBinary, iOffset + 1, 1))\r\n" 242 + "End Function\r\n" 243 + "Function IEBinary_getBytesAt(strBinary, iOffset, iLength)\r\n" 244 + " Dim aBytes()\r\n" 245 + " ReDim aBytes(iLength - 1)\r\n" 246 + " For i = 0 To iLength - 1\r\n" 247 + " aBytes(i) = IEBinary_getByteAt(strBinary, iOffset + i)\r\n" 248 + " Next\r\n" 249 + " IEBinary_getBytesAt = aBytes\r\n" 250 + "End Function\r\n" 251 + "Function IEBinary_getLength(strBinary)\r\n" 252 + " IEBinary_getLength = LenB(strBinary)\r\n" 253 + "End Function\r\n" 254 + "</script>\r\n" 255); 256 257var EXIF = {}; 258 259(function() { 260 261var bDebug = false; 262 263EXIF.Tags = { 264 265 // version tags 266 0x9000 : "ExifVersion", // EXIF version 267 0xA000 : "FlashpixVersion", // Flashpix format version 268 269 // colorspace tags 270 0xA001 : "ColorSpace", // Color space information tag 271 272 // image configuration 273 0xA002 : "PixelXDimension", // Valid width of meaningful image 274 0xA003 : "PixelYDimension", // Valid height of meaningful image 275 0x9101 : "ComponentsConfiguration", // Information about channels 276 0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel 277 278 // user information 279 0x927C : "MakerNote", // Any desired information written by the manufacturer 280 0x9286 : "UserComment", // Comments by user 281 282 // related file 283 0xA004 : "RelatedSoundFile", // Name of related sound file 284 285 // date and time 286 0x9003 : "DateTimeOriginal", // Date and time when the original image was generated 287 0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally 288 0x9290 : "SubsecTime", // Fractions of seconds for DateTime 289 0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal 290 0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized 291 292 // picture-taking conditions 293 0x829A : "ExposureTime", // Exposure time (in seconds) 294 0x829D : "FNumber", // F number 295 0x8822 : "ExposureProgram", // Exposure program 296 0x8824 : "SpectralSensitivity", // Spectral sensitivity 297 0x8827 : "ISOSpeedRatings", // ISO speed rating 298 0x8828 : "OECF", // Optoelectric conversion factor 299 0x9201 : "ShutterSpeedValue", // Shutter speed 300 0x9202 : "ApertureValue", // Lens aperture 301 0x9203 : "BrightnessValue", // Value of brightness 302 0x9204 : "ExposureBias", // Exposure bias 303 0x9205 : "MaxApertureValue", // Smallest F number of lens 304 0x9206 : "SubjectDistance", // Distance to subject in meters 305 0x9207 : "MeteringMode", // Metering mode 306 0x9208 : "LightSource", // Kind of light source 307 0x9209 : "Flash", // Flash status 308 0x9214 : "SubjectArea", // Location and area of main subject 309 0x920A : "FocalLength", // Focal length of the lens in mm 310 0xA20B : "FlashEnergy", // Strobe energy in BCPS 311 0xA20C : "SpatialFrequencyResponse", // 312 0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit 313 0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit 314 0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution 315 0xA214 : "SubjectLocation", // Location of subject in image 316 0xA215 : "ExposureIndex", // Exposure index selected on camera 317 0xA217 : "SensingMethod", // Image sensor type 318 0xA300 : "FileSource", // Image source (3 == DSC) 319 0xA301 : "SceneType", // Scene type (1 == directly photographed) 320 0xA302 : "CFAPattern", // Color filter array geometric pattern 321 0xA401 : "CustomRendered", // Special processing 322 0xA402 : "ExposureMode", // Exposure mode 323 0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual 324 0xA404 : "DigitalZoomRation", // Digital zoom ratio 325 0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm) 326 0xA406 : "SceneCaptureType", // Type of scene 327 0xA407 : "GainControl", // Degree of overall image gain adjustment 328 0xA408 : "Contrast", // Direction of contrast processing applied by camera 329 0xA409 : "Saturation", // Direction of saturation processing applied by camera 330 0xA40A : "Sharpness", // Direction of sharpness processing applied by camera 331 0xA40B : "DeviceSettingDescription", // 332 0xA40C : "SubjectDistanceRange", // Distance to subject 333 334 // other tags 335 0xA005 : "InteroperabilityIFDPointer", 336 0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image 337}; 338 339EXIF.TiffTags = { 340 0x0100 : "ImageWidth", 341 0x0101 : "ImageHeight", 342 0x8769 : "ExifIFDPointer", 343 0x8825 : "GPSInfoIFDPointer", 344 0xA005 : "InteroperabilityIFDPointer", 345 0x0102 : "BitsPerSample", 346 0x0103 : "Compression", 347 0x0106 : "PhotometricInterpretation", 348 0x0112 : "Orientation", 349 0x0115 : "SamplesPerPixel", 350 0x011C : "PlanarConfiguration", 351 0x0212 : "YCbCrSubSampling", 352 0x0213 : "YCbCrPositioning", 353 0x011A : "XResolution", 354 0x011B : "YResolution", 355 0x0128 : "ResolutionUnit", 356 0x0111 : "StripOffsets", 357 0x0116 : "RowsPerStrip", 358 0x0117 : "StripByteCounts", 359 0x0201 : "JPEGInterchangeFormat", 360 0x0202 : "JPEGInterchangeFormatLength", 361 0x012D : "TransferFunction", 362 0x013E : "WhitePoint", 363 0x013F : "PrimaryChromaticities", 364 0x0211 : "YCbCrCoefficients", 365 0x0214 : "ReferenceBlackWhite", 366 0x0132 : "DateTime", 367 0x010E : "ImageDescription", 368 0x010F : "Make", 369 0x0110 : "Model", 370 0x0131 : "Software", 371 0x013B : "Artist", 372 0x8298 : "Copyright" 373} 374 375EXIF.GPSTags = { 376 0x0000 : "GPSVersionID", 377 0x0001 : "GPSLatitudeRef", 378 0x0002 : "GPSLatitude", 379 0x0003 : "GPSLongitudeRef", 380 0x0004 : "GPSLongitude", 381 0x0005 : "GPSAltitudeRef", 382 0x0006 : "GPSAltitude", 383 0x0007 : "GPSTimeStamp", 384 0x0008 : "GPSSatellites", 385 0x0009 : "GPSStatus", 386 0x000A : "GPSMeasureMode", 387 0x000B : "GPSDOP", 388 0x000C : "GPSSpeedRef", 389 0x000D : "GPSSpeed", 390 0x000E : "GPSTrackRef", 391 0x000F : "GPSTrack", 392 0x0010 : "GPSImgDirectionRef", 393 0x0011 : "GPSImgDirection", 394 0x0012 : "GPSMapDatum", 395 0x0013 : "GPSDestLatitudeRef", 396 0x0014 : "GPSDestLatitude", 397 0x0015 : "GPSDestLongitudeRef", 398 0x0016 : "GPSDestLongitude", 399 0x0017 : "GPSDestBearingRef", 400 0x0018 : "GPSDestBearing", 401 0x0019 : "GPSDestDistanceRef", 402 0x001A : "GPSDestDistance", 403 0x001B : "GPSProcessingMethod", 404 0x001C : "GPSAreaInformation", 405 0x001D : "GPSDateStamp", 406 0x001E : "GPSDifferential" 407} 408 409EXIF.StringValues = { 410 ExposureProgram : { 411 0 : "Not defined", 412 1 : "Manual", 413 2 : "Normal program", 414 3 : "Aperture priority", 415 4 : "Shutter priority", 416 5 : "Creative program", 417 6 : "Action program", 418 7 : "Portrait mode", 419 8 : "Landscape mode" 420 }, 421 MeteringMode : { 422 0 : "Unknown", 423 1 : "Average", 424 2 : "CenterWeightedAverage", 425 3 : "Spot", 426 4 : "MultiSpot", 427 5 : "Pattern", 428 6 : "Partial", 429 255 : "Other" 430 }, 431 LightSource : { 432 0 : "Unknown", 433 1 : "Daylight", 434 2 : "Fluorescent", 435 3 : "Tungsten (incandescent light)", 436 4 : "Flash", 437 9 : "Fine weather", 438 10 : "Cloudy weather", 439 11 : "Shade", 440 12 : "Daylight fluorescent (D 5700 - 7100K)", 441 13 : "Day white fluorescent (N 4600 - 5400K)", 442 14 : "Cool white fluorescent (W 3900 - 4500K)", 443 15 : "White fluorescent (WW 3200 - 3700K)", 444 17 : "Standard light A", 445 18 : "Standard light B", 446 19 : "Standard light C", 447 20 : "D55", 448 21 : "D65", 449 22 : "D75", 450 23 : "D50", 451 24 : "ISO studio tungsten", 452 255 : "Other" 453 }, 454 Flash : { 455 0x0000 : "Flash did not fire", 456 0x0001 : "Flash fired", 457 0x0005 : "Strobe return light not detected", 458 0x0007 : "Strobe return light detected", 459 0x0009 : "Flash fired, compulsory flash mode", 460 0x000D : "Flash fired, compulsory flash mode, return light not detected", 461 0x000F : "Flash fired, compulsory flash mode, return light detected", 462 0x0010 : "Flash did not fire, compulsory flash mode", 463 0x0018 : "Flash did not fire, auto mode", 464 0x0019 : "Flash fired, auto mode", 465 0x001D : "Flash fired, auto mode, return light not detected", 466 0x001F : "Flash fired, auto mode, return light detected", 467 0x0020 : "No flash function", 468 0x0041 : "Flash fired, red-eye reduction mode", 469 0x0045 : "Flash fired, red-eye reduction mode, return light not detected", 470 0x0047 : "Flash fired, red-eye reduction mode, return light detected", 471 0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode", 472 0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", 473 0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", 474 0x0059 : "Flash fired, auto mode, red-eye reduction mode", 475 0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode", 476 0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode" 477 }, 478 SensingMethod : { 479 1 : "Not defined", 480 2 : "One-chip color area sensor", 481 3 : "Two-chip color area sensor", 482 4 : "Three-chip color area sensor", 483 5 : "Color sequential area sensor", 484 7 : "Trilinear sensor", 485 8 : "Color sequential linear sensor" 486 }, 487 SceneCaptureType : { 488 0 : "Standard", 489 1 : "Landscape", 490 2 : "Portrait", 491 3 : "Night scene" 492 }, 493 SceneType : { 494 1 : "Directly photographed" 495 }, 496 CustomRendered : { 497 0 : "Normal process", 498 1 : "Custom process" 499 }, 500 WhiteBalance : { 501 0 : "Auto white balance", 502 1 : "Manual white balance" 503 }, 504 GainControl : { 505 0 : "None", 506 1 : "Low gain up", 507 2 : "High gain up", 508 3 : "Low gain down", 509 4 : "High gain down" 510 }, 511 Contrast : { 512 0 : "Normal", 513 1 : "Soft", 514 2 : "Hard" 515 }, 516 Saturation : { 517 0 : "Normal", 518 1 : "Low saturation", 519 2 : "High saturation" 520 }, 521 Sharpness : { 522 0 : "Normal", 523 1 : "Soft", 524 2 : "Hard" 525 }, 526 SubjectDistanceRange : { 527 0 : "Unknown", 528 1 : "Macro", 529 2 : "Close view", 530 3 : "Distant view" 531 }, 532 FileSource : { 533 3 : "DSC" 534 }, 535 536 Components : { 537 0 : "", 538 1 : "Y", 539 2 : "Cb", 540 3 : "Cr", 541 4 : "R", 542 5 : "G", 543 6 : "B" 544 } 545} 546 547function addEvent(oElement, strEvent, fncHandler) 548{ 549 if (oElement.addEventListener) { 550 oElement.addEventListener(strEvent, fncHandler, false); 551 } else if (oElement.attachEvent) { 552 oElement.attachEvent("on" + strEvent, fncHandler); 553 } 554} 555 556 557function imageHasData(oImg) 558{ 559 return !!(oImg.exifdata); 560} 561 562function getImageData(oImg, fncCallback) 563{ 564 BinaryAjax( 565 oImg.src, 566 function(oHTTP) { 567 var oEXIF = findEXIFinJPEG(oHTTP.binaryResponse); 568 oImg.exifdata = oEXIF; 569 if (fncCallback) fncCallback(); 570 } 571 ) 572} 573 574function findEXIFinJPEG(oFile) { 575 var aMarkers = []; 576 577 if (oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) { 578 return false; // not a valid jpeg 579 } 580 581 var iOffset = 2; 582 var iLength = oFile.getLength(); 583 584 while (iOffset < iLength) { 585 if (oFile.getByteAt(iOffset) != 0xFF) { 586 if (bDebug) console.log("Not a valid marker at offset " + iOffset + ", found: " + oFile.getByteAt(iOffset)); 587 return false; // not a valid marker, something is wrong 588 } 589 590 var iMarker = oFile.getByteAt(iOffset+1); 591 592 // we could implement handling for other markers here, 593 // but we're only looking for 0xFFE1 for EXIF data 594 595 if (iMarker == 22400) { 596 if (bDebug) console.log("Found 0xFFE1 marker"); 597 return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2); 598 //iOffset += 2 + oFile.getShortAt(iOffset+2, true); 599 600 } else if (iMarker == 225) { 601 // 0xE1 = Application-specific 1 (for EXIF) 602 if (bDebug) console.log("Found 0xFFE1 marker"); 603 return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2); 604 605 } else { 606 iOffset += 2 + oFile.getShortAt(iOffset+2, true); 607 } 608 609 } 610 611} 612 613 614function readTags(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd) 615{ 616 var iEntries = oFile.getShortAt(iDirStart, bBigEnd); 617 var oTags = {}; 618 for (var i=0;i<iEntries;i++) { 619 var iEntryOffset = iDirStart + i*12 + 2; 620 var strTag = oStrings[oFile.getShortAt(iEntryOffset, bBigEnd)]; 621 if (!strTag && bDebug) console.log("Unknown tag: " + oFile.getShortAt(iEntryOffset, bBigEnd)); 622 oTags[strTag] = readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd); 623 } 624 return oTags; 625} 626 627 628function readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd) 629{ 630 var iType = oFile.getShortAt(iEntryOffset+2, bBigEnd); 631 var iNumValues = oFile.getLongAt(iEntryOffset+4, bBigEnd); 632 var iValueOffset = oFile.getLongAt(iEntryOffset+8, bBigEnd) + iTIFFStart; 633 634 switch (iType) { 635 case 1: // byte, 8-bit unsigned int 636 case 7: // undefined, 8-bit byte, value depending on field 637 if (iNumValues == 1) { 638 return oFile.getByteAt(iEntryOffset + 8, bBigEnd); 639 } else { 640 var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8); 641 var aVals = []; 642 for (var n=0;n<iNumValues;n++) { 643 aVals[n] = oFile.getByteAt(iValOffset + n); 644 } 645 return aVals; 646 } 647 break; 648 649 case 2: // ascii, 8-bit byte 650 var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8); 651 return oFile.getStringAt(iStringOffset, iNumValues-1); 652 break; 653 654 case 3: // short, 16 bit int 655 if (iNumValues == 1) { 656 return oFile.getShortAt(iEntryOffset + 8, bBigEnd); 657 } else { 658 var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8); 659 var aVals = []; 660 for (var n=0;n<iNumValues;n++) { 661 aVals[n] = oFile.getShortAt(iValOffset + 2*n, bBigEnd); 662 } 663 return aVals; 664 } 665 break; 666 667 case 4: // long, 32 bit int 668 if (iNumValues == 1) { 669 return oFile.getLongAt(iEntryOffset + 8, bBigEnd); 670 } else { 671 var aVals = []; 672 for (var n=0;n<iNumValues;n++) { 673 aVals[n] = oFile.getLongAt(iValueOffset + 4*n, bBigEnd); 674 } 675 return aVals; 676 } 677 break; 678 case 5: // rational = two long values, first is numerator, second is denominator 679 if (iNumValues == 1) { 680 return oFile.getLongAt(iValueOffset, bBigEnd) / oFile.getLongAt(iValueOffset+4, bBigEnd); 681 } else { 682 var aVals = []; 683 for (var n=0;n<iNumValues;n++) { 684 aVals[n] = oFile.getLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getLongAt(iValueOffset+4 + 8*n, bBigEnd); 685 } 686 return aVals; 687 } 688 break; 689 case 9: // slong, 32 bit signed int 690 if (iNumValues == 1) { 691 return oFile.getSLongAt(iEntryOffset + 8, bBigEnd); 692 } else { 693 var aVals = []; 694 for (var n=0;n<iNumValues;n++) { 695 aVals[n] = oFile.getSLongAt(iValueOffset + 4*n, bBigEnd); 696 } 697 return aVals; 698 } 699 break; 700 case 10: // signed rational, two slongs, first is numerator, second is denominator 701 if (iNumValues == 1) { 702 return oFile.getSLongAt(iValueOffset, bBigEnd) / oFile.getSLongAt(iValueOffset+4, bBigEnd); 703 } else { 704 var aVals = []; 705 for (var n=0;n<iNumValues;n++) { 706 aVals[n] = oFile.getSLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getSLongAt(iValueOffset+4 + 8*n, bBigEnd); 707 } 708 return aVals; 709 } 710 break; 711 } 712} 713 714 715function readEXIFData(oFile, iStart, iLength) 716{ 717 if (oFile.getStringAt(iStart, 4) != "Exif") { 718 if (bDebug) console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4)); 719 return false; 720 } 721 722 var bBigEnd; 723 724 var iTIFFOffset = iStart + 6; 725 726 // test for TIFF validity and endianness 727 if (oFile.getShortAt(iTIFFOffset) == 0x4949) { 728 bBigEnd = false; 729 } else if (oFile.getShortAt(iTIFFOffset) == 0x4D4D) { 730 bBigEnd = true; 731 } else { 732 if (bDebug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); 733 return false; 734 } 735 736 if (oFile.getShortAt(iTIFFOffset+2, bBigEnd) != 0x002A) { 737 if (bDebug) console.log("Not valid TIFF data! (no 0x002A)"); 738 return false; 739 } 740 741 if (oFile.getLongAt(iTIFFOffset+4, bBigEnd) != 0x00000008) { 742 if (bDebug) console.log("Not valid TIFF data! (First offset not 8)", oFile.getShortAt(iTIFFOffset+4, bBigEnd)); 743 return false; 744 } 745 746 var oTags = readTags(oFile, iTIFFOffset, iTIFFOffset+8, EXIF.TiffTags, bBigEnd); 747 748 if (oTags.ExifIFDPointer) { 749 var oEXIFTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd); 750 for (var strTag in oEXIFTags) { 751 switch (strTag) { 752 case "LightSource" : 753 case "Flash" : 754 case "MeteringMode" : 755 case "ExposureProgram" : 756 case "SensingMethod" : 757 case "SceneCaptureType" : 758 case "SceneType" : 759 case "CustomRendered" : 760 case "WhiteBalance" : 761 case "GainControl" : 762 case "Contrast" : 763 case "Saturation" : 764 case "Sharpness" : 765 case "SubjectDistanceRange" : 766 case "FileSource" : 767 oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]]; 768 break; 769 770 case "ExifVersion" : 771 case "FlashpixVersion" : 772 oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]); 773 break; 774 775 case "ComponentsConfiguration" : 776 oEXIFTags[strTag] = 777 EXIF.StringValues.Components[oEXIFTags[strTag][0]] 778 + EXIF.StringValues.Components[oEXIFTags[strTag][1]] 779 + EXIF.StringValues.Components[oEXIFTags[strTag][2]] 780 + EXIF.StringValues.Components[oEXIFTags[strTag][3]]; 781 break; 782 } 783 oTags[strTag] = oEXIFTags[strTag]; 784 } 785 } 786 787 if (oTags.GPSInfoIFDPointer) { 788 var oGPSTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd); 789 for (var strTag in oGPSTags) { 790 switch (strTag) { 791 case "GPSVersionID" : 792 oGPSTags[strTag] = oGPSTags[strTag][0] 793 + "." + oGPSTags[strTag][1] 794 + "." + oGPSTags[strTag][2] 795 + "." + oGPSTags[strTag][3]; 796 break; 797 } 798 oTags[strTag] = oGPSTags[strTag]; 799 } 800 } 801 802 return oTags; 803} 804 805 806EXIF.getData = function(oImg, fncCallback) 807{ 808 if (!oImg.complete){ 809 if (fncCallback) fncCallback(); 810 return false; 811 } 812 813 getImageData(oImg, fncCallback); 814 /* 815 if (!imageHasData(oImg)) { 816 getImageData(oImg, fncCallback); 817 } else { 818 if (fncCallback) fncCallback(); 819 } 820 */ 821 return true; 822} 823 824EXIF.getTag = function(oImg, strTag) 825{ 826 if (!imageHasData(oImg)) return; 827 return oImg.exifdata[strTag]; 828} 829 830EXIF.getAllTags = function(oImg) 831{ 832 if (!imageHasData(oImg)) return {}; 833 var oData = oImg.exifdata; 834 var oAllTags = {}; 835 for (var a in oData) { 836 if (oData.hasOwnProperty(a)) { 837 oAllTags[a] = oData[a]; 838 } 839 } 840 return oAllTags; 841} 842 843EXIF.readFromBinaryFile = function(oFile) { 844 return findEXIFinJPEG(oFile); 845} 846 847// load data for images manually 848jQuery.fn.exifLoad = function(fncCallback) { 849 var oImg = this.get(0); 850 851 EXIF.getData(oImg, function(){ 852 if(fncCallback) fncCallback(oImg.exifdata); 853 }); 854} 855 856})(); 857 858})(); 859 860