parser.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*jslint browser: true, devel: true, bitwise: false, debug: true, eqeq: false, es5: true, evil: false, forin: false, newcap: false, nomen: true, plusplus: true, regexp: false, unparam: false, sloppy: true, stupid: false, sub: false, todo: true, vars: true, white: true */
  2. var jpeg = require('./jpeg'),
  3. exif = require('./exif'),
  4. simplify = require('./simplify');
  5. function ExifResult(startMarker, tags, imageSize, thumbnailOffset, thumbnailLength, thumbnailType, app1Offset) {
  6. this.startMarker = startMarker;
  7. this.tags = tags;
  8. this.imageSize = imageSize;
  9. this.thumbnailOffset = thumbnailOffset;
  10. this.thumbnailLength = thumbnailLength;
  11. this.thumbnailType = thumbnailType;
  12. this.app1Offset = app1Offset;
  13. }
  14. ExifResult.prototype = {
  15. hasThumbnail: function(mime) {
  16. if(!this.thumbnailOffset || !this.thumbnailLength) {
  17. return false;
  18. }
  19. if(typeof mime !== 'string') {
  20. return true;
  21. }
  22. if(mime.toLowerCase().trim() === 'image/jpeg') {
  23. return this.thumbnailType === 6;
  24. }
  25. if(mime.toLowerCase().trim() === 'image/tiff') {
  26. return this.thumbnailType === 1;
  27. }
  28. return false;
  29. },
  30. getThumbnailOffset: function() {
  31. return this.app1Offset + 6 + this.thumbnailOffset;
  32. },
  33. getThumbnailLength: function() {
  34. return this.thumbnailLength;
  35. },
  36. getThumbnailBuffer: function() {
  37. return this._getThumbnailStream().nextBuffer(this.thumbnailLength);
  38. },
  39. _getThumbnailStream: function() {
  40. return this.startMarker.openWithOffset(this.getThumbnailOffset());
  41. },
  42. getImageSize: function() {
  43. return this.imageSize;
  44. },
  45. getThumbnailSize: function() {
  46. var stream = this._getThumbnailStream(), size;
  47. jpeg.parseSections(stream, function(sectionType, sectionStream) {
  48. if(jpeg.getSectionName(sectionType).name === 'SOF') {
  49. size = jpeg.getSizeFromSOFSection(sectionStream);
  50. }
  51. });
  52. return size;
  53. }
  54. };
  55. function Parser(stream) {
  56. this.stream = stream;
  57. this.flags = {
  58. readBinaryTags: false,
  59. resolveTagNames: true,
  60. simplifyValues: true,
  61. imageSize: true,
  62. hidePointers: true,
  63. returnTags: true
  64. };
  65. }
  66. Parser.prototype = {
  67. enableBinaryFields: function(enable) {
  68. this.flags.readBinaryTags = !!enable;
  69. return this;
  70. },
  71. enablePointers: function(enable) {
  72. this.flags.hidePointers = !enable;
  73. return this;
  74. },
  75. enableTagNames: function(enable) {
  76. this.flags.resolveTagNames = !!enable;
  77. return this;
  78. },
  79. enableImageSize: function(enable) {
  80. this.flags.imageSize = !!enable;
  81. return this;
  82. },
  83. enableReturnTags: function(enable) {
  84. this.flags.returnTags = !!enable;
  85. return this;
  86. },
  87. enableSimpleValues: function(enable) {
  88. this.flags.simplifyValues = !!enable;
  89. return this;
  90. },
  91. parse: function() {
  92. var start = this.stream.mark(),
  93. stream = start.openWithOffset(0),
  94. flags = this.flags,
  95. tags,
  96. imageSize,
  97. thumbnailOffset,
  98. thumbnailLength,
  99. thumbnailType,
  100. app1Offset,
  101. tagNames,
  102. getTagValue, setTagValue;
  103. if(flags.resolveTagNames) {
  104. tagNames = require('./exif-tags');
  105. }
  106. if(flags.resolveTagNames) {
  107. tags = {};
  108. getTagValue = function(t) {
  109. return tags[t.name];
  110. };
  111. setTagValue = function(t, value) {
  112. tags[t.name] = value;
  113. };
  114. } else {
  115. tags = [];
  116. getTagValue = function(t) {
  117. var i;
  118. for(i = 0; i < tags.length; ++i) {
  119. if(tags[i].type === t.type && tags[i].section === t.section) {
  120. return tags.value;
  121. }
  122. }
  123. };
  124. setTagValue = function(t, value) {
  125. var i;
  126. for(i = 0; i < tags.length; ++i) {
  127. if(tags[i].type === t.type && tags[i].section === t.section) {
  128. tags.value = value;
  129. return;
  130. }
  131. }
  132. };
  133. }
  134. jpeg.parseSections(stream, function(sectionType, sectionStream) {
  135. var validExifHeaders, sectionOffset = sectionStream.offsetFrom(start);
  136. if(sectionType === 0xE1) {
  137. validExifHeaders = exif.parseTags(sectionStream, function(ifdSection, tagType, value, format) {
  138. //ignore binary fields if disabled
  139. if(!flags.readBinaryTags && format === 7) {
  140. return;
  141. }
  142. if(tagType === 0x0201) {
  143. thumbnailOffset = value[0];
  144. if(flags.hidePointers) {return;}
  145. } else if(tagType === 0x0202) {
  146. thumbnailLength = value[0];
  147. if(flags.hidePointers) {return;}
  148. } else if(tagType === 0x0103) {
  149. thumbnailType = value[0];
  150. if(flags.hidePointers) {return;}
  151. }
  152. //if flag is set to not store tags, return here after storing pointers
  153. if(!flags.returnTags) {
  154. return;
  155. }
  156. if(flags.simplifyValues) {
  157. value = simplify.simplifyValue(value, format);
  158. }
  159. if(flags.resolveTagNames) {
  160. var sectionTagNames = ifdSection === exif.GPSIFD ? tagNames.gps : tagNames.exif;
  161. var name = sectionTagNames[tagType];
  162. if(!name) {
  163. name = tagNames.exif[tagType];
  164. }
  165. if (!tags.hasOwnProperty(name)) {
  166. tags[name] = value;
  167. }
  168. } else {
  169. tags.push({
  170. section: ifdSection,
  171. type: tagType,
  172. value: value
  173. });
  174. }
  175. });
  176. if(validExifHeaders) {
  177. app1Offset = sectionOffset;
  178. }
  179. }
  180. else if(flags.imageSize && jpeg.getSectionName(sectionType).name === 'SOF') {
  181. imageSize = jpeg.getSizeFromSOFSection(sectionStream);
  182. }
  183. });
  184. if(flags.simplifyValues) {
  185. simplify.castDegreeValues(getTagValue, setTagValue);
  186. simplify.castDateValues(getTagValue, setTagValue);
  187. }
  188. return new ExifResult(start, tags, imageSize, thumbnailOffset, thumbnailLength, thumbnailType, app1Offset);
  189. }
  190. };
  191. module.exports = Parser;