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
+
+ Edit
+ Crop
+
+
+
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..d8c3803
--- /dev/null
+++ b/js/load-image-iptc-map.js
@@ -0,0 +1,130 @@
+/*
+ * 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
+ */
+
+/* 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:
+ // ==========
+ 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',
+ 0x82: 'ImageType',
+ 0x83: 'ImageOrientation',
+ 0x87: 'LanguageID'
+
+ // We don't record these tags:
+ //
+ // 0x00: 'RecordVersion',
+ // 0x7d: 'RasterizedCaption',
+ // 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..51a3d27
--- /dev/null
+++ b/js/load-image-iptc.js
@@ -0,0 +1,139 @@
+/*
+ * 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 */
+
+;(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, 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)
+ fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize)
+
+ // Check if we already stored a value with this name
+ if (data.iptc.hasOwnProperty(segmentType)) {
+ // Value already stored with this name, create multivalue field
+ if (data.iptc[segmentType] instanceof Array) {
+ data.iptc[segmentType].push(fieldValue)
+ } else {
+ data.iptc[segmentType] = [data.iptc[segmentType], fieldValue]
+ }
+ } else {
+ data.iptc[segmentType] = fieldValue
+ }
+ }
+ }
+ segmentStartPos++
+ }
+ }
+
+ loadImage.parseIptcData = function (dataView, offset, length, data, options) {
+ if (options.disableIptc) {
+ return
+ }
+
+ // Found '8BIM' ?
+ var isFieldSegmentStart = function (dataView, offset) {
+ return (
+ dataView.getUint32(offset) === 0x3842494d &&
+ dataView.getUint16(offset + 4) === 0x0404
+ )
+ }
+
+ // 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) {
+ 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 startOffset = offset + 8 + nameHeaderLength
+ var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength)
+
+ // Create the iptc object to store the tags:
+ data.iptc = new loadImage.IptcMap()
+
+ // Parse the tags
+ return loadImage.parseIptcTags(
+ dataView,
+ startOffset,
+ sectionLength,
+ data
+ )
+ }
+ offset++
+ }
+ console.log('No Iptc data at this offset - could be XMP')
+ }
+
+ // 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.
+})
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"
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 @@
+
+