load-image-iptc.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * JavaScript Load Image IPTC Parser
  3. * https://github.com/blueimp/JavaScript-Load-Image
  4. *
  5. * Copyright 2013, Sebastian Tschan
  6. * Copyright 2018, Dave Bevan
  7. * https://blueimp.net
  8. *
  9. * Licensed under the MIT license:
  10. * https://opensource.org/licenses/MIT
  11. */
  12. /* global define, module, require, Buffer */
  13. ;(function(factory) {
  14. 'use strict'
  15. if (typeof define === 'function' && define.amd) {
  16. // Register as an anonymous AMD module:
  17. define(['./load-image', './load-image-meta'], factory)
  18. } else if (typeof module === 'object' && module.exports) {
  19. factory(require('./load-image'), require('./load-image-meta'))
  20. } else {
  21. // Browser globals:
  22. factory(window.loadImage)
  23. }
  24. })(function(loadImage) {
  25. 'use strict'
  26. loadImage.IptcMap = function() {
  27. return this
  28. }
  29. loadImage.IptcMap.prototype.map = {
  30. ObjectName: 0x5
  31. }
  32. loadImage.IptcMap.prototype.get = function(id) {
  33. return this[id] || this[this.map[id]]
  34. }
  35. loadImage.parseIptcTags = function(
  36. dataView,
  37. startOffset,
  38. sectionLength,
  39. data
  40. ) {
  41. /**
  42. * Retrieves string for the given Buffer and range
  43. *
  44. * @param {Buffer} buffer IPTC buffer
  45. * @param {number} start Range start
  46. * @param {number} length Range length
  47. * @returns {string} String value
  48. */
  49. function getStringFromDB(buffer, start, length) {
  50. var outstr = ''
  51. for (var n = start; n < start + length; n++) {
  52. outstr += String.fromCharCode(buffer.getUint8(n))
  53. }
  54. return outstr
  55. }
  56. var fieldValue, dataSize, segmentType
  57. var segmentStartPos = startOffset
  58. while (segmentStartPos < startOffset + sectionLength) {
  59. // we currently handle the 2: class of iptc tag
  60. if (
  61. dataView.getUint8(segmentStartPos) === 0x1c &&
  62. dataView.getUint8(segmentStartPos + 1) === 0x02
  63. ) {
  64. segmentType = dataView.getUint8(segmentStartPos + 2)
  65. // only store data for known tags
  66. if (segmentType in data.iptc.tags) {
  67. dataSize = dataView.getInt16(segmentStartPos + 3)
  68. fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize)
  69. // Check if we already stored a value with this name
  70. if (Object.prototype.hasOwnProperty.call(data.iptc, segmentType)) {
  71. // Value already stored with this name, create multivalue field
  72. if (data.iptc[segmentType] instanceof Array) {
  73. data.iptc[segmentType].push(fieldValue)
  74. } else {
  75. data.iptc[segmentType] = [data.iptc[segmentType], fieldValue]
  76. }
  77. } else {
  78. data.iptc[segmentType] = fieldValue
  79. }
  80. }
  81. }
  82. segmentStartPos++
  83. }
  84. }
  85. loadImage.parseIptcData = function(dataView, offset, length, data, options) {
  86. if (options.disableIptc) {
  87. return
  88. }
  89. var markerLength = offset + length
  90. // Found '8BIM<EOT><EOT>' ?
  91. var isFieldSegmentStart = function(dataView, offset) {
  92. return (
  93. dataView.getUint32(offset) === 0x3842494d &&
  94. dataView.getUint16(offset + 4) === 0x0404
  95. )
  96. }
  97. // Hunt forward, looking for the correct IPTC block signature:
  98. // Reference: https://metacpan.org/pod/distribution/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/Structures.pod#Structure-of-a-Photoshop-style-APP13-segment
  99. // From https://github.com/exif-js/exif-js/blob/master/exif.js ~ line 474 on
  100. while (offset + 8 < markerLength) {
  101. if (isFieldSegmentStart(dataView, offset)) {
  102. var nameHeaderLength = dataView.getUint8(offset + 7)
  103. if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1
  104. // Check for pre photoshop 6 format
  105. if (nameHeaderLength === 0) {
  106. // Always 4
  107. nameHeaderLength = 4
  108. }
  109. var startOffset = offset + 8 + nameHeaderLength
  110. if (startOffset > markerLength) {
  111. // eslint-disable-next-line no-console
  112. console.log('Invalid IPTC data: Invalid segment offset.')
  113. break
  114. }
  115. var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength)
  116. if (offset + sectionLength > markerLength) {
  117. // eslint-disable-next-line no-console
  118. console.log('Invalid IPTC data: Invalid segment size.')
  119. break
  120. }
  121. // Create the iptc object to store the tags:
  122. data.iptc = new loadImage.IptcMap()
  123. // Parse the tags
  124. return loadImage.parseIptcTags(
  125. dataView,
  126. startOffset,
  127. sectionLength,
  128. data
  129. )
  130. }
  131. // eslint-disable-next-line no-param-reassign
  132. offset++
  133. }
  134. // eslint-disable-next-line no-console
  135. console.log('No IPTC data at this offset - could be XMP')
  136. }
  137. // Registers this IPTC parser for the APP13 JPEG meta data segment:
  138. loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData)
  139. // Adds the following properties to the parseMetaData callback data:
  140. // * iptc: The iptc tags, parsed by the parseIptcData method
  141. // Adds the following options to the parseMetaData method:
  142. // * disableIptc: Disables IPTC parsing.
  143. })