- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / examples / extensions / imageinfo / imageinfo / exif.js
1 /*
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]
5  */
6
7
8 var EXIF = {};
9
10 (function() {
11
12 var bDebug = false;
13
14 EXIF.Tags = {
15
16   // version tags
17   0x9000 : "ExifVersion",                       // EXIF version
18   0xA000 : "FlashpixVersion",           // Flashpix format version
19
20   // colorspace tags
21   0xA001 : "ColorSpace",                        // Color space information tag
22
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
28
29   // user information
30   0x927C : "MakerNote",                 // Any desired information written by the manufacturer
31   0x9286 : "UserComment",                       // Comments by user
32
33   // related file
34   0xA004 : "RelatedSoundFile",          // Name of related sound file
35
36   // date and time
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
42
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
84
85   // other tags
86   0xA005 : "InteroperabilityIFDPointer",
87   0xA420 : "ImageUniqueID"              // Identifier assigned uniquely to each image
88 };
89
90 EXIF.TiffTags = {
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",
117   0x0132 : "DateTime",
118   0x010E : "ImageDescription",
119   0x010F : "Make",
120   0x0110 : "Model",
121   0x0131 : "Software",
122   0x013B : "Artist",
123   0x8298 : "Copyright"
124 }
125
126 EXIF.GPSTags = {
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",
138   0x000B : "GPSDOP",
139   0x000C : "GPSSpeedRef",
140   0x000D : "GPSSpeed",
141   0x000E : "GPSTrackRef",
142   0x000F : "GPSTrack",
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"
158 }
159
160 EXIF.StringValues = {
161   ExposureProgram : {
162     0 : "Not defined",
163     1 : "Manual",
164     2 : "Normal program",
165     3 : "Aperture priority",
166     4 : "Shutter priority",
167     5 : "Creative program",
168     6 : "Action program",
169     7 : "Portrait mode",
170     8 : "Landscape mode"
171   },
172   MeteringMode : {
173     0 : "Unknown",
174     1 : "Average",
175     2 : "CenterWeightedAverage",
176     3 : "Spot",
177     4 : "MultiSpot",
178     5 : "Pattern",
179     6 : "Partial",
180     255 : "Other"
181   },
182   LightSource : {
183     0 : "Unknown",
184     1 : "Daylight",
185     2 : "Fluorescent",
186     3 : "Tungsten (incandescent light)",
187     4 : "Flash",
188     9 : "Fine weather",
189     10 : "Cloudy weather",
190     11 : "Shade",
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",
198     20 : "D55",
199     21 : "D65",
200     22 : "D75",
201     23 : "D50",
202     24 : "ISO studio tungsten",
203     255 : "Other"
204   },
205   Flash : {
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"
228   },
229   SensingMethod : {
230     1 : "Not defined",
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"
237   },
238   SceneCaptureType : {
239     0 : "Standard",
240     1 : "Landscape",
241     2 : "Portrait",
242     3 : "Night scene"
243   },
244   SceneType : {
245     1 : "Directly photographed"
246   },
247   CustomRendered : {
248     0 : "Normal process",
249     1 : "Custom process"
250   },
251   WhiteBalance : {
252     0 : "Auto white balance",
253     1 : "Manual white balance"
254   },
255   GainControl : {
256     0 : "None",
257     1 : "Low gain up",
258     2 : "High gain up",
259     3 : "Low gain down",
260     4 : "High gain down"
261   },
262   Contrast : {
263     0 : "Normal",
264     1 : "Soft",
265     2 : "Hard"
266   },
267   Saturation : {
268     0 : "Normal",
269     1 : "Low saturation",
270     2 : "High saturation"
271   },
272   Sharpness : {
273     0 : "Normal",
274     1 : "Soft",
275     2 : "Hard"
276   },
277   SubjectDistanceRange : {
278     0 : "Unknown",
279     1 : "Macro",
280     2 : "Close view",
281     3 : "Distant view"
282   },
283   FileSource : {
284     3 : "DSC"
285   },
286
287   Components : {
288     0 : "",
289     1 : "Y",
290     2 : "Cb",
291     3 : "Cr",
292     4 : "R",
293     5 : "G",
294     6 : "B"
295   }
296 }
297
298 function addEvent(oElement, strEvent, fncHandler)
299 {
300   if (oElement.addEventListener) {
301     oElement.addEventListener(strEvent, fncHandler, false);
302   } else if (oElement.attachEvent) {
303     oElement.attachEvent("on" + strEvent, fncHandler);
304   }
305 }
306
307
308 function imageHasData(oImg)
309 {
310   return !!(oImg.exifdata);
311 }
312
313 function getImageData(oImg, fncCallback)
314 {
315   BinaryAjax(
316     oImg.src,
317     function(oHTTP) {
318       var oEXIF = findEXIFinJPEG(oHTTP.binaryResponse);
319       oImg.exifdata = oEXIF || {};
320       if (fncCallback) fncCallback();
321     }
322   )
323 }
324
325 function findEXIFinJPEG(oFile) {
326   var aMarkers = [];
327
328   if (oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) {
329     return false; // not a valid jpeg
330   }
331
332   var iOffset = 2;
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
338     }
339
340     var iMarker = oFile.getByteAt(iOffset+1);
341
342     // we could implement handling for other markers here,
343     // but we're only looking for 0xFFE1 for EXIF data
344
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);
349
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);
354
355     } else {
356       iOffset += 2 + oFile.getShortAt(iOffset+2, true);
357     }
358
359   }
360
361 }
362
363
364 function readTags(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd)
365 {
366   var iEntries = oFile.getShortAt(iDirStart, bBigEnd);
367   var oTags = {};
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);
373   }
374   return oTags;
375 }
376
377
378 function readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd)
379 {
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;
383
384   switch (iType) {
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);
389       } else {
390         var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
391         var aVals = [];
392         for (var n=0;n<iNumValues;n++) {
393           aVals[n] = oFile.getByteAt(iValOffset + n);
394         }
395         return aVals;
396       }
397       break;
398
399     case 2: // ascii, 8-bit byte
400       var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
401       return oFile.getStringAt(iStringOffset, iNumValues-1);
402       break;
403
404     case 3: // short, 16 bit int
405       if (iNumValues == 1) {
406         return oFile.getShortAt(iEntryOffset + 8, bBigEnd);
407       } else {
408         var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8);
409         var aVals = [];
410         for (var n=0;n<iNumValues;n++) {
411           aVals[n] = oFile.getShortAt(iValOffset + 2*n, bBigEnd);
412         }
413         return aVals;
414       }
415       break;
416
417     case 4: // long, 32 bit int
418       if (iNumValues == 1) {
419         return oFile.getLongAt(iEntryOffset + 8, bBigEnd);
420       } else {
421         var aVals = [];
422         for (var n=0;n<iNumValues;n++) {
423           aVals[n] = oFile.getLongAt(iValueOffset + 4*n, bBigEnd);
424         }
425         return aVals;
426       }
427       break;
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);
431       } else {
432         var aVals = [];
433         for (var n=0;n<iNumValues;n++) {
434           aVals[n] = oFile.getLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getLongAt(iValueOffset+4 + 8*n, bBigEnd);
435         }
436         return aVals;
437       }
438       break;
439     case 9: // slong, 32 bit signed int
440       if (iNumValues == 1) {
441         return oFile.getSLongAt(iEntryOffset + 8, bBigEnd);
442       } else {
443         var aVals = [];
444         for (var n=0;n<iNumValues;n++) {
445           aVals[n] = oFile.getSLongAt(iValueOffset + 4*n, bBigEnd);
446         }
447         return aVals;
448       }
449       break;
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);
453       } else {
454         var aVals = [];
455         for (var n=0;n<iNumValues;n++) {
456           aVals[n] = oFile.getSLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getSLongAt(iValueOffset+4 + 8*n, bBigEnd);
457         }
458         return aVals;
459       }
460       break;
461   }
462 }
463
464
465 function readEXIFData(oFile, iStart, iLength)
466 {
467   if (oFile.getStringAt(iStart, 4) != "Exif") {
468     if (bDebug) console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4));
469     return false;
470   }
471
472   var bBigEnd;
473
474   var iTIFFOffset = iStart + 6;
475
476   // test for TIFF validity and endianness
477   if (oFile.getShortAt(iTIFFOffset) == 0x4949) {
478     bBigEnd = false;
479   } else if (oFile.getShortAt(iTIFFOffset) == 0x4D4D) {
480     bBigEnd = true;
481   } else {
482     if (bDebug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
483     return false;
484   }
485
486   if (oFile.getShortAt(iTIFFOffset+2, bBigEnd) != 0x002A) {
487     if (bDebug) console.log("Not valid TIFF data! (no 0x002A)");
488     return false;
489   }
490
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));
493     return false;
494   }
495
496   var oTags = readTags(oFile, iTIFFOffset, iTIFFOffset+8, EXIF.TiffTags, bBigEnd);
497
498   if (oTags.ExifIFDPointer) {
499     var oEXIFTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd);
500     for (var strTag in oEXIFTags) {
501       switch (strTag) {
502         case "LightSource" :
503         case "Flash" :
504         case "MeteringMode" :
505         case "ExposureProgram" :
506         case "SensingMethod" :
507         case "SceneCaptureType" :
508         case "SceneType" :
509         case "CustomRendered" :
510         case "WhiteBalance" :
511         case "GainControl" :
512         case "Contrast" :
513         case "Saturation" :
514         case "Sharpness" :
515         case "SubjectDistanceRange" :
516         case "FileSource" :
517           oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]];
518           break;
519
520         case "ExifVersion" :
521         case "FlashpixVersion" :
522           oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]);
523           break;
524
525         case "ComponentsConfiguration" :
526           oEXIFTags[strTag] =
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]];
531           break;
532       }
533       oTags[strTag] = oEXIFTags[strTag];
534     }
535   }
536
537   if (oTags.GPSInfoIFDPointer) {
538     var oGPSTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd);
539     for (var strTag in oGPSTags) {
540       switch (strTag) {
541         case "GPSVersionID" :
542           oGPSTags[strTag] = oGPSTags[strTag][0]
543             + "." + oGPSTags[strTag][1]
544             + "." + oGPSTags[strTag][2]
545             + "." + oGPSTags[strTag][3];
546           break;
547       }
548       oTags[strTag] = oGPSTags[strTag];
549     }
550   }
551
552   return oTags;
553 }
554
555
556 EXIF.getData = function(oImg, fncCallback)
557 {
558   if (!oImg.complete) return false;
559   if (!imageHasData(oImg)) {
560     getImageData(oImg, fncCallback);
561   } else {
562     if (fncCallback) fncCallback();
563   }
564   return true;
565 }
566
567 EXIF.getTag = function(oImg, strTag)
568 {
569   if (!imageHasData(oImg)) return;
570   return oImg.exifdata[strTag];
571 }
572
573 EXIF.pretty = function(oImg)
574 {
575   if (!imageHasData(oImg)) return "";
576   var oData = oImg.exifdata;
577   var strPretty = "";
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";
582       } else {
583         strPretty += a + " : " + oData[a] + "\r\n";
584       }
585     }
586   }
587   return strPretty;
588 }
589
590 EXIF.readFromBinaryFile = function(oFile) {
591   return findEXIFinJPEG(oFile);
592 }
593
594 function loadAllImages()
595 {
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",
601           function() {
602             EXIF.getData(this);
603           }
604         );
605       } else {
606         EXIF.getData(aImages[i]);
607       }
608     }
609   }
610 }
611
612 addEvent(window, "load", loadAllImages);
613
614 })();
615