1
0

exif.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. /*
  2. * Javascript EXIF Reader 0.1.6
  3. * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/
  4. * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
  5. */
  6. var EXIF = (function() {
  7. var debug = false;
  8. var ExifTags = {
  9. // version tags
  10. 0x9000: "ExifVersion", // EXIF version
  11. 0xA000: "FlashpixVersion", // Flashpix format version
  12. // colorspace tags
  13. 0xA001: "ColorSpace", // Color space information tag
  14. // image configuration
  15. 0xA002: "PixelXDimension", // Valid width of meaningful image
  16. 0xA003: "PixelYDimension", // Valid height of meaningful image
  17. 0x9101: "ComponentsConfiguration", // Information about channels
  18. 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel
  19. // user information
  20. 0x927C: "MakerNote", // Any desired information written by the manufacturer
  21. 0x9286: "UserComment", // Comments by user
  22. // related file
  23. 0xA004: "RelatedSoundFile", // Name of related sound file
  24. // date and time
  25. 0x9003: "DateTimeOriginal", // Date and time when the original image was generated
  26. 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally
  27. 0x9290: "SubsecTime", // Fractions of seconds for DateTime
  28. 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
  29. 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
  30. // picture-taking conditions
  31. 0x829A: "ExposureTime", // Exposure time (in seconds)
  32. 0x829D: "FNumber", // F number
  33. 0x8822: "ExposureProgram", // Exposure program
  34. 0x8824: "SpectralSensitivity", // Spectral sensitivity
  35. 0x8827: "ISOSpeedRatings", // ISO speed rating
  36. 0x8828: "OECF", // Optoelectric conversion factor
  37. 0x9201: "ShutterSpeedValue", // Shutter speed
  38. 0x9202: "ApertureValue", // Lens aperture
  39. 0x9203: "BrightnessValue", // Value of brightness
  40. 0x9204: "ExposureBias", // Exposure bias
  41. 0x9205: "MaxApertureValue", // Smallest F number of lens
  42. 0x9206: "SubjectDistance", // Distance to subject in meters
  43. 0x9207: "MeteringMode", // Metering mode
  44. 0x9208: "LightSource", // Kind of light source
  45. 0x9209: "Flash", // Flash status
  46. 0x9214: "SubjectArea", // Location and area of main subject
  47. 0x920A: "FocalLength", // Focal length of the lens in mm
  48. 0xA20B: "FlashEnergy", // Strobe energy in BCPS
  49. 0xA20C: "SpatialFrequencyResponse", //
  50. 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
  51. 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
  52. 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
  53. 0xA214: "SubjectLocation", // Location of subject in image
  54. 0xA215: "ExposureIndex", // Exposure index selected on camera
  55. 0xA217: "SensingMethod", // Image sensor type
  56. 0xA300: "FileSource", // Image source (3 == DSC)
  57. 0xA301: "SceneType", // Scene type (1 == directly photographed)
  58. 0xA302: "CFAPattern", // Color filter array geometric pattern
  59. 0xA401: "CustomRendered", // Special processing
  60. 0xA402: "ExposureMode", // Exposure mode
  61. 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual
  62. 0xA404: "DigitalZoomRation", // Digital zoom ratio
  63. 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
  64. 0xA406: "SceneCaptureType", // Type of scene
  65. 0xA407: "GainControl", // Degree of overall image gain adjustment
  66. 0xA408: "Contrast", // Direction of contrast processing applied by camera
  67. 0xA409: "Saturation", // Direction of saturation processing applied by camera
  68. 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera
  69. 0xA40B: "DeviceSettingDescription", //
  70. 0xA40C: "SubjectDistanceRange", // Distance to subject
  71. // other tags
  72. 0xA005: "InteroperabilityIFDPointer",
  73. 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image
  74. };
  75. var TiffTags = {
  76. 0x0100: "ImageWidth",
  77. 0x0101: "ImageHeight",
  78. 0x8769: "ExifIFDPointer",
  79. 0x8825: "GPSInfoIFDPointer",
  80. 0xA005: "InteroperabilityIFDPointer",
  81. 0x0102: "BitsPerSample",
  82. 0x0103: "Compression",
  83. 0x0106: "PhotometricInterpretation",
  84. 0x0112: "Orientation",
  85. 0x0115: "SamplesPerPixel",
  86. 0x011C: "PlanarConfiguration",
  87. 0x0212: "YCbCrSubSampling",
  88. 0x0213: "YCbCrPositioning",
  89. 0x011A: "XResolution",
  90. 0x011B: "YResolution",
  91. 0x0128: "ResolutionUnit",
  92. 0x0111: "StripOffsets",
  93. 0x0116: "RowsPerStrip",
  94. 0x0117: "StripByteCounts",
  95. 0x0201: "JPEGInterchangeFormat",
  96. 0x0202: "JPEGInterchangeFormatLength",
  97. 0x012D: "TransferFunction",
  98. 0x013E: "WhitePoint",
  99. 0x013F: "PrimaryChromaticities",
  100. 0x0211: "YCbCrCoefficients",
  101. 0x0214: "ReferenceBlackWhite",
  102. 0x0132: "DateTime",
  103. 0x010E: "ImageDescription",
  104. 0x010F: "Make",
  105. 0x0110: "Model",
  106. 0x0131: "Software",
  107. 0x013B: "Artist",
  108. 0x8298: "Copyright"
  109. };
  110. var GPSTags = {
  111. 0x0000: "GPSVersionID",
  112. 0x0001: "GPSLatitudeRef",
  113. 0x0002: "GPSLatitude",
  114. 0x0003: "GPSLongitudeRef",
  115. 0x0004: "GPSLongitude",
  116. 0x0005: "GPSAltitudeRef",
  117. 0x0006: "GPSAltitude",
  118. 0x0007: "GPSTimeStamp",
  119. 0x0008: "GPSSatellites",
  120. 0x0009: "GPSStatus",
  121. 0x000A: "GPSMeasureMode",
  122. 0x000B: "GPSDOP",
  123. 0x000C: "GPSSpeedRef",
  124. 0x000D: "GPSSpeed",
  125. 0x000E: "GPSTrackRef",
  126. 0x000F: "GPSTrack",
  127. 0x0010: "GPSImgDirectionRef",
  128. 0x0011: "GPSImgDirection",
  129. 0x0012: "GPSMapDatum",
  130. 0x0013: "GPSDestLatitudeRef",
  131. 0x0014: "GPSDestLatitude",
  132. 0x0015: "GPSDestLongitudeRef",
  133. 0x0016: "GPSDestLongitude",
  134. 0x0017: "GPSDestBearingRef",
  135. 0x0018: "GPSDestBearing",
  136. 0x0019: "GPSDestDistanceRef",
  137. 0x001A: "GPSDestDistance",
  138. 0x001B: "GPSProcessingMethod",
  139. 0x001C: "GPSAreaInformation",
  140. 0x001D: "GPSDateStamp",
  141. 0x001E: "GPSDifferential"
  142. };
  143. var StringValues = {
  144. ExposureProgram: {
  145. 0: "Not defined",
  146. 1: "Manual",
  147. 2: "Normal program",
  148. 3: "Aperture priority",
  149. 4: "Shutter priority",
  150. 5: "Creative program",
  151. 6: "Action program",
  152. 7: "Portrait mode",
  153. 8: "Landscape mode"
  154. },
  155. MeteringMode: {
  156. 0: "Unknown",
  157. 1: "Average",
  158. 2: "CenterWeightedAverage",
  159. 3: "Spot",
  160. 4: "MultiSpot",
  161. 5: "Pattern",
  162. 6: "Partial",
  163. 255: "Other"
  164. },
  165. LightSource: {
  166. 0: "Unknown",
  167. 1: "Daylight",
  168. 2: "Fluorescent",
  169. 3: "Tungsten (incandescent light)",
  170. 4: "Flash",
  171. 9: "Fine weather",
  172. 10: "Cloudy weather",
  173. 11: "Shade",
  174. 12: "Daylight fluorescent (D 5700 - 7100K)",
  175. 13: "Day white fluorescent (N 4600 - 5400K)",
  176. 14: "Cool white fluorescent (W 3900 - 4500K)",
  177. 15: "White fluorescent (WW 3200 - 3700K)",
  178. 17: "Standard light A",
  179. 18: "Standard light B",
  180. 19: "Standard light C",
  181. 20: "D55",
  182. 21: "D65",
  183. 22: "D75",
  184. 23: "D50",
  185. 24: "ISO studio tungsten",
  186. 255: "Other"
  187. },
  188. Flash: {
  189. 0x0000: "Flash did not fire",
  190. 0x0001: "Flash fired",
  191. 0x0005: "Strobe return light not detected",
  192. 0x0007: "Strobe return light detected",
  193. 0x0009: "Flash fired, compulsory flash mode",
  194. 0x000D: "Flash fired, compulsory flash mode, return light not detected",
  195. 0x000F: "Flash fired, compulsory flash mode, return light detected",
  196. 0x0010: "Flash did not fire, compulsory flash mode",
  197. 0x0018: "Flash did not fire, auto mode",
  198. 0x0019: "Flash fired, auto mode",
  199. 0x001D: "Flash fired, auto mode, return light not detected",
  200. 0x001F: "Flash fired, auto mode, return light detected",
  201. 0x0020: "No flash function",
  202. 0x0041: "Flash fired, red-eye reduction mode",
  203. 0x0045: "Flash fired, red-eye reduction mode, return light not detected",
  204. 0x0047: "Flash fired, red-eye reduction mode, return light detected",
  205. 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
  206. 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
  207. 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
  208. 0x0059: "Flash fired, auto mode, red-eye reduction mode",
  209. 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
  210. 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
  211. },
  212. SensingMethod: {
  213. 1: "Not defined",
  214. 2: "One-chip color area sensor",
  215. 3: "Two-chip color area sensor",
  216. 4: "Three-chip color area sensor",
  217. 5: "Color sequential area sensor",
  218. 7: "Trilinear sensor",
  219. 8: "Color sequential linear sensor"
  220. },
  221. SceneCaptureType: {
  222. 0: "Standard",
  223. 1: "Landscape",
  224. 2: "Portrait",
  225. 3: "Night scene"
  226. },
  227. SceneType: {
  228. 1: "Directly photographed"
  229. },
  230. CustomRendered: {
  231. 0: "Normal process",
  232. 1: "Custom process"
  233. },
  234. WhiteBalance: {
  235. 0: "Auto white balance",
  236. 1: "Manual white balance"
  237. },
  238. GainControl: {
  239. 0: "None",
  240. 1: "Low gain up",
  241. 2: "High gain up",
  242. 3: "Low gain down",
  243. 4: "High gain down"
  244. },
  245. Contrast: {
  246. 0: "Normal",
  247. 1: "Soft",
  248. 2: "Hard"
  249. },
  250. Saturation: {
  251. 0: "Normal",
  252. 1: "Low saturation",
  253. 2: "High saturation"
  254. },
  255. Sharpness: {
  256. 0: "Normal",
  257. 1: "Soft",
  258. 2: "Hard"
  259. },
  260. SubjectDistanceRange: {
  261. 0: "Unknown",
  262. 1: "Macro",
  263. 2: "Close view",
  264. 3: "Distant view"
  265. },
  266. FileSource: {
  267. 3: "DSC"
  268. },
  269. Components: {
  270. 0: "",
  271. 1: "Y",
  272. 2: "Cb",
  273. 3: "Cr",
  274. 4: "R",
  275. 5: "G",
  276. 6: "B"
  277. }
  278. };
  279. function addEvent(element, event, handler) {
  280. if (element.addEventListener) {
  281. element.addEventListener(event, handler, false);
  282. } else if (element.attachEvent) {
  283. element.attachEvent("on" + event, handler);
  284. }
  285. }
  286. function imageHasData(img) {
  287. return !!(img.exifdata);
  288. }
  289. function getImageData(img, callback) {
  290. BinaryAjax(img.src, function(http) {
  291. var data = findEXIFinJPEG(http.binaryResponse);
  292. img.exifdata = data || {};
  293. if (callback) {
  294. callback.call(img)
  295. }
  296. });
  297. }
  298. function findEXIFinJPEG(file) {
  299. if (file.getByteAt(0) != 0xFF || file.getByteAt(1) != 0xD8) {
  300. return false; // not a valid jpeg
  301. }
  302. var offset = 2,
  303. length = file.getLength(),
  304. marker;
  305. while (offset < length) {
  306. if (file.getByteAt(offset) != 0xFF) {
  307. if (debug)
  308. console.log("Not a valid marker at offset " + offset + ", found: " + file.getByteAt(offset));
  309. return false; // not a valid marker, something is wrong
  310. }
  311. marker = file.getByteAt(offset + 1);
  312. // we could implement handling for other markers here,
  313. // but we're only looking for 0xFFE1 for EXIF data
  314. if (marker == 22400) {
  315. if (debug)
  316. console.log("Found 0xFFE1 marker");
  317. return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2);
  318. // offset += 2 + file.getShortAt(offset+2, true);
  319. } else if (marker == 225) {
  320. // 0xE1 = Application-specific 1 (for EXIF)
  321. if (debug)
  322. console.log("Found 0xFFE1 marker");
  323. return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2);
  324. } else {
  325. offset += 2 + file.getShortAt(offset + 2, true);
  326. }
  327. }
  328. }
  329. function readTags(file, tiffStart, dirStart, strings, bigEnd) {
  330. var entries = file.getShortAt(dirStart, bigEnd),
  331. tags = {},
  332. entryOffset, tag,
  333. i;
  334. for (i = 0; i < entries; i++) {
  335. entryOffset = dirStart + i * 12 + 2;
  336. tag = strings[file.getShortAt(entryOffset, bigEnd)];
  337. if (!tag && debug)
  338. console.log("Unknown tag: " + file.getShortAt(entryOffset, bigEnd));
  339. tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
  340. }
  341. return tags;
  342. }
  343. function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
  344. var type = file.getShortAt(entryOffset + 2, bigEnd),
  345. numValues = file.getLongAt(entryOffset + 4, bigEnd),
  346. valueOffset = file.getLongAt(entryOffset + 8, bigEnd) + tiffStart,
  347. offset,
  348. vals, val, n,
  349. numerator, denominator;
  350. switch (type) {
  351. case 1: // byte, 8-bit unsigned int
  352. case 7: // undefined, 8-bit byte, value depending on field
  353. if (numValues == 1) {
  354. return file.getByteAt(entryOffset + 8, bigEnd);
  355. } else {
  356. offset = numValues > 4 ? valueOffset : (entryOffset + 8);
  357. vals = [];
  358. for (n = 0; n < numValues; n++) {
  359. vals[n] = file.getByteAt(offset + n);
  360. }
  361. return vals;
  362. }
  363. case 2: // ascii, 8-bit byte
  364. offset = numValues > 4 ? valueOffset : (entryOffset + 8);
  365. return file.getStringAt(offset, numValues - 1);
  366. case 3: // short, 16 bit int
  367. if (numValues == 1) {
  368. return file.getShortAt(entryOffset + 8, bigEnd);
  369. } else {
  370. offset = numValues > 2 ? valueOffset : (entryOffset + 8);
  371. vals = [];
  372. for (n = 0; n < numValues; n++) {
  373. vals[n] = file.getShortAt(offset + 2 * n, bigEnd);
  374. }
  375. return vals;
  376. }
  377. case 4: // long, 32 bit int
  378. if (numValues == 1) {
  379. return file.getLongAt(entryOffset + 8, bigEnd);
  380. } else {
  381. vals = [];
  382. for (var n = 0; n < numValues; n++) {
  383. vals[n] = file.getLongAt(valueOffset + 4 * n, bigEnd);
  384. }
  385. return vals;
  386. }
  387. case 5: // rational = two long values, first is numerator, second is denominator
  388. if (numValues == 1) {
  389. numerator = file.getLongAt(valueOffset, bigEnd);
  390. denominator = file.getLongAt(valueOffset + 4, bigEnd);
  391. val = new Number(numerator / denominator);
  392. val.numerator = numerator;
  393. val.denominator = denominator;
  394. return val;
  395. } else {
  396. vals = [];
  397. for (n = 0; n < numValues; n++) {
  398. numerator = file.getLongAt(valueOffset + 8 * n, bigEnd);
  399. denominator = file.getLongAt(valueOffset + 4 + 8 * n, bigEnd);
  400. vals[n] = new Number(numerator / denominator);
  401. vals[n].numerator = numerator;
  402. vals[n].denominator = denominator;
  403. }
  404. return vals;
  405. }
  406. case 9: // slong, 32 bit signed int
  407. if (numValues == 1) {
  408. return file.getSLongAt(entryOffset + 8, bigEnd);
  409. } else {
  410. vals = [];
  411. for (n = 0; n < numValues; n++) {
  412. vals[n] = file.getSLongAt(valueOffset + 4 * n, bigEnd);
  413. }
  414. return vals;
  415. }
  416. case 10: // signed rational, two slongs, first is numerator, second is denominator
  417. if (numValues == 1) {
  418. return file.getSLongAt(valueOffset, bigEnd) / file.getSLongAt(valueOffset + 4, bigEnd);
  419. } else {
  420. vals = [];
  421. for (n = 0; n < numValues; n++) {
  422. vals[n] = file.getSLongAt(valueOffset + 8 * n, bigEnd) / file.getSLongAt(valueOffset + 4 + 8 * n, bigEnd);
  423. }
  424. return vals;
  425. }
  426. }
  427. }
  428. function readEXIFData(file, start) {
  429. if (file.getStringAt(start, 4) != "Exif") {
  430. if (debug)
  431. console.log("Not valid EXIF data! " + file.getStringAt(start, 4));
  432. return false;
  433. }
  434. var bigEnd,
  435. tags, tag,
  436. exifData, gpsData,
  437. tiffOffset = start + 6;
  438. // test for TIFF validity and endianness
  439. if (file.getShortAt(tiffOffset) == 0x4949) {
  440. bigEnd = false;
  441. } else if (file.getShortAt(tiffOffset) == 0x4D4D) {
  442. bigEnd = true;
  443. } else {
  444. if (debug)
  445. console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
  446. return false;
  447. }
  448. if (file.getShortAt(tiffOffset + 2, bigEnd) != 0x002A) {
  449. if (debug)
  450. console.log("Not valid TIFF data! (no 0x002A)");
  451. return false;
  452. }
  453. if (file.getLongAt(tiffOffset + 4, bigEnd) != 0x00000008) {
  454. if (debug)
  455. console.log("Not valid TIFF data! (First offset not 8)", file.getShortAt(tiffOffset + 4, bigEnd));
  456. return false;
  457. }
  458. tags = readTags(file, tiffOffset, tiffOffset + 8, TiffTags, bigEnd);
  459. if (tags.ExifIFDPointer) {
  460. exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
  461. for (tag in exifData) {
  462. switch (tag) {
  463. case "LightSource" :
  464. case "Flash" :
  465. case "MeteringMode" :
  466. case "ExposureProgram" :
  467. case "SensingMethod" :
  468. case "SceneCaptureType" :
  469. case "SceneType" :
  470. case "CustomRendered" :
  471. case "WhiteBalance" :
  472. case "GainControl" :
  473. case "Contrast" :
  474. case "Saturation" :
  475. case "Sharpness" :
  476. case "SubjectDistanceRange" :
  477. case "FileSource" :
  478. exifData[tag] = StringValues[tag][exifData[tag]];
  479. break;
  480. case "ExifVersion" :
  481. case "FlashpixVersion" :
  482. exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
  483. break;
  484. case "ComponentsConfiguration" :
  485. exifData[tag] =
  486. StringValues.Components[exifData[tag][0]]
  487. + StringValues.Components[exifData[tag][1]]
  488. + StringValues.Components[exifData[tag][2]]
  489. + StringValues.Components[exifData[tag][3]];
  490. break;
  491. }
  492. tags[tag] = exifData[tag];
  493. }
  494. }
  495. if (tags.GPSInfoIFDPointer) {
  496. gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
  497. for (tag in gpsData) {
  498. switch (tag) {
  499. case "GPSVersionID" :
  500. gpsData[tag] = gpsData[tag][0]
  501. + "." + gpsData[tag][1]
  502. + "." + gpsData[tag][2]
  503. + "." + gpsData[tag][3];
  504. break;
  505. }
  506. tags[tag] = gpsData[tag];
  507. }
  508. }
  509. return tags;
  510. }
  511. function getData(img, callback) {
  512. if (!img.complete)
  513. return false;
  514. if (!imageHasData(img)) {
  515. getImageData(img, callback);
  516. } else {
  517. if (callback) {
  518. callback.call(img);
  519. }
  520. }
  521. return true;
  522. }
  523. function getTag(img, tag) {
  524. if (!imageHasData(img))
  525. return;
  526. return img.exifdata[tag];
  527. }
  528. function getAllTags(img) {
  529. if (!imageHasData(img))
  530. return {};
  531. var a,
  532. data = img.exifdata,
  533. tags = {};
  534. for (a in data) {
  535. if (data.hasOwnProperty(a)) {
  536. tags[a] = data[a];
  537. }
  538. }
  539. return tags;
  540. }
  541. function pretty(img) {
  542. if (!imageHasData(img))
  543. return "";
  544. var a,
  545. data = img.exifdata,
  546. strPretty = "";
  547. for (a in data) {
  548. if (data.hasOwnProperty(a)) {
  549. if (typeof data[a] == "object") {
  550. if (data[a] instanceof Number) {
  551. strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
  552. } else {
  553. strPretty += a + " : [" + data[a].length + " values]\r\n";
  554. }
  555. } else {
  556. strPretty += a + " : " + data[a] + "\r\n";
  557. }
  558. }
  559. }
  560. return strPretty;
  561. }
  562. function readFromBinaryFile(file) {
  563. return findEXIFinJPEG(file);
  564. }
  565. return {
  566. readFromBinaryFile: readFromBinaryFile,
  567. pretty: pretty,
  568. getTag: getTag,
  569. getAllTags: getAllTags,
  570. getData: getData,
  571. Tags: ExifTags,
  572. TiffTags: TiffTags,
  573. GPSTags: GPSTags,
  574. StringValues: StringValues
  575. };
  576. })();