From 7231af801c1e964fa19b15259c66cc19b4086dd9 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 29 Apr 2020 23:15:23 +0900 Subject: [PATCH 001/108] Correct JSDoc function argument name. --- 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 9f6a900..faf28a0 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -75,7 +75,7 @@ /** * Retrieves tag value for the given DataView and range * - * @param {number} tagCode Private IFD tag code + * @param {number} tagCode tag code * @param {IptcMap} map IPTC tag map * @param {DataView} dataView Data view interface * @param {number} offset Range start From 98de525a64082ccd76e7a0b6754f9a400a5108ed Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 01:29:23 +0900 Subject: [PATCH 002/108] Use Blob.slice directly instead of via a wrapper. --- js/load-image-meta.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index 21c9961..f677d2c 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -29,19 +29,12 @@ })(function (loadImage) { 'use strict' - var hasblobSlice = - typeof Blob !== 'undefined' && + loadImage.blobSlice = + loadImage.global.Blob && (Blob.prototype.slice || Blob.prototype.webkitSlice || Blob.prototype.mozSlice) - loadImage.blobSlice = - hasblobSlice && - function () { - var slice = this.slice || this.webkitSlice || this.mozSlice - return slice.apply(this, arguments) - } - loadImage.metaDataParsers = { jpeg: { 0xffe1: [], // APP1 marker From 2b1bf2f95a454d065e41c1c65f62ec8154f2d832 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 01:30:03 +0900 Subject: [PATCH 003/108] Check for DataView on the global object. --- js/load-image-meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index f677d2c..2c68a57 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -58,7 +58,7 @@ // 256 KiB should contain all EXIF/ICC/IPTC segments: var maxMetaDataSize = options.maxMetaDataSize || 262144 var noMetaData = !( - typeof DataView !== 'undefined' && + loadImage.global.DataView && file && file.size >= 12 && file.type === 'image/jpeg' && From 14fc0d3cb315cf6d9f17671db90546a3f5438900 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 01:35:30 +0900 Subject: [PATCH 004/108] Move buffer.slice polyfill into separate function. --- js/load-image-meta.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index 2c68a57..39b552c 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -35,6 +35,18 @@ Blob.prototype.webkitSlice || Blob.prototype.mozSlice) + loadImage.bufferSlice = + loadImage.global.ArrayBuffer.prototype.slice || + function (begin, end) { + // Polyfill for IE10, which does not support ArrayBuffer.slice + // eslint-disable-next-line no-param-reassign + end = end || this.byteLength - begin + var arr1 = new Uint8Array(this, begin, end) + var arr2 = new Uint8Array(end) + arr2.set(arr1) + return arr2.buffer + } + loadImage.metaDataParsers = { jpeg: { 0xffe1: [], // APP1 marker @@ -89,8 +101,6 @@ var markerLength var parsers var i - var arr1 - var arr2 // Check for the JPEG marker (0xffd8): if (dataView.getUint16(0) === 0xffd8) { while (offset < maxOffset) { @@ -136,16 +146,7 @@ // Meta length must be longer than JPEG marker (2) // plus APPn marker (2), followed by length bytes (2): if (!options.disableImageHead && headLength > 6) { - if (buffer.slice) { - data.imageHead = buffer.slice(0, headLength) - } else { - // Workaround for IE10, which does not support - // ArrayBuffer.slice: - arr1 = new Uint8Array(buffer, 0, headLength) - arr2 = new Uint8Array(headLength) - arr2.set(arr1) - data.imageHead = arr2.buffer - } + data.imageHead = loadImage.bufferSlice.call(buffer, 0, headLength) } } else { // eslint-disable-next-line no-console From efa4c7577b95198c0d4f4b633336c5e1de419c95 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 01:37:03 +0900 Subject: [PATCH 005/108] Use polyfill backed buffer.slice function. --- js/load-image-exif.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 7dff053..98d1624 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -82,9 +82,12 @@ console.log('Invalid Exif data: Invalid thumbnail data.') return } - return new Blob([dataView.buffer.slice(offset, offset + length)], { - type: 'image/jpeg' - }) + return new Blob( + [loadImage.bufferSlice.call(dataView.buffer, offset, offset + length)], + { + type: 'image/jpeg' + } + ) } var ExifTagTypes = { From 85353ae0d58e0c39627fb36e5fba1d9036b2261c Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 02:30:17 +0900 Subject: [PATCH 006/108] Add OffsetTime mappings from Exif version 2.31. --- js/load-image-exif-map.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/load-image-exif-map.js b/js/load-image-exif-map.js index 9684ae8..3ef16b9 100644 --- a/js/load-image-exif-map.js +++ b/js/load-image-exif-map.js @@ -79,6 +79,9 @@ 0xa004: 'RelatedSoundFile', // Name of related sound file 0x9003: 'DateTimeOriginal', // Date and time when the original image was generated 0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally + 0x9010: 'OffsetTime', // Time zone when the image file was last changed + 0x9011: 'OffsetTimeOriginal', // Time zone when the image was stored digitally + 0x9012: 'OffsetTimeDigitized', // Time zone when the image was stored digitally 0x9290: 'SubSecTime', // Fractions of seconds for DateTime 0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal 0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized From 8c76ddd736dda659234a0a32ecbb8f798fe96da9 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 04:40:57 +0900 Subject: [PATCH 007/108] Add NPM install instruction. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a13ac05..b7f2ad2 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,12 @@ complete image header after resizing. ## Setup +Install via [NPM](https://www.npmjs.com/): + +```sh +npm install blueimp-load-image +``` + Include the (combined and minified) JavaScript Load Image script in your HTML markup: From bb0a0fe9006bf469b00a74413d7b2ef41e8e087d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 04:44:31 +0900 Subject: [PATCH 008/108] Store IFD1 (Thumbnail) data in a separate ExifMap. Retrieving the Thumbnail Blob now requires another method call: https://github.com/blueimp/JavaScript-Load-Image/blob/master/README.md#exif-thumbnail Resolves #103. --- README.md | 23 ++++++++---- css/demo.css | 4 +- js/demo/demo.js | 22 +++++++---- js/load-image-exif-map.js | 15 +++++--- js/load-image-exif.js | 77 ++++++++++++++++++++++----------------- 5 files changed, 84 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index b7f2ad2..513dff7 100644 --- a/README.md +++ b/README.md @@ -533,11 +533,17 @@ Example code displaying a thumbnail image embedded into the Exif meta data: loadImage( fileOrBlobOrUrl, function (img, data) { - var thumbBlob = data.exif && data.exif.get('Thumbnail') - if (thumbBlob) { - loadImage(thumbBlob, function (thumbImage) { - document.body.appendChild(thumbImage) - }) + var exif = data.exif + var thumbnail = exif && exif.get('Thumbnail') + var blob = thumbnail.get('Blob') + if (blob) { + loadImage( + blob, + function (thumbImage) { + document.body.appendChild(thumbImage) + }, + { orientation: exif.get('Orientation') } + ) } }, { meta: true } @@ -610,7 +616,6 @@ loadImage( The Exif parser adds additional options: - `disableExif`: Disables Exif parsing when `true`. -- `disableExifThumbnail`: Disables parsing of Thumbnail data when `true`. - `disableExifOffsets`: Disables storing Exif tag offsets when `true`. - `includeExifTags`: A map of Exif tags to include for parsing (includes all but the excluded tags by default). @@ -628,8 +633,10 @@ loadImage.parseMetaData( { includeExifTags: { 0x0112: true, // Orientation - 0x0201: true, // JPEGInterchangeFormat (Thumbnail data offset) - 0x0202: true, // JPEGInterchangeFormatLength (Thumbnail data length) + ifd1: { + 0x0201: true, // JPEGInterchangeFormat (Thumbnail data offset) + 0x0202: true // JPEGInterchangeFormatLength (Thumbnail data length) + }, 0x8769: { // ExifIFDPointer 0x9000: true // ExifVersion diff --git a/css/demo.css b/css/demo.css index fb0f6a3..c2cc539 100644 --- a/css/demo.css +++ b/css/demo.css @@ -51,8 +51,10 @@ tr:nth-child(odd) { background: #eee; color: #222; } -td { +td, +th { padding: 10px; + text-align: left; } #result, #thumbnail { diff --git a/js/demo/demo.js b/js/demo/demo.js index d1ca79d..5330354 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -26,17 +26,20 @@ $(function () { * Displays tag data * * @param {*} node jQuery node - * @param {object} tags Tags object + * @param {object} tags Tags map + * @param {string} title Tags title */ - function displayTagData(node, tags) { + function displayTagData(node, tags, title) { var table = $('
') var row = $('') var cell = $('') + var headerCell = $('') var prop + table.append(row.clone().append(headerCell.clone().text(title))) for (prop in tags) { if (Object.prototype.hasOwnProperty.call(tags, prop)) { if (typeof tags[prop] === 'object') { - displayTagData(node, tags[prop]) + displayTagData(node, tags[prop], prop) continue } table.append( @@ -84,13 +87,16 @@ $(function () { var exif = data.exif var iptc = data.iptc if (exif) { - displayThumbnailImage(thumbNode, exif.get('Thumbnail'), { - orientation: exif.get('Orientation') - }) - displayTagData(exifNode, exif.getAll()) + var thumbnail = exif.get('Thumbnail') + if (thumbnail) { + displayThumbnailImage(thumbNode, thumbnail.get('Blob'), { + orientation: exif.get('Orientation') + }) + } + displayTagData(exifNode, exif.getAll(), 'TIFF') } if (iptc) { - displayTagData(iptcNode, iptc.getAll()) + displayTagData(iptcNode, iptc.getAll(), 'IPTC') } } diff --git a/js/load-image-exif-map.js b/js/load-image-exif-map.js index 3ef16b9..dbb18c4 100644 --- a/js/load-image-exif-map.js +++ b/js/load-image-exif-map.js @@ -180,6 +180,9 @@ } } + // IFD1 directory can contain any IFD0 tags: + ExifMapProto.tags.ifd1 = ExifMapProto.tags + ExifMapProto.stringValues = { ExposureProgram: { 0: 'Undefined', @@ -375,7 +378,7 @@ if (Object.prototype.hasOwnProperty.call(this, prop)) { obj = this[prop] if (obj && obj.getAll) { - map[this.privateIFDs[prop].name] = obj.getAll() + map[this.ifds[prop].name] = obj.getAll() } else { name = this.tags[prop] if (name) map[name] = this.getText(name) @@ -387,7 +390,7 @@ ExifMapProto.getName = function (tagCode) { var name = this.tags[tagCode] - if (typeof name === 'object') return this.privateIFDs[tagCode].name + if (typeof name === 'object') return this.ifds[tagCode].name return name } @@ -395,17 +398,17 @@ ;(function () { var tags = ExifMapProto.tags var prop - var privateIFD + var ifd var subTags // Map the tag names to tags: for (prop in tags) { if (Object.prototype.hasOwnProperty.call(tags, prop)) { - privateIFD = ExifMapProto.privateIFDs[prop] - if (privateIFD) { + ifd = ExifMapProto.ifds[prop] + if (ifd) { subTags = tags[prop] for (prop in subTags) { if (Object.prototype.hasOwnProperty.call(subTags, prop)) { - privateIFD.map[subTags[prop]] = Number(prop) + ifd.map[subTags[prop]] = Number(prop) } } } else { diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 98d1624..d61779e 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -32,12 +32,12 @@ * * @name ExifMap * @class - * @param {number} tagCode Private IFD tag code + * @param {number|string} tagCode IFD tag code */ function ExifMap(tagCode) { if (tagCode) { Object.defineProperty(this, 'map', { - value: this.privateIFDs[tagCode].map + value: this.ifds[tagCode].map }) Object.defineProperty(this, 'tags', { value: (this.tags && this.tags[tagCode]) || {} @@ -47,13 +47,15 @@ ExifMap.prototype.map = { Orientation: 0x0112, - Thumbnail: 0x0201, + Thumbnail: 'ifd1', + Blob: 0x0201, // Alias for JPEGInterchangeFormat Exif: 0x8769, GPSInfo: 0x8825, Interoperability: 0xa005 } - ExifMap.prototype.privateIFDs = { + ExifMap.prototype.ifds = { + ifd1: { name: 'Thumbnail', map: ExifMap.prototype.map }, 0x8769: { name: 'Exif', map: {} }, 0x8825: { name: 'GPSInfo', map: {} }, 0xa005: { name: 'Interoperability', map: {} } @@ -219,6 +221,21 @@ return values } + /** + * Determines if the given tag should be included. + * + * @param {object} includeTags Map of tags to include + * @param {object} excludeTags Map of tags to exclude + * @param {number|string} tagCode Tag code to check + * @returns {boolean} True if the tag should be included + */ + function shouldIncludeTag(includeTags, excludeTags, tagCode) { + return ( + (!includeTags || includeTags[tagCode]) && + (!excludeTags || excludeTags[tagCode] !== true) + ) + } + /** * Parses Exif tags. * @@ -256,8 +273,7 @@ for (i = 0; i < tagsNumber; i += 1) { tagOffset = dirOffset + 2 + 12 * i tagNumber = dataView.getUint16(tagOffset, littleEndian) - if (includeTags && !includeTags[tagNumber]) continue - if (excludeTags && excludeTags[tagNumber] === true) continue + if (!shouldIncludeTag(includeTags, excludeTags, tagNumber)) continue tagValue = getExifValue( dataView, tiffOffset, @@ -276,17 +292,17 @@ } /** - * Parses Private IFD tags. + * Parses tags in a given IFD (Image File Directory). * * @param {object} data Data object to store exif tags and offsets - * @param {number} tagCode Private IFD tag code + * @param {number|string} tagCode IFD tag code * @param {DataView} dataView Data view interface * @param {number} tiffOffset TIFF offset * @param {boolean} littleEndian Little endian encoding * @param {object} includeTags Map of tags to include * @param {object} excludeTags Map of tags to exclude */ - function parseExifPrivateIFD( + function parseExifIFD( data, tagCode, dataView, @@ -328,7 +344,7 @@ var tiffOffset = offset + 10 var littleEndian var dirOffset - var privateIFDs + var thumbnailIFD // Check for the ASCII code for "Exif" (0x45786966): if (dataView.getUint32(offset + 4) !== 0x45786966) { // No Exif data, might be XMP data instead @@ -369,8 +385,8 @@ data.exifTiffOffset = tiffOffset data.exifLittleEndian = littleEndian } - // Parse the tags of the main image directory and retrieve the - // offset to the next directory, usually the thumbnail directory: + // Parse the tags of the main image directory (IFD0) and retrieve the + // offset to the next directory (IFD1), usually the thumbnail directory: dirOffset = parseExifTags( dataView, tiffOffset, @@ -381,29 +397,14 @@ includeTags, excludeTags ) - if (dirOffset && !options.disableExifThumbnail) { - dirOffset = parseExifTags( - dataView, - tiffOffset, - tiffOffset + dirOffset, - littleEndian, - data.exif, - data.exifOffsets, - includeTags, - excludeTags - ) - // Check for JPEG Thumbnail offset: - if (data.exif[0x0201] && data.exif[0x0202]) { - data.exif[0x0201] = getExifThumbnail( - dataView, - tiffOffset + data.exif[0x0201], - data.exif[0x0202] // Thumbnail data length - ) + if (dirOffset && shouldIncludeTag(includeTags, excludeTags, 'ifd1')) { + data.exif.ifd1 = dirOffset + if (data.exifOffsets) { + data.exifOffsets.ifd1 = tiffOffset + dirOffset } } - privateIFDs = Object.keys(data.exif.privateIFDs) - privateIFDs.forEach(function (tagCode) { - parseExifPrivateIFD( + Object.keys(data.exif.ifds).forEach(function (tagCode) { + parseExifIFD( data, tagCode, dataView, @@ -413,6 +414,15 @@ excludeTags ) }) + thumbnailIFD = data.exif.ifd1 + // Check for JPEG Thumbnail offset and data length: + if (thumbnailIFD && thumbnailIFD[0x0201] && thumbnailIFD[0x0202]) { + thumbnailIFD[0x0201] = getExifThumbnail( + dataView, + tiffOffset + thumbnailIFD[0x0201], + thumbnailIFD[0x0202] // Thumbnail data length + ) + } } // Registers the Exif parser for the APP1 JPEG meta data segment: @@ -441,7 +451,6 @@ // Adds the following options to the parseMetaData method: // - disableExif: Disables Exif parsing when true. - // - disableExifThumbnail: Disables parsing of Thumbnail data when true. // - disableExifOffsets: Disables storing Exif tag offsets when true. // - includeExifTags: A map of Exif tags to include for parsing. // - excludeExifTags: A map of Exif tags to exclude from parsing. From 7320cc329b9e9beb6b2381ae0f034c6f6759bf7a Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 04:45:40 +0900 Subject: [PATCH 009/108] 5.0.0 --- js/load-image.all.min.js | 2 +- js/load-image.all.min.js.map | 2 +- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/load-image.all.min.js b/js/load-image.all.min.js index e3616c9..1fa4d62 100644 --- a/js/load-image.all.min.js +++ b/js/load-image.all.min.js @@ -1,2 +1,2 @@ -!function(n){"use strict";function s(i,a,n){var r,o=document.createElement("img");function e(e,t){t&&console.log(t),e&&s.isInstanceOf("Blob",e)?(i=e,r=s.createObjectURL(i)):(r=i,n&&n.crossOrigin&&(o.crossOrigin=n.crossOrigin)),o.src=r}return o.onerror=function(e){return s.onerror(o,e,i,r,a,n)},o.onload=function(e){return s.onload(o,e,i,r,a,n)},"string"==typeof i?(s.requiresMetaData(n)?s.fetchBlob(i,e,n):e(),o):s.isInstanceOf("Blob",i)||s.isInstanceOf("File",i)?(r=s.createObjectURL(i))?(o.src=r,o):s.readFile(i,function(e){var t=e.target;t&&t.result?o.src=t.result:a&&a(e)}):void 0}var t=n.createObjectURL&&n||n.URL&&URL.revokeObjectURL&&URL||n.webkitURL&&webkitURL;function o(e,t){!e||"blob:"!==e.slice(0,5)||t&&t.noRevoke||s.revokeObjectURL(e)}s.requiresMetaData=function(e){return e&&e.meta},s.fetchBlob=function(e,t){t()},s.isInstanceOf=function(e,t){return Object.prototype.toString.call(t)==="[object "+e+"]"},s.transform=function(e,t,i,a,n){i(e,n)},s.onerror=function(e,t,i,a,n,r){o(a,r),n&&n.call(e,t)},s.onload=function(e,t,i,a,n,r){o(a,r),n&&s.transform(e,r,n,i,{originalWidth:e.naturalWidth||e.width,originalHeight:e.naturalHeight||e.height})},s.createObjectURL=function(e){return!!t&&t.createObjectURL(e)},s.revokeObjectURL=function(e){return!!t&&t.revokeObjectURL(e)},s.readFile=function(e,t,i){if(n.FileReader){var a=new FileReader;if(a.onload=a.onerror=t,a[i=i||"readAsDataURL"])return a[i](e),a}return!1},s.global=n,"function"==typeof define&&define.amd?define(function(){return s}):"object"==typeof module&&module.exports?module.exports=s:n.loadImage=s}("undefined"!=typeof window&&window||this),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image"],e):"object"==typeof module&&module.exports?e(require("./load-image")):e(window.loadImage)}(function(x){"use strict";var r=x.transform;x.createCanvas=function(e,t,i){if(i&&x.global.OffscreenCanvas)return new OffscreenCanvas(e,t);var a=document.createElement("canvas");return a.width=e,a.height=t,a},x.transform=function(e,t,i,a,n){r.call(x,x.scale(e,t,n),t,i,a,n)},x.transformCoordinates=function(){},x.getTransformedOptions=function(e,t){var i,a,n,r,o=t.aspectRatio;if(!o)return t;for(a in i={},t)Object.prototype.hasOwnProperty.call(t,a)&&(i[a]=t[a]);return i.crop=!0,o<(n=e.naturalWidth||e.width)/(r=e.naturalHeight||e.height)?(i.maxWidth=r*o,i.maxHeight=r):(i.maxWidth=n,i.maxHeight=n/o),i},x.drawImage=function(e,t,i,a,n,r,o,s,c){var l=t.getContext("2d");return!1===c.imageSmoothingEnabled?(l.msImageSmoothingEnabled=!1,l.imageSmoothingEnabled=!1):c.imageSmoothingQuality&&(l.imageSmoothingQuality=c.imageSmoothingQuality),l.drawImage(e,i,a,n,r,0,0,o,s),l},x.requiresCanvas=function(e){return e.canvas||e.crop||!!e.aspectRatio},x.scale=function(e,t,i){t=t||{},i=i||{};var a,n,r,o,s,c,l,u,f,d,g,m,h=e.getContext||x.requiresCanvas(t)&&!!x.global.HTMLCanvasElement,p=e.naturalWidth||e.width,A=e.naturalHeight||e.height,b=p,S=A;function y(){var e=Math.max((r||b)/b,(o||S)/S);1c.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if((a=p.metaDataParsers.jpeg[t])&&!g.disableMetaDataParsers)for(n=0;ne.byteLength)){if(1===n)return d.getValue(e,s,r);for(c=[],l=0;le.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((l=i+2+12*(c=e.getUint16(i,a)))+4>e.byteLength)){for(u=0;ul.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===l.getUint16(e+8)){switch(l.getUint16(m)){case 18761:f=!0;break;case 19789:f=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===l.getUint16(m+2,f)?(a=l.getUint32(m+4,f),u.exif=new h,i.disableExifOffsets||(u.exifOffsets=new h,u.exifTiffOffset=m,u.exifLittleEndian=f),(a=p(l,m,m+a,f,u.exif,u.exifOffsets,d,g))&&!i.disableExifThumbnail&&(a=p(l,m,m+a,f,u.exif,u.exifOffsets,d,g),u.exif[513]&&u.exif[514]&&(u.exif[513]=function(e,t,i){if(i&&!(t+i>e.byteLength))return new Blob([e.buffer.slice(t,t+i)],{type:"image/jpeg"});console.log("Invalid Exif data: Invalid thumbnail data.")}(l,m+u.exif[513],u.exif[514]))),Object.keys(u.exif.privateIFDs).forEach(function(e){var t,i,a,n,r,o,s,c;i=e,a=l,n=m,r=f,o=d,s=g,(c=(t=u).exif[i])&&(t.exif[i]=new h(i),t.exifOffsets&&(t.exifOffsets[i]=new h(i)),p(a,n,n+c,r,t.exif[i],t.exifOffsets&&t.exifOffsets[i],o&&o[i],s&&s[i]))})):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},n.metaDataParsers.jpeg[65505].push(n.parseExifData),n.exifWriters={274:function(e,t,i){return new DataView(e,t.exifOffsets[274]+8,2).setUint16(0,i,t.exifLittleEndian),e}},n.writeExifData=function(e,t,i,a){n.exifWriters[t.exif.map[i]](e,t,a)},n.ExifMap=h}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";var n=e.ExifMap.prototype;n.tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",34665:{36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber"},34853:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},40965:{1:"InteroperabilityIndex"}},n.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},n.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},n.getAll=function(){var e,t,i,a={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&((t=this[e])&&t.getAll?a[this.privateIFDs[e].name]=t.getAll():(i=this.tags[e])&&(a[i]=this.getText(i)));return a},n.getName=function(e){var t=this.tags[e];return"object"==typeof t?this.privateIFDs[e].name:t},function(){var e,t,i,a=n.tags;for(e in a)if(Object.prototype.hasOwnProperty.call(a,e))if(t=n.privateIFDs[e])for(e in i=a[e])Object.prototype.hasOwnProperty.call(i,e)&&(t.map[i[e]]=Number(e));else n.map[a[e]]=Number(e)}()}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(e){"use strict";function g(){}function m(e,t,i,a,n){return"binary"===t.types[e]?new Blob([i.buffer.slice(a,a+n)]):"Uint16"===t.types[e]?i.getUint16(a):function(e,t,i){for(var a="",n=t+i,r=t;ro.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if((a=m.metaDataParsers.jpeg[t])&&!u.disableMetaDataParsers)for(n=0;ne.byteLength)){if(1===n)return d.getValue(e,s,r);for(l=[],c=0;ce.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((c=i+2+12*(l=e.getUint16(i,a)))+4>e.byteLength)){for(f=0;fc.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===c.getUint16(e+8)){switch(c.getUint16(m)){case 18761:u=!0;break;case 19789:u=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===c.getUint16(m+2,u)?(a=c.getUint32(m+4,u),f.exif=new h,i.disableExifOffsets||(f.exifOffsets=new h,f.exifTiffOffset=m,f.exifLittleEndian=u),(a=A(c,m,m+a,u,f.exif,f.exifOffsets,d,g))&&p(d,g,"ifd1")&&(f.exif.ifd1=a,f.exifOffsets&&(f.exifOffsets.ifd1=m+a)),Object.keys(f.exif.ifds).forEach(function(e){var t,i,a,n,r,o,s,l;i=e,a=c,n=m,r=u,o=d,s=g,(l=(t=f).exif[i])&&(t.exif[i]=new h(i),t.exifOffsets&&(t.exifOffsets[i]=new h(i)),A(a,n,n+l,r,t.exif[i],t.exifOffsets&&t.exifOffsets[i],o&&o[i],s&&s[i]))}),(n=f.exif.ifd1)&&n[513]&&n[514]&&(n[513]=function(e,t,i){if(i&&!(t+i>e.byteLength))return new Blob([r.bufferSlice.call(e.buffer,t,t+i)],{type:"image/jpeg"});console.log("Invalid Exif data: Invalid thumbnail data.")}(c,m+n[513],n[514]))):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},r.metaDataParsers.jpeg[65505].push(r.parseExifData),r.exifWriters={274:function(e,t,i){return new DataView(e,t.exifOffsets[274]+8,2).setUint16(0,i,t.exifLittleEndian),e}},r.writeExifData=function(e,t,i,a){r.exifWriters[t.exif.map[i]](e,t,a)},r.ExifMap=h}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";var n=e.ExifMap.prototype;n.tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",34665:{36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",36880:"OffsetTime",36881:"OffsetTimeOriginal",36882:"OffsetTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber"},34853:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},40965:{1:"InteroperabilityIndex"}},n.tags.ifd1=n.tags,n.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},n.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},n.getAll=function(){var e,t,i,a={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&((t=this[e])&&t.getAll?a[this.ifds[e].name]=t.getAll():(i=this.tags[e])&&(a[i]=this.getText(i)));return a},n.getName=function(e){var t=this.tags[e];return"object"==typeof t?this.ifds[e].name:t},function(){var e,t,i,a=n.tags;for(e in a)if(Object.prototype.hasOwnProperty.call(a,e))if(t=n.ifds[e])for(e in i=a[e])Object.prototype.hasOwnProperty.call(i,e)&&(t.map[i[e]]=Number(e));else n.map[a[e]]=Number(e)}()}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(e){"use strict";function g(){}function m(e,t,i,a,n){return"binary"===t.types[e]?new Blob([i.buffer.slice(a,a+n)]):"Uint16"===t.types[e]?i.getUint16(a):function(e,t,i){for(var a="",n=t+i,r=t;r Date: Thu, 30 Apr 2020 05:04:42 +0900 Subject: [PATCH 010/108] Documentation: Check if Thumbnail data exists. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 513dff7..5c3541c 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ loadImage( function (img, data) { var exif = data.exif var thumbnail = exif && exif.get('Thumbnail') - var blob = thumbnail.get('Blob') + var blob = thumbnail && thumbnail.get('Blob') if (blob) { loadImage( blob, From 6c86e7a7669a3270a030e74c3097756c0183c96b Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 05:06:34 +0900 Subject: [PATCH 011/108] 5.0.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index cab51de..b2f249e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "blueimp-load-image", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 32ce77d..9ce6d49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-load-image", - "version": "5.0.0", + "version": "5.0.1", "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 methods to parse image meta data to extract IPTC and Exif tags as well as embedded thumbnail images and to restore the complete image header after resizing.", "keywords": [ From 87c57fce1c6b315188c841f51c02d3bdacf69a8c Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 13:34:56 +0900 Subject: [PATCH 012/108] Documentation: Fix orientation text variable name. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c3541c..6755cd7 100644 --- a/README.md +++ b/README.md @@ -517,7 +517,7 @@ become available, as well as three additional methods: - `exif.getAll()` ```js -var flashText = data.exif.getText('Orientation') // e.g. right-top for value 6 +var orientationText = data.exif.getText('Orientation') // e.g. right-top var name = data.exif.getName(0x0112) // Orientation From c7a4d8952404f354e2f5cce51c53e6bd4c5c6bdf Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 13:42:04 +0900 Subject: [PATCH 013/108] Check if console is available (old browsers). --- js/load-image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/load-image.js b/js/load-image.js index 335ff70..9cb5046 100644 --- a/js/load-image.js +++ b/js/load-image.js @@ -34,7 +34,7 @@ * @param {Error} err Error object */ function fetchBlobCallback(blob, err) { - if (err) console.log(err) // eslint-disable-line no-console + if (err && $.console) console.log(err) // eslint-disable-line no-console if (blob && loadImage.isInstanceOf('Blob', blob)) { // eslint-disable-next-line no-param-reassign file = blob From 031f7ca3becc47118e756f05fb9bba5371f0664b Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 17:02:56 +0900 Subject: [PATCH 014/108] Refactor canvas tests into other test sections. --- test/test.js | 112 ++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/test/test.js b/test/test.js index 01e1086..a1290b0 100644 --- a/test/test.js +++ b/test/test.js @@ -150,9 +150,10 @@ expect(img.onerror).to.be.an.instanceOf(Function) }) - it('Load image url', function (done) { + it('Load image url as img element', function (done) { expect( loadImage(imageUrlGIF, function (img) { + expect(img.nodeName.toLowerCase()).to.equal('img') expect(img.width).to.equal(60) expect(img.height).to.equal(40) done() @@ -160,9 +161,10 @@ ).to.be.ok }) - it('Load image blob', function (done) { + it('Load image blob as img element', function (done) { expect( loadImage(blobGIF, function (img) { + expect(img.nodeName.toLowerCase()).to.equal('img') expect(img.width).to.equal(60) expect(img.height).to.equal(40) done() @@ -170,11 +172,11 @@ ).to.be.ok }) - it('Return image loading error to callback', function (done) { + it('Handle image loading error', function (done) { expect( - loadImage('404', function (img) { - expect(img).to.be.an.instanceOf(window.Event) - expect(img.type).to.equal('error') + loadImage('404', function (err) { + expect(err).to.be.an.instanceOf(window.Event) + expect(err.type).to.equal('error') done() }) ).to.be.ok @@ -190,6 +192,20 @@ ).to.be.ok }) + it('Load image as canvas for canvas: true', function (done) { + expect( + loadImage( + imageUrlGIF, + function (img) { + expect(img.getContext).to.be.an.instanceOf(Function) + expect(img.nodeName.toLowerCase()).to.equal('canvas') + done() + }, + { canvas: true } + ) + ).to.be.ok + }) + describe('ObjectURL revoke', function () { // Using XMLHttpRequest via the request helper function to test Object // URLs to work around Edge Legacy and IE caching image URLs. @@ -431,6 +447,28 @@ ) ).to.be.ok }) + + it('Accept a canvas element as source image', function (done) { + expect( + loadImage( + blobGIF, + function (img) { + expect(img.getContext).to.be.an.instanceOf(Function) + expect(img.nodeName.toLowerCase()).to.equal('canvas') + // eslint-disable-next-line no-param-reassign + img = loadImage.scale(img, { + maxWidth: 30 + }) + expect(img.getContext).to.be.an.instanceOf(Function) + expect(img.nodeName.toLowerCase()).to.equal('canvas') + expect(img.width).to.equal(30) + expect(img.height).to.equal(20) + done() + }, + { canvas: true } + ) + ).to.be.ok + }) }) describe('contain', function () { @@ -2235,68 +2273,6 @@ }) }) - describe('Canvas', function () { - it('Return img element to callback if canvas is not true', function (done) { - expect( - loadImage(blobGIF, function (img) { - expect(img.getContext).to.be.undefined - expect(img.nodeName.toLowerCase()).to.equal('img') - done() - }) - ).to.be.ok - }) - - it('Return canvas element to callback if canvas is true', function (done) { - expect( - loadImage( - blobGIF, - function (img) { - expect(img.getContext).to.be.an.instanceOf(Function) - expect(img.nodeName.toLowerCase()).to.equal('canvas') - done() - }, - { canvas: true } - ) - ).to.be.ok - }) - - it('Return scaled canvas element to callback', function (done) { - expect( - loadImage( - blobGIF, - function (img) { - expect(img.getContext).to.be.an.instanceOf(Function) - expect(img.nodeName.toLowerCase()).to.equal('canvas') - expect(img.width).to.equal(30) - expect(img.height).to.equal(20) - done() - }, - { canvas: true, maxWidth: 30 } - ) - ).to.be.ok - }) - - it('Accept a canvas element as parameter for loadImage.scale', function (done) { - expect( - loadImage( - blobGIF, - function (img) { - // eslint-disable-next-line no-param-reassign - img = loadImage.scale(img, { - maxWidth: 30 - }) - expect(img.getContext).to.be.an.instanceOf(Function) - expect(img.nodeName.toLowerCase()).to.equal('canvas') - expect(img.width).to.equal(30) - expect(img.height).to.equal(20) - done() - }, - { canvas: true } - ) - ).to.be.ok - }) - }) - describe('Metadata', function () { it('Parse EXIF tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { From 48e60c130ea2064f89f160d0310666e1f2e51852 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 17:37:42 +0900 Subject: [PATCH 015/108] Only run meta data tests if APIs are available. --- test/test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test.js b/test/test.js index a1290b0..ca6ad6f 100644 --- a/test/test.js +++ b/test/test.js @@ -2274,6 +2274,8 @@ }) describe('Metadata', function () { + if (!window.DataView || !loadImage.blobSlice) return + it('Parse EXIF tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exif).to.be.ok From 2989bda436cae93199edf81cab426c8cad7de468 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 17:40:26 +0900 Subject: [PATCH 016/108] Test API availability via property access. --- js/load-image-fetch.js | 11 +++++------ test/test.js | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/js/load-image-fetch.js b/js/load-image-fetch.js index 7aabda1..122b360 100644 --- a/js/load-image-fetch.js +++ b/js/load-image-fetch.js @@ -25,7 +25,9 @@ })(function (loadImage) { 'use strict' - if (typeof fetch !== 'undefined' && typeof Request !== 'undefined') { + var global = loadImage.global + + if (global.fetch && global.Request) { loadImage.fetchBlob = function (url, callback, options) { fetch(new Request(url, options)) .then(function (response) { @@ -36,11 +38,8 @@ callback(null, err) }) } - } else if ( - // Check for XHR2 support: - typeof XMLHttpRequest !== 'undefined' && - typeof ProgressEvent !== 'undefined' - ) { + } else if (global.XMLHttpRequest && global.ProgressEvent) { + // Browser supports XHR Level 2 and XHR responseType blob loadImage.fetchBlob = function (url, callback, options) { // eslint-disable-next-line no-param-reassign options = options || {} diff --git a/test/test.js b/test/test.js index ca6ad6f..4503ddc 100644 --- a/test/test.js +++ b/test/test.js @@ -2518,8 +2518,8 @@ describe('Fetch', function () { if ( - !('fetch' in window && 'Request' in window) && - !('XMLHttpRequest' in window && 'ProgressEvent' in window) + !(window.fetch && window.Request) && + !(window.XMLHttpRequest && window.ProgressEvent) ) { return } From 0a1a61fb98393d4509218fda7f1bdb8316f14c9f Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 18:20:18 +0900 Subject: [PATCH 017/108] Avoid camel case for test section descriptions. --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 4503ddc..e441167 100644 --- a/test/test.js +++ b/test/test.js @@ -206,7 +206,7 @@ ).to.be.ok }) - describe('ObjectURL revoke', function () { + describe('Object URL revoke', function () { // Using XMLHttpRequest via the request helper function to test Object // URLs to work around Edge Legacy and IE caching image URLs. if (!window.XMLHttpRequest) return From bc66fda8228683f56baecb9dbcb5da8498d0321d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 30 Apr 2020 21:09:22 +0900 Subject: [PATCH 018/108] Demo: Display meta data in a unified section. --- index.html | 7 ++----- js/demo/demo.js | 10 ++++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index e499f04..45f6f28 100644 --- a/index.html +++ b/index.html @@ -116,13 +116,10 @@

Result

API.

-