From 2ea9076d0d327522128ae23a7b24ae2f8e9ac3e3 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 20:48:37 +0000 Subject: [PATCH 01/14] Add an IPTC parser, plus documentation, demo page etc. --- README.md | 39 +++++++++ iptc.html | 73 +++++++++++++++++ js/demo/demo-iptc.js | 165 ++++++++++++++++++++++++++++++++++++++ js/load-image-iptc-map.js | 124 ++++++++++++++++++++++++++++ js/load-image-iptc.js | 146 +++++++++++++++++++++++++++++++++ 5 files changed, 547 insertions(+) create mode 100644 iptc.html create mode 100644 js/demo/demo-iptc.js create mode 100644 js/load-image-iptc-map.js create mode 100644 js/load-image-iptc.js diff --git a/README.md b/README.md index 2a03e0b..8e35c63 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,13 @@ Or alternatively, choose which components you want to include: + + + + + ``` @@ -314,6 +319,40 @@ disable certain aspects of the parser: * **disableExifSub**: Disables parsing of the Exif Sub IFD. * **disableExifGps**: Disables parsing of the Exif GPS Info IFD. +### Iptc parser +If you include the Load Image Iptc Parser extension, the argument passed to the +callback for **parseMetaData** will contain the additional property **iptc** if +Iptc data could be found in the given image. +The **iptc** object stores the parsed Iptc tags: + +```js +var objectname = data.iptc[0x5]; +``` + +It also provides an **iptc.get()** method to retrieve the tag value via the +tag's mapped name: + +```js +var objectname = data.iptc.get('ObjectName'); +``` + +By default, the only available mapped names are **ObjectName**. +If you also include the Load Image Iptc Map library, additional tag mappings +become available, as well as two additional methods, **iptc.getText()** and +**iptc.getAll()**: + +```js +var keywords = data.iptc.getText('Keywords'); // e.g.: ['Weather','Sky'] + +// A map of all parsed tags with their mapped names as keys and their text values: +var allTags = data.iptc.getAll(); +``` + +The Iptc parser also adds additional options for the parseMetaData method, to +disable certain aspects of the parser: + +* **disableIptc**: Disables Iptc parsing. + ## License The JavaScript Load Image script is released under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/iptc.html b/iptc.html new file mode 100644 index 0000000..7071731 --- /dev/null +++ b/iptc.html @@ -0,0 +1,73 @@ + + + + + + +Codestin Search App + + + + + + + +

JavaScript Load Image IPTC Demo

+

JavaScript Load Image is a library to load images provided as File or Blob objects or via URL.
+It returns an optionally scaled and/or cropped HTML img or canvas element.
+It also provides a method to parse image meta data to extract Iptc tags and thumbnails and to restore the complete image header after resizing.

+ +
+

Select an image file

+

+

Or drag & drop an image file onto this webpage.

+
+

Result

+ +
+

This demo works only in browsers with support for the URL or FileReader API.

