UNPKG

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 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. })