blueimp-load-image
Version:
JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled, cropped or rotated HTML img or canvas element. It also provides methods to parse image metadata to extract IPTC and Exif tags a
240 lines (222 loc) • 7.04 kB
JavaScript
/*
* JavaScript Load Image IPTC Parser
* https://github.com/blueimp/JavaScript-Load-Image
*
* Copyright 2013, Sebastian Tschan
* Copyright 2018, Dave Bevan
* https://blueimp.net
*
* Licensed under the MIT license:
* https://opensource.org/licenses/MIT
*/
/* global define, module, require, DataView */
;(function (factory) {
'use strict'
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define(['./load-image', './load-image-meta'], factory)
} else if (typeof module === 'object' && module.exports) {
factory(require('./load-image'), require('./load-image-meta'))
} else {
// Browser globals:
factory(window.loadImage)
}
})(function (loadImage) {
'use strict'
/**
* IPTC tag map
*
* @name IptcMap
* @class
*/
function IptcMap() {}
IptcMap.prototype.map = {
ObjectName: 5
}
IptcMap.prototype.types = {
0: 'Uint16', // ApplicationRecordVersion
200: 'Uint16', // ObjectPreviewFileFormat
201: 'Uint16', // ObjectPreviewFileVersion
202: 'binary' // ObjectPreviewData
}
/**
* Retrieves IPTC tag value
*
* @param {number|string} id IPTC tag code or name
* @returns {object} IPTC tag value
*/
IptcMap.prototype.get = function (id) {
return this[id] || this[this.map[id]]
}
/**
* Retrieves string for the given DataView and range
*
* @param {DataView} dataView Data view interface
* @param {number} offset Offset start
* @param {number} length Offset length
* @returns {string} String value
*/
function getStringValue(dataView, offset, length) {
var outstr = ''
var end = offset + length
for (var n = offset; n < end; n += 1) {
outstr += String.fromCharCode(dataView.getUint8(n))
}
return outstr
}
/**
* Retrieves tag value for the given DataView and range
*
* @param {number} tagCode tag code
* @param {IptcMap} map IPTC tag map
* @param {DataView} dataView Data view interface
* @param {number} offset Range start
* @param {number} length Range length
* @returns {object} Tag value
*/
function getTagValue(tagCode, map, dataView, offset, length) {
if (map.types[tagCode] === 'binary') {
return new Blob([dataView.buffer.slice(offset, offset + length)])
}
if (map.types[tagCode] === 'Uint16') {
return dataView.getUint16(offset)
}
return getStringValue(dataView, offset, length)
}
/**
* Combines IPTC value with existing ones.
*
* @param {object} value Existing IPTC field value
* @param {object} newValue New IPTC field value
* @returns {object} Resulting IPTC field value
*/
function combineTagValues(value, newValue) {
if (value === undefined) return newValue
if (value instanceof Array) {
value.push(newValue)
return value
}
return [value, newValue]
}
/**
* Parses IPTC tags.
*
* @param {DataView} dataView Data view interface
* @param {number} segmentOffset Segment offset
* @param {number} segmentLength Segment length
* @param {object} data Data export object
* @param {object} includeTags Map of tags to include
* @param {object} excludeTags Map of tags to exclude
*/
function parseIptcTags(
dataView,
segmentOffset,
segmentLength,
data,
includeTags,
excludeTags
) {
var value, tagSize, tagCode
var segmentEnd = segmentOffset + segmentLength
var offset = segmentOffset
while (offset < segmentEnd) {
if (
dataView.getUint8(offset) === 0x1c && // tag marker
dataView.getUint8(offset + 1) === 0x02 // record number, only handles v2
) {
tagCode = dataView.getUint8(offset + 2)
if (
(!includeTags || includeTags[tagCode]) &&
(!excludeTags || !excludeTags[tagCode])
) {
tagSize = dataView.getInt16(offset + 3)
value = getTagValue(tagCode, data.iptc, dataView, offset + 5, tagSize)
data.iptc[tagCode] = combineTagValues(data.iptc[tagCode], value)
if (data.iptcOffsets) {
data.iptcOffsets[tagCode] = offset
}
}
}
offset += 1
}
}
/**
* Tests if field segment starts at offset.
*
* @param {DataView} dataView Data view interface
* @param {number} offset Segment offset
* @returns {boolean} True if '8BIM<EOT><EOT>' exists at offset
*/
function isSegmentStart(dataView, offset) {
return (
dataView.getUint32(offset) === 0x3842494d && // Photoshop segment start
dataView.getUint16(offset + 4) === 0x0404 // IPTC segment start
)
}
/**
* Returns header length.
*
* @param {DataView} dataView Data view interface
* @param {number} offset Segment offset
* @returns {number} Header length
*/
function getHeaderLength(dataView, offset) {
var length = dataView.getUint8(offset + 7)
if (length % 2 !== 0) length += 1
// Check for pre photoshop 6 format
if (length === 0) {
// Always 4
length = 4
}
return length
}
loadImage.parseIptcData = function (dataView, offset, length, data, options) {
if (options.disableIptc) {
return
}
var markerLength = offset + length
while (offset + 8 < markerLength) {
if (isSegmentStart(dataView, offset)) {
var headerLength = getHeaderLength(dataView, offset)
var segmentOffset = offset + 8 + headerLength
if (segmentOffset > markerLength) {
// eslint-disable-next-line no-console
console.log('Invalid IPTC data: Invalid segment offset.')
break
}
var segmentLength = dataView.getUint16(offset + 6 + headerLength)
if (offset + segmentLength > markerLength) {
// eslint-disable-next-line no-console
console.log('Invalid IPTC data: Invalid segment size.')
break
}
// Create the iptc object to store the tags:
data.iptc = new IptcMap()
if (!options.disableIptcOffsets) {
data.iptcOffsets = new IptcMap()
}
parseIptcTags(
dataView,
segmentOffset,
segmentLength,
data,
options.includeIptcTags,
options.excludeIptcTags || { 202: true } // ObjectPreviewData
)
return
}
// eslint-disable-next-line no-param-reassign
offset += 1
}
}
// Registers this IPTC parser for the APP13 JPEG metadata segment:
loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData)
loadImage.IptcMap = IptcMap
// Adds the following properties to the parseMetaData callback data:
// - iptc: The iptc tags, parsed by the parseIptcData method
// Adds the following options to the parseMetaData method:
// - disableIptc: Disables IPTC parsing when true.
// - disableIptcOffsets: Disables storing IPTC tag offsets when true.
// - includeIptcTags: A map of IPTC tags to include for parsing.
// - excludeIptcTags: A map of IPTC tags to exclude from parsing.
})