+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/js/demo/demo-iptc.js b/js/demo/demo-iptc.js new file mode 100644 index 0000000..4411d7f --- /dev/null +++ b/js/demo/demo-iptc.js @@ -0,0 +1,165 @@ +/* + * JavaScript Load Image Demo JS + * 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 loadImage, HTMLCanvasElement, $ */ + +$(function () { + 'use strict' + + var result = $('#result') + var iptcNode = $('#iptc') + var actionsNode = $('#actions') + var currentFile + var coordinates + + function displayIptcData (iptc) { + var tags = iptc.getAll() + var table = iptcNode.find('table').empty() + var row = $('') + var cell = $('') + var prop + for (prop in tags) { + if (tags.hasOwnProperty(prop)) { + table.append( + row.clone() + .append(cell.clone().text(prop)) + .append(cell.clone().text(tags[prop])) + ) + } + } + iptcNode.show() + } + + function updateResults (img, data) { + var fileName = currentFile.name + var href = img.src + var dataURLStart + var content + if (!(img.src || img instanceof HTMLCanvasElement)) { + content = $('Loading image file failed') + } else { + if (!href) { + href = img.toDataURL(currentFile.type + 'REMOVEME') + // Check if file type is supported for the dataURL export: + dataURLStart = 'data:' + currentFile.type + if (href.slice(0, dataURLStart.length) !== dataURLStart) { + fileName = fileName.replace(/\.\w+$/, '.png') + } + } + content = $('').append(img) + .attr('download', fileName) + .attr('href', href) + } + result.children().replaceWith(content) + if (img.getContext) { + actionsNode.show() + } + if (data && data.iptc) { + displayIptcData(data.iptc) + } + } + + function displayImage (file, options) { + currentFile = file + if (!loadImage( + file, + updateResults, + options + )) { + result.children().replaceWith( + $('' + + 'Your browser does not support the URL or FileReader API.' + + '') + ) + } + } + + function dropChangeHandler (e) { + e.preventDefault() + e = e.originalEvent + var target = e.dataTransfer || e.target + var file = target && target.files && target.files[0] + var options = { + maxWidth: result.width(), + canvas: true, + pixelRatio: window.devicePixelRatio, + downsamplingRatio: 0.5, + orientation: true + } + if (!file) { + return + } + iptcNode.hide() + displayImage(file, options) + } + + // Hide URL/FileReader API requirement message in capable browsers: + if (window.createObjectURL || window.URL || window.webkitURL || + window.FileReader) { + result.children().hide() + } + + $(document) + .on('dragover', function (e) { + e.preventDefault() + e = e.originalEvent + e.dataTransfer.dropEffect = 'copy' + }) + .on('drop', dropChangeHandler) + + $('#file-input') + .on('change', dropChangeHandler) + + $('#edit') + .on('click', function (event) { + event.preventDefault() + var imgNode = result.find('img, canvas') + var img = imgNode[0] + var pixelRatio = window.devicePixelRatio || 1 + imgNode.Jcrop({ + setSelect: [ + 40, + 40, + (img.width / pixelRatio) - 40, + (img.height / pixelRatio) - 40 + ], + onSelect: function (coords) { + coordinates = coords + }, + onRelease: function () { + coordinates = null + } + }).parent().on('click', function (event) { + event.preventDefault() + }) + }) + + $('#crop') + .on('click', function (event) { + event.preventDefault() + var img = result.find('img, canvas')[0] + var pixelRatio = window.devicePixelRatio || 1 + if (img && coordinates) { + updateResults(loadImage.scale(img, { + left: coordinates.x * pixelRatio, + top: coordinates.y * pixelRatio, + sourceWidth: coordinates.w * pixelRatio, + sourceHeight: coordinates.h * pixelRatio, + minWidth: result.width(), + maxWidth: result.width(), + pixelRatio: pixelRatio, + downsamplingRatio: 0.5 + })) + coordinates = null + } + }) +}) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js new file mode 100644 index 0000000..12b585f --- /dev/null +++ b/js/load-image-iptc-map.js @@ -0,0 +1,124 @@ +/* + * JavaScript Load Image Iptc Map + * https://github.com/blueimp/JavaScript-Load-Image + * + * Copyright 2018, Dave Bevan + * + * Iptc tags mapping based on + * https://github.com/jseidelin/exif-js + * + * Licensed under the MIT license: + * https://opensource.org/licenses/MIT + */ + +/* global define */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define(['./load-image', './load-image-iptc'], factory) + } else if (typeof module === 'object' && module.exports) { + factory(require('./load-image'), require('./load-image-iptc')) + } else { + // Browser globals: + factory(window.loadImage) + } +})(function (loadImage) { + 'use strict' + + loadImage.IptcMap.prototype.tags = { + // ========== + // Iptc tags: + // ========== + //0x00 : "RecordVersion", + 0x03 : "ObjectType", + 0x04 : "ObjectAttribute", + 0x05 : "ObjectName", + 0x07 : "EditStatus", + 0x08 : "EditorialUpdate", + 0x0a : "Urgency", + 0x0c : "SubjectRef", + 0x0f : "Category", + 0x14 : "SupplCategory", + 0x16 : "FixtureID", + 0x19 : "Keywords", + 0x1a : "ContentLocCode", + 0x1b : "ContentLocName", + 0x1e : "ReleaseDate", + 0x23 : "ReleaseTime", + 0x25 : "ExpirationDate", + 0x26 : "ExpirationTime", + 0x28 : "SpecialInstructions", + 0x2a : "ActionAdvised", + 0x2d : "RefService", + 0x2f : "RefDate", + 0x32 : "RefNumber", + 0x37 : "DateCreated", + 0x3c : "TimeCreated", + 0x3e : "DigitalCreationDate", + 0x3f : "DigitalCreationTime", + 0x41 : "OriginatingProgram", + 0x46 : "ProgramVersion", + 0x4b : "ObjectCycle", + 0x50 : "Byline", + 0x55 : "BylineTitle", + 0x5a : "City", + 0x5c : "Sublocation", + 0x5f : "State", + 0x64 : "CountryCode", + 0x65 : "CountryName", + 0x67 : "OrigTransRef", + 0x69 : "Headline", + 0x6e : "Credit", + 0x73 : "Source", + 0x74 : "CopyrightNotice", + 0x76 : "Contact", + 0x78 : "Caption", + 0x7a : "WriterEditor", + //0x7d : "RasterizedCaption", + 0x82 : "ImageType", + 0x83 : "ImageOrientation", + 0x87 : "LanguageID", + //0x96 : "AudioType", + //0x97 : "AudioSamplingRate", + //0x98 : "AudioSamplingRes", + //0x99 : "AudioDuration", + //0x9a : "AudioOutcue", + //0xc8 : "PreviewFileFormat", + //0xc9 : "PreviewFileFormatVer", + //0xca : "PreviewData" + } + + loadImage.IptcMap.prototype.getText = function (id) { + var value = this.get(id) + return String(value) + } + + ;(function (iptcMapPrototype) { + var tags = iptcMapPrototype.tags + var map = iptcMapPrototype.map || {} + var prop + // Map the tag names to tags: + for (prop in tags) { + if (tags.hasOwnProperty(prop)) { + map[tags[prop]] = prop + } + } + })(loadImage.IptcMap.prototype) + + loadImage.IptcMap.prototype.getAll = function () { + var map = {} + var prop + var id + for (prop in this) { + if (this.hasOwnProperty(prop)) { + id = this.tags[prop] + if (id) { + map[id] = this.getText(id) + } + } + } + return map + } +}) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js new file mode 100644 index 0000000..d85ed5b --- /dev/null +++ b/js/load-image-iptc.js @@ -0,0 +1,146 @@ +/* + * JavaScript Load Image Iptc Parser + * https://github.com/blueimp/JavaScript-Load-Image + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * https://opensource.org/licenses/MIT + */ + +/* global define, Blob */ + +;(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' + + loadImage.IptcMap = function () { + return this + } + + loadImage.IptcMap.prototype.map = { + "ObjectName": 0x5 + } + + loadImage.IptcMap.prototype.get = function (id) { + return this[id] || this[this.map[id]] + } + + loadImage.parseIptcTags = function ( + dataView, + startOffset, + sectionLength, + data + ) { + + function getStringFromDB(buffer, start, length) { + var outstr = ""; + for (var n = start; n < start+length; n++) { + outstr += String.fromCharCode(buffer.getUint8(n)); + } + return outstr; + } + + var fieldValue, fieldName, dataSize, segmentType, segmentSize + var segmentStartPos = startOffset + while (segmentStartPos < startOffset+sectionLength) { + // we currently handle the 2: class of iptc tag + if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02) { + + segmentType = dataView.getUint8(segmentStartPos+2) + + // only store data for known tags + if (segmentType in data.iptc.tags) { + + dataSize = dataView.getInt16(segmentStartPos+3) + segmentSize = dataSize + 5 + fieldName = data.iptc.tags[segmentType] + fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize) + + // integer field IDs (same as the exif module) + fieldName=segmentType + + // Check if we already stored a value with this name + if (data.iptc.hasOwnProperty(fieldName)) { + // Value already stored with this name, create multivalue field + if (data.iptc[fieldName] instanceof Array) { + data.iptc[fieldName].push(fieldValue) + } + else { + data.iptc[fieldName] = [data.iptc[fieldName], fieldValue] + } + } + else { + data.iptc[fieldName] = fieldValue; + } + } + } + segmentStartPos++; + } + } + + loadImage.parseIptcData = function (dataView, offset, length, data, options) { + if (options.disableIptc) { + return + } + + // skip over leading (variable) chars, to the always-fixed "8BIM" sequence + offset+=18 + + var littleEndian + var dirOffset + + // From https://github.com/exif-js/exif-js/blob/master/exif.js ~ line 474 on + + // Check for the "Iptc" "8BIM" ASCII sequence (0x3842494d 0x0404): + if (dataView.getUint32(offset) !== 0x3842494d && dataView.getUint16(offset + 4) !== 0x0404) { + console.log('no Iptc data at this offset') + // No Iptc data, might be XMP data instead + return + } + + var nameHeaderLength = dataView.getUint8(offset + 7) + if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1 + // Check for pre photoshop 6 format + if (nameHeaderLength === 0) { + // Always 4 + nameHeaderLength = 4 + } + + var startOffset = offset + 8 + nameHeaderLength + var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength) + + // Create the iptc object to store the tags: + data.iptc = new loadImage.IptcMap() + window._iptc=data + + // Parse the tags + loadImage.parseIptcTags( + dataView, + startOffset, + sectionLength, + data + ) + } + + // Registers this Iptc parser for the APP13 JPEG meta data segment: + loadImage.metaDataParsers.jpeg[0xffed]=[] + loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData) + + // 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. +}) From 17bf1387734449a2d5be4fa45ba10595fc7190a5 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 20:55:46 +0000 Subject: [PATCH 02/14] Add copyright and IPTC source references. --- js/load-image-iptc-map.js | 3 +++ js/load-image-iptc.js | 1 + 2 files changed, 4 insertions(+) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index 12b585f..bcfe2d5 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -2,10 +2,13 @@ * JavaScript Load Image Iptc Map * https://github.com/blueimp/JavaScript-Load-Image * + * Copyright 2013, Sebastian Tschan * Copyright 2018, Dave Bevan * * Iptc tags mapping based on * https://github.com/jseidelin/exif-js + * https://iptc.org/standards/photo-metadata + * http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf * * Licensed under the MIT license: * https://opensource.org/licenses/MIT diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index d85ed5b..aec034c 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -3,6 +3,7 @@ * https://github.com/blueimp/JavaScript-Load-Image * * Copyright 2013, Sebastian Tschan + * Copyright 2018, Dave Bevan * https://blueimp.net * * Licensed under the MIT license: From eef367b1dd2f70e5697459ceb92fd5b31696a478 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 22:42:49 +0000 Subject: [PATCH 03/14] Revise the 8BIM detection method to correctly parse the three std IPTC test images at https://iptc.org/standards/photo-metadata/iptc-standard/ --- js/load-image-iptc.js | 69 +++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index aec034c..b72db80 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -96,43 +96,54 @@ return } - // skip over leading (variable) chars, to the always-fixed "8BIM" sequence - offset+=18 + // Found "8BIM" ? + var isFieldSegmentStart = function(dataView, offset){ + return ( + dataView.getUint32(offset) === 0x3842494d && + dataView.getUint16(offset+4) === 0x0404 + ) + } - var littleEndian - var dirOffset + // Hunt forward, looking for the correct Iptc block signature: + // Reference: https://metacpan.org/pod/distribution/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/Structures.pod#Structure-of-a-Photoshop-style-APP13-segment // From https://github.com/exif-js/exif-js/blob/master/exif.js ~ line 474 on + while (offset < offset+length) { - // Check for the "Iptc" "8BIM" ASCII sequence (0x3842494d 0x0404): - if (dataView.getUint32(offset) !== 0x3842494d && dataView.getUint16(offset + 4) !== 0x0404) { - console.log('no Iptc data at this offset') - // No Iptc data, might be XMP data instead - return - } + if (isFieldSegmentStart(dataView, offset)) { - var nameHeaderLength = dataView.getUint8(offset + 7) - if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1 - // Check for pre photoshop 6 format - if (nameHeaderLength === 0) { - // Always 4 - nameHeaderLength = 4 - } + var nameHeaderLength = dataView.getUint8(offset + 7) + if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1 + // Check for pre photoshop 6 format + if (nameHeaderLength === 0) { + // Always 4 + nameHeaderLength = 4 + } + + var startOffset = offset + 8 + nameHeaderLength + var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength) + + // Create the iptc object to store the tags: + data.iptc = new loadImage.IptcMap() - var startOffset = offset + 8 + nameHeaderLength - var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength) + // Parse the tags + return loadImage.parseIptcTags( + dataView, + startOffset, + sectionLength, + data + ) + + break; + + } + + offset++ + + } - // Create the iptc object to store the tags: - data.iptc = new loadImage.IptcMap() - window._iptc=data + console.log('No Iptc data at this offset - could be XMP') - // Parse the tags - loadImage.parseIptcTags( - dataView, - startOffset, - sectionLength, - data - ) } // Registers this Iptc parser for the APP13 JPEG meta data segment: From 6fb6a2739988b99e7654541b7fca2e42cc618f86 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:04:45 +0000 Subject: [PATCH 04/14] Fix JS formatting issues thrown by Travis CI --- js/load-image-iptc-map.js | 114 +++++++++++++++++++------------------- js/load-image-iptc.js | 43 ++++++-------- 2 files changed, 74 insertions(+), 83 deletions(-) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index bcfe2d5..ad21897 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -34,63 +34,63 @@ // ========== // Iptc tags: // ========== - //0x00 : "RecordVersion", - 0x03 : "ObjectType", - 0x04 : "ObjectAttribute", - 0x05 : "ObjectName", - 0x07 : "EditStatus", - 0x08 : "EditorialUpdate", - 0x0a : "Urgency", - 0x0c : "SubjectRef", - 0x0f : "Category", - 0x14 : "SupplCategory", - 0x16 : "FixtureID", - 0x19 : "Keywords", - 0x1a : "ContentLocCode", - 0x1b : "ContentLocName", - 0x1e : "ReleaseDate", - 0x23 : "ReleaseTime", - 0x25 : "ExpirationDate", - 0x26 : "ExpirationTime", - 0x28 : "SpecialInstructions", - 0x2a : "ActionAdvised", - 0x2d : "RefService", - 0x2f : "RefDate", - 0x32 : "RefNumber", - 0x37 : "DateCreated", - 0x3c : "TimeCreated", - 0x3e : "DigitalCreationDate", - 0x3f : "DigitalCreationTime", - 0x41 : "OriginatingProgram", - 0x46 : "ProgramVersion", - 0x4b : "ObjectCycle", - 0x50 : "Byline", - 0x55 : "BylineTitle", - 0x5a : "City", - 0x5c : "Sublocation", - 0x5f : "State", - 0x64 : "CountryCode", - 0x65 : "CountryName", - 0x67 : "OrigTransRef", - 0x69 : "Headline", - 0x6e : "Credit", - 0x73 : "Source", - 0x74 : "CopyrightNotice", - 0x76 : "Contact", - 0x78 : "Caption", - 0x7a : "WriterEditor", - //0x7d : "RasterizedCaption", - 0x82 : "ImageType", - 0x83 : "ImageOrientation", - 0x87 : "LanguageID", - //0x96 : "AudioType", - //0x97 : "AudioSamplingRate", - //0x98 : "AudioSamplingRes", - //0x99 : "AudioDuration", - //0x9a : "AudioOutcue", - //0xc8 : "PreviewFileFormat", - //0xc9 : "PreviewFileFormatVer", - //0xca : "PreviewData" + // 0x00: "RecordVersion", + 0x03: "ObjectType", + 0x04: "ObjectAttribute", + 0x05: "ObjectName", + 0x07: "EditStatus", + 0x08: "EditorialUpdate", + 0x0a: "Urgency", + 0x0c: "SubjectRef", + 0x0f: "Category", + 0x14: "SupplCategory", + 0x16: "FixtureID", + 0x19: "Keywords", + 0x1a: "ContentLocCode", + 0x1b: "ContentLocName", + 0x1e: "ReleaseDate", + 0x23: "ReleaseTime", + 0x25: "ExpirationDate", + 0x26: "ExpirationTime", + 0x28: "SpecialInstructions", + 0x2a: "ActionAdvised", + 0x2d: "RefService", + 0x2f: "RefDate", + 0x32: "RefNumber", + 0x37: "DateCreated", + 0x3c: "TimeCreated", + 0x3e: "DigitalCreationDate", + 0x3f: "DigitalCreationTime", + 0x41: "OriginatingProgram", + 0x46: "ProgramVersion", + 0x4b: "ObjectCycle", + 0x50: "Byline", + 0x55: "BylineTitle", + 0x5a: "City", + 0x5c: "Sublocation", + 0x5f: "State", + 0x64: "CountryCode", + 0x65: "CountryName", + 0x67: "OrigTransRef", + 0x69: "Headline", + 0x6e: "Credit", + 0x73: "Source", + 0x74: "CopyrightNotice", + 0x76: "Contact", + 0x78: "Caption", + 0x7a: "WriterEditor", + // 0x7d: "RasterizedCaption", + 0x82: "ImageType", + 0x83: "ImageOrientation", + 0x87: "LanguageID", + // 0x96: "AudioType", + // 0x97: "AudioSamplingRate", + // 0x98: "AudioSamplingRes", + // 0x99: "AudioDuration", + // 0x9a: "AudioOutcue", + // 0xc8: "PreviewFileFormat", + // 0xc9: "PreviewFileFormatVer", + // 0xca: "PreviewData" } loadImage.IptcMap.prototype.getText = function (id) { diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index b72db80..41e3406 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -10,7 +10,7 @@ * https://opensource.org/licenses/MIT */ -/* global define, Blob */ +/* global define */ ;(function (factory) { 'use strict' @@ -31,7 +31,7 @@ } loadImage.IptcMap.prototype.map = { - "ObjectName": 0x5 + 'ObjectName': 0x5 } loadImage.IptcMap.prototype.get = function (id) { @@ -44,30 +44,28 @@ sectionLength, data ) { - function getStringFromDB(buffer, start, length) { - var outstr = ""; - for (var n = start; n < start+length; n++) { - outstr += String.fromCharCode(buffer.getUint8(n)); + var outstr = "" + for (var n = start; n < start + length; n++) { + outstr += String.fromCharCode(buffer.getUint8(n)) } - return outstr; + return outstr } - var fieldValue, fieldName, dataSize, segmentType, segmentSize + var fieldValue, fieldName, dataSize, segmentType, var segmentStartPos = startOffset - while (segmentStartPos < startOffset+sectionLength) { + while (segmentStartPos < startOffset + sectionLength) { // we currently handle the 2: class of iptc tag - if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02) { + if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { - segmentType = dataView.getUint8(segmentStartPos+2) + segmentType = dataView.getUint8(segmentStartPos + 2) // only store data for known tags if (segmentType in data.iptc.tags) { - dataSize = dataView.getInt16(segmentStartPos+3) - segmentSize = dataSize + 5 + dataSize = dataView.getInt16(segmentStartPos + 3) fieldName = data.iptc.tags[segmentType] - fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize) + fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize) // integer field IDs (same as the exif module) fieldName=segmentType @@ -83,11 +81,11 @@ } } else { - data.iptc[fieldName] = fieldValue; + data.iptc[fieldName] = fieldValue } } } - segmentStartPos++; + segmentStartPos++ } } @@ -97,10 +95,10 @@ } // Found "8BIM" ? - var isFieldSegmentStart = function(dataView, offset){ + var isFieldSegmentStart = function (dataView, offset) { return ( dataView.getUint32(offset) === 0x3842494d && - dataView.getUint16(offset+4) === 0x0404 + dataView.getUint16(offset + 4) === 0x0404 ) } @@ -108,10 +106,8 @@ // Reference: https://metacpan.org/pod/distribution/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/Structures.pod#Structure-of-a-Photoshop-style-APP13-segment // From https://github.com/exif-js/exif-js/blob/master/exif.js ~ line 474 on - while (offset < offset+length) { - + while (offset < offset + length) { if (isFieldSegmentStart(dataView, offset)) { - var nameHeaderLength = dataView.getUint8(offset + 7) if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1 // Check for pre photoshop 6 format @@ -133,13 +129,8 @@ sectionLength, data ) - - break; - } - offset++ - } console.log('No Iptc data at this offset - could be XMP') From aea3493f25edb01f425abdd953f14254af0064bb Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:07:43 +0000 Subject: [PATCH 05/14] Switch quotes --- js/load-image-iptc-map.js | 114 +++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index ad21897..40805fa 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -34,63 +34,63 @@ // ========== // Iptc tags: // ========== - // 0x00: "RecordVersion", - 0x03: "ObjectType", - 0x04: "ObjectAttribute", - 0x05: "ObjectName", - 0x07: "EditStatus", - 0x08: "EditorialUpdate", - 0x0a: "Urgency", - 0x0c: "SubjectRef", - 0x0f: "Category", - 0x14: "SupplCategory", - 0x16: "FixtureID", - 0x19: "Keywords", - 0x1a: "ContentLocCode", - 0x1b: "ContentLocName", - 0x1e: "ReleaseDate", - 0x23: "ReleaseTime", - 0x25: "ExpirationDate", - 0x26: "ExpirationTime", - 0x28: "SpecialInstructions", - 0x2a: "ActionAdvised", - 0x2d: "RefService", - 0x2f: "RefDate", - 0x32: "RefNumber", - 0x37: "DateCreated", - 0x3c: "TimeCreated", - 0x3e: "DigitalCreationDate", - 0x3f: "DigitalCreationTime", - 0x41: "OriginatingProgram", - 0x46: "ProgramVersion", - 0x4b: "ObjectCycle", - 0x50: "Byline", - 0x55: "BylineTitle", - 0x5a: "City", - 0x5c: "Sublocation", - 0x5f: "State", - 0x64: "CountryCode", - 0x65: "CountryName", - 0x67: "OrigTransRef", - 0x69: "Headline", - 0x6e: "Credit", - 0x73: "Source", - 0x74: "CopyrightNotice", - 0x76: "Contact", - 0x78: "Caption", - 0x7a: "WriterEditor", - // 0x7d: "RasterizedCaption", - 0x82: "ImageType", - 0x83: "ImageOrientation", - 0x87: "LanguageID", - // 0x96: "AudioType", - // 0x97: "AudioSamplingRate", - // 0x98: "AudioSamplingRes", - // 0x99: "AudioDuration", - // 0x9a: "AudioOutcue", - // 0xc8: "PreviewFileFormat", - // 0xc9: "PreviewFileFormatVer", - // 0xca: "PreviewData" + // 0x00: 'RecordVersion', + 0x03: 'ObjectType', + 0x04: 'ObjectAttribute', + 0x05: 'ObjectName', + 0x07: 'EditStatus', + 0x08: 'EditorialUpdate', + 0x0a: 'Urgency', + 0x0c: 'SubjectRef', + 0x0f: 'Category', + 0x14: 'SupplCategory', + 0x16: 'FixtureID', + 0x19: 'Keywords', + 0x1a: 'ContentLocCode', + 0x1b: 'ContentLocName', + 0x1e: 'ReleaseDate', + 0x23: 'ReleaseTime', + 0x25: 'ExpirationDate', + 0x26: 'ExpirationTime', + 0x28: 'SpecialInstructions', + 0x2a: 'ActionAdvised', + 0x2d: 'RefService', + 0x2f: 'RefDate', + 0x32: 'RefNumber', + 0x37: 'DateCreated', + 0x3c: 'TimeCreated', + 0x3e: 'DigitalCreationDate', + 0x3f: 'DigitalCreationTime', + 0x41: 'OriginatingProgram', + 0x46: 'ProgramVersion', + 0x4b: 'ObjectCycle', + 0x50: 'Byline', + 0x55: 'BylineTitle', + 0x5a: 'City', + 0x5c: 'Sublocation', + 0x5f: 'State', + 0x64: 'CountryCode', + 0x65: 'CountryName', + 0x67: 'OrigTransRef', + 0x69: 'Headline', + 0x6e: 'Credit', + 0x73: 'Source', + 0x74: 'CopyrightNotice', + 0x76: 'Contact', + 0x78: 'Caption', + 0x7a: 'WriterEditor', + // 0x7d: 'RasterizedCaption', + 0x82: 'ImageType', + 0x83: 'ImageOrientation', + 0x87: 'LanguageID', + // 0x96: 'AudioType', + // 0x97: 'AudioSamplingRate', + // 0x98: 'AudioSamplingRes', + // 0x99: 'AudioDuration', + // 0x9a: 'AudioOutcue', + // 0xc8: 'PreviewFileFormat', + // 0xc9: 'PreviewFileFormatVer', + // 0xca: 'PreviewData' } loadImage.IptcMap.prototype.getText = function (id) { From d5d716ead8b4c4786ba03e48b788d15b89bfd3fb Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:08:28 +0000 Subject: [PATCH 06/14] Remove errant , --- js/load-image-iptc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index 41e3406..a0c2c16 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -52,7 +52,7 @@ return outstr } - var fieldValue, fieldName, dataSize, segmentType, + var fieldValue, fieldName, dataSize, segmentType var segmentStartPos = startOffset while (segmentStartPos < startOffset + sectionLength) { // we currently handle the 2: class of iptc tag From dd29b7a37d91ea2d1217847bd43cc8f4ebd3c293 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:10:40 +0000 Subject: [PATCH 07/14] Remove errant , and clean up record of IPTC fields we do not read --- js/load-image-iptc-map.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index 40805fa..d8c3803 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -34,7 +34,6 @@ // ========== // Iptc tags: // ========== - // 0x00: 'RecordVersion', 0x03: 'ObjectType', 0x04: 'ObjectAttribute', 0x05: 'ObjectName', @@ -79,10 +78,14 @@ 0x76: 'Contact', 0x78: 'Caption', 0x7a: 'WriterEditor', - // 0x7d: 'RasterizedCaption', 0x82: 'ImageType', 0x83: 'ImageOrientation', - 0x87: 'LanguageID', + 0x87: 'LanguageID' + + // We don't record these tags: + // + // 0x00: 'RecordVersion', + // 0x7d: 'RasterizedCaption', // 0x96: 'AudioType', // 0x97: 'AudioSamplingRate', // 0x98: 'AudioSamplingRes', From fca8eb543a19678a9625bbc3871894cee2e55cde Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:16:48 +0000 Subject: [PATCH 08/14] More Travis fixes --- js/load-image-iptc.js | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index a0c2c16..1398b6d 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -44,44 +44,36 @@ sectionLength, data ) { - function getStringFromDB(buffer, start, length) { - var outstr = "" + function getStringFromDB (buffer, start, length) { + var outstr = '' for (var n = start; n < start + length; n++) { - outstr += String.fromCharCode(buffer.getUint8(n)) + outstr += String.fromCharCode(buffer.getUint8(n)) } return outstr } - var fieldValue, fieldName, dataSize, segmentType + var fieldValue, dataSize, segmentType var segmentStartPos = startOffset while (segmentStartPos < startOffset + sectionLength) { // we currently handle the 2: class of iptc tag if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { - segmentType = dataView.getUint8(segmentStartPos + 2) // only store data for known tags if (segmentType in data.iptc.tags) { - dataSize = dataView.getInt16(segmentStartPos + 3) - fieldName = data.iptc.tags[segmentType] fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize) - // integer field IDs (same as the exif module) - fieldName=segmentType - // Check if we already stored a value with this name - if (data.iptc.hasOwnProperty(fieldName)) { + if (data.iptc.hasOwnProperty(segmentType)) { // Value already stored with this name, create multivalue field - if (data.iptc[fieldName] instanceof Array) { - data.iptc[fieldName].push(fieldValue) - } - else { - data.iptc[fieldName] = [data.iptc[fieldName], fieldValue] + if (data.iptc[segmentType] instanceof Array) { + data.iptc[segmentType].push(fieldValue) + } else { + data.iptc[segmentType] = [data.iptc[segmentType], fieldValue] } - } - else { - data.iptc[fieldName] = fieldValue + } else { + data.iptc[segmentType] = fieldValue } } } @@ -94,7 +86,7 @@ return } - // Found "8BIM" ? + // Found '8BIM' ? var isFieldSegmentStart = function (dataView, offset) { return ( dataView.getUint32(offset) === 0x3842494d && @@ -132,9 +124,7 @@ } offset++ } - console.log('No Iptc data at this offset - could be XMP') - } // Registers this Iptc parser for the APP13 JPEG meta data segment: From 28fa561299ee5d438bc24162c84b6582cad52350 Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Wed, 7 Nov 2018 23:23:13 +0000 Subject: [PATCH 09/14] Hopefully the final Travis fix! --- js/load-image-iptc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index 1398b6d..51a3d27 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -128,7 +128,7 @@ } // Registers this Iptc parser for the APP13 JPEG meta data segment: - loadImage.metaDataParsers.jpeg[0xffed]=[] + loadImage.metaDataParsers.jpeg[0xffed] = [] loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData) // Adds the following properties to the parseMetaData callback data: From f5734960c5e20338106e7c716c91cb351a270c1d Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Thu, 8 Nov 2018 08:41:06 +0000 Subject: [PATCH 10/14] Initial test update - add Iptc data (ObjectName 2:5) to the test image. --- test/test.js | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/test/test.js b/test/test.js index 873dc2e..2ed16a4 100644 --- a/test/test.js +++ b/test/test.js @@ -23,26 +23,21 @@ 'ovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7' var imageUrlGIF = 'data:image/gif;base64,' + b64DataGIF var blobGIF = canCreateBlob && window.dataURLtoBlob(imageUrlGIF) - // 2x1px JPEG (color white, with the Exif orientation flag set to 6): + // 2x1px JPEG (color white, with the Exif orientation flag set to 6 and Iptc ObjectName (2:5) set to 'objectname'): var b64DataJPEG = - '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAA' + - 'ABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEB' + - 'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' + - 'EBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEB' + - 'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' + - 'EBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEB' + - 'AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBA' + - 'QAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk' + - 'M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1' + - 'hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj' + - 'pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+' + - 'Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAA' + - 'AAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAx' + - 'EEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl' + - '8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2' + - 'hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq' + - 'srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8v' + - 'P09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q==' + '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAAAAAAD/' + + '7QAsUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAA8cAgUACm9iamVjdG5hbWUA/9sAQwABAQEBAQEB' + + 'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB' + + '/9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB' + + 'AQEBAQEBAQEBAQEB/8AAEQgAAQACAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgME' + + 'BQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEV' + + 'UtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3' + + 'eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh' + + '4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALUR' + + 'AAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDTh' + + 'JfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJ' + + 'ipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz' + + '9PX29/j5+v/aAAwDAQACEQMRAD8A/v4ooooA/9k=' var imageUrlJPEG = 'data:image/jpeg;base64,' + b64DataJPEG var blobJPEG = canCreateBlob && window.dataURLtoBlob(imageUrlJPEG) function createBlob (data, type) { From a1dcf71c4141e25cbf317a73edb4f366a294597e Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Thu, 8 Nov 2018 10:19:32 +0000 Subject: [PATCH 11/14] Add iptc files to min package, add Iptc to description and tags. --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c9fd26c..65dbbc4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "blueimp-load-image", "version": "2.19.0", "title": "JavaScript Load Image", - "description": "JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled and/or cropped HTML img or canvas element. It also provides a method to parse image meta data to extract Exif tags and thumbnails and to restore the complete image header after resizing.", + "description": "JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled and/or cropped HTML img or canvas element. It also provides methods to parse image meta data to extract Exif tags and thumbnails, Iptc tags and to restore the complete image header after resizing.", "keywords": [ "javascript", "load", @@ -17,6 +17,7 @@ "canvas", "meta", "exif", + "iptc", "thumbnail", "resizing" ], @@ -51,7 +52,7 @@ "mocha": "mocha-chrome http://127.0.0.1:$PORT/test", "unit": "PORT=$(get-port) concurrently -k -s first 'npm run serve' 'npm run mocha'", "test": "npm run lint && npm run unit", - "build": "cd js && uglifyjs load-image.js load-image-scale.js load-image-meta.js load-image-fetch.js load-image-exif.js load-image-exif-map.js load-image-orientation.js -c -m -o load-image.all.min.js --source-map url=load-image.all.min.js.map", + "build": "cd js && uglifyjs load-image.js load-image-scale.js load-image-meta.js load-image-fetch.js load-image-exif.js load-image-exif-map.js load-image-iptc.js load-image-iptc-map.js load-image-orientation.js -c -m -o load-image.all.min.js --source-map url=load-image.all.min.js.map", "preversion": "npm test", "version": "npm run build && git add -A js", "postversion": "git push --tags origin master master:gh-pages && npm publish" From aca612947715f4c76ccc8bb28a9e2d8d043cfb5a Mon Sep 17 00:00:00 2001 From: Dave Bevan Date: Thu, 8 Nov 2018 10:20:31 +0000 Subject: [PATCH 12/14] Include the new iptc parser files. --- test/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/index.html b/test/index.html index 7673322..df901ed 100644 --- a/test/index.html +++ b/test/index.html @@ -36,6 +36,8 @@ + +