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