2 * Javascript EXIF Reader 0.1.2
3 * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
4 * MIT License [http://www.opensource.org/licenses/mit-license.php]
17 0x9000 : "ExifVersion", // EXIF version
18 0xA000 : "FlashpixVersion", // Flashpix format version
21 0xA001 : "ColorSpace", // Color space information tag
23 // image configuration
24 0xA002 : "PixelXDimension", // Valid width of meaningful image
25 0xA003 : "PixelYDimension", // Valid height of meaningful image
26 0x9101 : "ComponentsConfiguration", // Information about channels
27 0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel
30 0x927C : "MakerNote", // Any desired information written by the manufacturer
31 0x9286 : "UserComment", // Comments by user
34 0xA004 : "RelatedSoundFile", // Name of related sound file
37 0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
38 0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
39 0x9290 : "SubsecTime", // Fractions of seconds for DateTime
40 0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
41 0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
43 // picture-taking conditions
44 0x829A : "ExposureTime", // Exposure time (in seconds)
45 0x829D : "FNumber", // F number
46 0x8822 : "ExposureProgram", // Exposure program
47 0x8824 : "SpectralSensitivity", // Spectral sensitivity
48 0x8827 : "ISOSpeedRatings", // ISO speed rating
49 0x8828 : "OECF", // Optoelectric conversion factor
50 0x9201 : "ShutterSpeedValue", // Shutter speed
51 0x9202 : "ApertureValue", // Lens aperture
52 0x9203 : "BrightnessValue", // Value of brightness
53 0x9204 : "ExposureBias", // Exposure bias
54 0x9205 : "MaxApertureValue", // Smallest F number of lens
55 0x9206 : "SubjectDistance", // Distance to subject in meters
56 0x9207 : "MeteringMode", // Metering mode
57 0x9208 : "LightSource", // Kind of light source
58 0x9209 : "Flash", // Flash status
59 0x9214 : "SubjectArea", // Location and area of main subject
60 0x920A : "FocalLength", // Focal length of the lens in mm
61 0xA20B : "FlashEnergy", // Strobe energy in BCPS
62 0xA20C : "SpatialFrequencyResponse", //
63 0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
64 0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
65 0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
66 0xA214 : "SubjectLocation", // Location of subject in image
67 0xA215 : "ExposureIndex", // Exposure index selected on camera
68 0xA217 : "SensingMethod", // Image sensor type
69 0xA300 : "FileSource", // Image source (3 == DSC)
70 0xA301 : "SceneType", // Scene type (1 == directly photographed)
71 0xA302 : "CFAPattern", // Color filter array geometric pattern
72 0xA401 : "CustomRendered", // Special processing
73 0xA402 : "ExposureMode", // Exposure mode
74 0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
75 0xA404 : "DigitalZoomRation", // Digital zoom ratio
76 0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
77 0xA406 : "SceneCaptureType", // Type of scene
78 0xA407 : "GainControl", // Degree of overall image gain adjustment
79 0xA408 : "Contrast", // Direction of contrast processing applied by camera
80 0xA409 : "Saturation", // Direction of saturation processing applied by camera
81 0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
82 0xA40B : "DeviceSettingDescription", //
83 0xA40C : "SubjectDistanceRange", // Distance to subject
86 0xA005 : "InteroperabilityIFDPointer",
87 0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image
91 0x0100 : "ImageWidth",
92 0x0101 : "ImageHeight",
93 0x8769 : "ExifIFDPointer",
94 0x8825 : "GPSInfoIFDPointer",
95 0xA005 : "InteroperabilityIFDPointer",
96 0x0102 : "BitsPerSample",
97 0x0103 : "Compression",
98 0x0106 : "PhotometricInterpretation",
99 0x0112 : "Orientation",
100 0x0115 : "SamplesPerPixel",
101 0x011C : "PlanarConfiguration",
102 0x0212 : "YCbCrSubSampling",
103 0x0213 : "YCbCrPositioning",
104 0x011A : "XResolution",
105 0x011B : "YResolution",
106 0x0128 : "ResolutionUnit",
107 0x0111 : "StripOffsets",
108 0x0116 : "RowsPerStrip",
109 0x0117 : "StripByteCounts",
110 0x0201 : "JPEGInterchangeFormat",
111 0x0202 : "JPEGInterchangeFormatLength",
112 0x012D : "TransferFunction",
113 0x013E : "WhitePoint",
114 0x013F : "PrimaryChromaticities",
115 0x0211 : "YCbCrCoefficients",
116 0x0214 : "ReferenceBlackWhite",
118 0x010E : "ImageDescription",
127 0x0000 : "GPSVersionID",
128 0x0001 : "GPSLatitudeRef",
129 0x0002 : "GPSLatitude",
130 0x0003 : "GPSLongitudeRef",
131 0x0004 : "GPSLongitude",
132 0x0005 : "GPSAltitudeRef",
133 0x0006 : "GPSAltitude",
134 0x0007 : "GPSTimeStamp",
135 0x0008 : "GPSSatellites",
136 0x0009 : "GPSStatus",
137 0x000A : "GPSMeasureMode",
139 0x000C : "GPSSpeedRef",
141 0x000E : "GPSTrackRef",
143 0x0010 : "GPSImgDirectionRef",
144 0x0011 : "GPSImgDirection",
145 0x0012 : "GPSMapDatum",
146 0x0013 : "GPSDestLatitudeRef",
147 0x0014 : "GPSDestLatitude",
148 0x0015 : "GPSDestLongitudeRef",
149 0x0016 : "GPSDestLongitude",
150 0x0017 : "GPSDestBearingRef",
151 0x0018 : "GPSDestBearing",
152 0x0019 : "GPSDestDistanceRef",
153 0x001A : "GPSDestDistance",
154 0x001B : "GPSProcessingMethod",
155 0x001C : "GPSAreaInformation",
156 0x001D : "GPSDateStamp",
157 0x001E : "GPSDifferential"
160 EXIF.StringValues = {
164 2 : "Normal program",
165 3 : "Aperture priority",
166 4 : "Shutter priority",
167 5 : "Creative program",
168 6 : "Action program",
175 2 : "CenterWeightedAverage",
186 3 : "Tungsten (incandescent light)",
189 10 : "Cloudy weather",
191 12 : "Daylight fluorescent (D 5700 - 7100K)",
192 13 : "Day white fluorescent (N 4600 - 5400K)",
193 14 : "Cool white fluorescent (W 3900 - 4500K)",
194 15 : "White fluorescent (WW 3200 - 3700K)",
195 17 : "Standard light A",
196 18 : "Standard light B",
197 19 : "Standard light C",
202 24 : "ISO studio tungsten",
206 0x0000 : "Flash did not fire",
207 0x0001 : "Flash fired",
208 0x0005 : "Strobe return light not detected",
209 0x0007 : "Strobe return light detected",
210 0x0009 : "Flash fired, compulsory flash mode",
211 0x000D : "Flash fired, compulsory flash mode, return light not detected",
212 0x000F : "Flash fired, compulsory flash mode, return light detected",
213 0x0010 : "Flash did not fire, compulsory flash mode",
214 0x0018 : "Flash did not fire, auto mode",
215 0x0019 : "Flash fired, auto mode",
216 0x001D : "Flash fired, auto mode, return light not detected",
217 0x001F : "Flash fired, auto mode, return light detected",
218 0x0020 : "No flash function",
219 0x0041 : "Flash fired, red-eye reduction mode",
220 0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
221 0x0047 : "Flash fired, red-eye reduction mode, return light detected",
222 0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
223 0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
224 0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
225 0x0059 : "Flash fired, auto mode, red-eye reduction mode",
226 0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
227 0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
231 2 : "One-chip color area sensor",
232 3 : "Two-chip color area sensor",
233 4 : "Three-chip color area sensor",
234 5 : "Color sequential area sensor",
235 7 : "Trilinear sensor",
236 8 : "Color sequential linear sensor"
245 1 : "Directly photographed"
248 0 : "Normal process",
252 0 : "Auto white balance",
253 1 : "Manual white balance"
269 1 : "Low saturation",
270 2 : "High saturation"
277 SubjectDistanceRange : {
298 function addEvent(oElement, strEvent, fncHandler)
300 if (oElement.addEventListener) {
301 oElement.addEventListener(strEvent, fncHandler, false);
302 } else if (oElement.attachEvent) {
303 oElement.attachEvent("on" + strEvent, fncHandler);
308 function imageHasData(oImg)
310 return !!(oImg.exifdata);
313 function getImageData(oImg, fncCallback)
318 var oEXIF = findEXIFinJPEG(oHTTP.binaryResponse);
319 oImg.exifdata = oEXIF || {};
320 if (fncCallback) fncCallback();
325 function findEXIFinJPEG(oFile) {
328 if (oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) {
329 return false; // not a valid jpeg
333 var iLength = oFile.getLength();
334 while (iOffset < iLength) {
335 if (oFile.getByteAt(iOffset) != 0xFF) {
336 if (bDebug) console.log("Not a valid marker at offset " + iOffset + ", found: " + oFile.getByteAt(iOffset));
337 return false; // not a valid marker, something is wrong
340 var iMarker = oFile.getByteAt(iOffset+1);
342 // we could implement handling for other markers here,
343 // but we're only looking for 0xFFE1 for EXIF data
345 if (iMarker == 22400) {
346 if (bDebug) console.log("Found 0xFFE1 marker");
347 return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2);
348 iOffset += 2 + oFile.getShortAt(iOffset+2, true);
350 } else if (iMarker == 225) {
351 // 0xE1 = Application-specific 1 (for EXIF)
352 if (bDebug) console.log("Found 0xFFE1 marker");
353 return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2);
356 iOffset += 2 + oFile.getShortAt(iOffset+2, true);
364 function readTags(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd)
366 var iEntries = oFile.getShortAt(iDirStart, bBigEnd);
368 for (var i=0;i<iEntries;i++) {
369 var iEntryOffset = iDirStart + i*12 + 2;
370 var strTag = oStrings[oFile.getShortAt(iEntryOffset, bBigEnd)];
371 if (!strTag && bDebug) console.log("Unknown tag: " + oFile.getShortAt(iEntryOffset, bBigEnd));
372 oTags[strTag] = readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd);
378 function readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd)
380 var iType = oFile.getShortAt(iEntryOffset+2, bBigEnd);
381 var iNumValues = oFile.getLongAt(iEntryOffset+4, bBigEnd);
382 var iValueOffset = oFile.getLongAt(iEntryOffset+8, bBigEnd) + iTIFFStart;
385 case 1: // byte, 8-bit unsigned int
386 case 7: // undefined, 8-bit byte, value depending on field
387 if (iNumValues == 1) {
388 return oFile.getByteAt(iEntryOffset + 8, bBigEnd);
390 var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
392 for (var n=0;n<iNumValues;n++) {
393 aVals[n] = oFile.getByteAt(iValOffset + n);
399 case 2: // ascii, 8-bit byte
400 var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
401 return oFile.getStringAt(iStringOffset, iNumValues-1);
404 case 3: // short, 16 bit int
405 if (iNumValues == 1) {
406 return oFile.getShortAt(iEntryOffset + 8, bBigEnd);
408 var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8);
410 for (var n=0;n<iNumValues;n++) {
411 aVals[n] = oFile.getShortAt(iValOffset + 2*n, bBigEnd);
417 case 4: // long, 32 bit int
418 if (iNumValues == 1) {
419 return oFile.getLongAt(iEntryOffset + 8, bBigEnd);
422 for (var n=0;n<iNumValues;n++) {
423 aVals[n] = oFile.getLongAt(iValueOffset + 4*n, bBigEnd);
428 case 5: // rational = two long values, first is numerator, second is denominator
429 if (iNumValues == 1) {
430 return oFile.getLongAt(iValueOffset, bBigEnd) / oFile.getLongAt(iValueOffset+4, bBigEnd);
433 for (var n=0;n<iNumValues;n++) {
434 aVals[n] = oFile.getLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getLongAt(iValueOffset+4 + 8*n, bBigEnd);
439 case 9: // slong, 32 bit signed int
440 if (iNumValues == 1) {
441 return oFile.getSLongAt(iEntryOffset + 8, bBigEnd);
444 for (var n=0;n<iNumValues;n++) {
445 aVals[n] = oFile.getSLongAt(iValueOffset + 4*n, bBigEnd);
450 case 10: // signed rational, two slongs, first is numerator, second is denominator
451 if (iNumValues == 1) {
452 return oFile.getSLongAt(iValueOffset, bBigEnd) / oFile.getSLongAt(iValueOffset+4, bBigEnd);
455 for (var n=0;n<iNumValues;n++) {
456 aVals[n] = oFile.getSLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getSLongAt(iValueOffset+4 + 8*n, bBigEnd);
465 function readEXIFData(oFile, iStart, iLength)
467 if (oFile.getStringAt(iStart, 4) != "Exif") {
468 if (bDebug) console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4));
474 var iTIFFOffset = iStart + 6;
476 // test for TIFF validity and endianness
477 if (oFile.getShortAt(iTIFFOffset) == 0x4949) {
479 } else if (oFile.getShortAt(iTIFFOffset) == 0x4D4D) {
482 if (bDebug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
486 if (oFile.getShortAt(iTIFFOffset+2, bBigEnd) != 0x002A) {
487 if (bDebug) console.log("Not valid TIFF data! (no 0x002A)");
491 if (oFile.getLongAt(iTIFFOffset+4, bBigEnd) != 0x00000008) {
492 if (bDebug) console.log("Not valid TIFF data! (First offset not 8)", oFile.getShortAt(iTIFFOffset+4, bBigEnd));
496 var oTags = readTags(oFile, iTIFFOffset, iTIFFOffset+8, EXIF.TiffTags, bBigEnd);
498 if (oTags.ExifIFDPointer) {
499 var oEXIFTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd);
500 for (var strTag in oEXIFTags) {
504 case "MeteringMode" :
505 case "ExposureProgram" :
506 case "SensingMethod" :
507 case "SceneCaptureType" :
509 case "CustomRendered" :
510 case "WhiteBalance" :
515 case "SubjectDistanceRange" :
517 oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]];
521 case "FlashpixVersion" :
522 oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]);
525 case "ComponentsConfiguration" :
527 EXIF.StringValues.Components[oEXIFTags[strTag][0]]
528 + EXIF.StringValues.Components[oEXIFTags[strTag][1]]
529 + EXIF.StringValues.Components[oEXIFTags[strTag][2]]
530 + EXIF.StringValues.Components[oEXIFTags[strTag][3]];
533 oTags[strTag] = oEXIFTags[strTag];
537 if (oTags.GPSInfoIFDPointer) {
538 var oGPSTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd);
539 for (var strTag in oGPSTags) {
541 case "GPSVersionID" :
542 oGPSTags[strTag] = oGPSTags[strTag][0]
543 + "." + oGPSTags[strTag][1]
544 + "." + oGPSTags[strTag][2]
545 + "." + oGPSTags[strTag][3];
548 oTags[strTag] = oGPSTags[strTag];
556 EXIF.getData = function(oImg, fncCallback)
558 if (!oImg.complete) return false;
559 if (!imageHasData(oImg)) {
560 getImageData(oImg, fncCallback);
562 if (fncCallback) fncCallback();
567 EXIF.getTag = function(oImg, strTag)
569 if (!imageHasData(oImg)) return;
570 return oImg.exifdata[strTag];
573 EXIF.pretty = function(oImg)
575 if (!imageHasData(oImg)) return "";
576 var oData = oImg.exifdata;
578 for (var a in oData) {
579 if (oData.hasOwnProperty(a)) {
580 if (typeof oData[a] == "object") {
581 strPretty += a + " : [" + oData[a].length + " values]\r\n";
583 strPretty += a + " : " + oData[a] + "\r\n";
590 EXIF.readFromBinaryFile = function(oFile) {
591 return findEXIFinJPEG(oFile);
594 function loadAllImages()
596 var aImages = document.getElementsByTagName("img");
597 for (var i=0;i<aImages.length;i++) {
598 if (aImages[i].getAttribute("exif") == "true") {
599 if (!aImages[i].complete) {
600 addEvent(aImages[i], "load",
606 EXIF.getData(aImages[i]);
612 addEvent(window, "load", loadAllImages);