From ef6f1eb9fcf3e7a4bd3476897fe5f2a563bd6d03 Mon Sep 17 00:00:00 2001 From: Donald Date: Wed, 25 Mar 2020 23:21:47 +0800 Subject: [PATCH] fixed: file size may increase in some situation --- dist/browser-image-compression.d.ts | 25 +++++++++++++++---------- dist/browser-image-compression.js | 2 +- dist/browser-image-compression.js.map | 2 +- dist/browser-image-compression.mjs | 2 +- dist/browser-image-compression.mjs.map | 2 +- example/basic.html | 4 ++-- example/development.html | 4 ++-- lib/image-compression.js | 21 +++++++++++++-------- lib/utils.js | 2 +- 9 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dist/browser-image-compression.d.ts b/dist/browser-image-compression.d.ts index 790ae12..94ff80d 100644 --- a/dist/browser-image-compression.d.ts +++ b/dist/browser-image-compression.d.ts @@ -1,5 +1,9 @@ -declare module 'browser-image-compression' { - interface Options { +// Type definitions for browser-image-compression 1.0 +// Project: https://github.com/Donaldcwl/browser-image-compression +// Definitions by: Donald +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +interface Options { /** @default Number.POSITIVE_INFINITY */ maxSizeMB?: number; /** @default undefined */ @@ -7,16 +11,17 @@ declare module 'browser-image-compression' { /** @default false */ useWebWorker?: boolean; /** @default 10 */ - maxIteration?: number, + maxIteration?: number; /** Default to be the exif orientation from the image file */ - exifOrientation?: number, + exifOrientation?: number; /** A function takes one progress argument (progress from 0 to 100) */ - onProgress?: (progress: number) => void, + onProgress?: (progress: number) => void; /** Default to be the original mime type from the image file */ - fileType?: string - } + fileType?: string; +} - function imageCompression (image: Blob, options: Options): Promise; +declare function imageCompression(image: File | Blob, options: Options): Promise; - export = imageCompression; -} +export as namespace imageCompression; + +export = imageCompression; diff --git a/dist/browser-image-compression.js b/dist/browser-image-compression.js index 1808b19..ac3af47 100644 --- a/dist/browser-image-compression.js +++ b/dist/browser-image-compression.js @@ -5,5 +5,5 @@ * https://github.com/Donaldcwl/browser-image-compression */ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).imageCompression=n()}(this,(function(){"use strict";function _defineProperty(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function ownKeys(e,n){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),r.push.apply(r,t)}return r}function _objectSpread2(e){for(var n=1;n2&&void 0!==arguments[2]?arguments[2]:Date.now();return new Promise((function(t){for(var o=e.split(","),i=o[0].match(/:(.*?);/)[1],a=atob(o[1]),s=a.length,c=new Uint8Array(s);s--;)c[s]=a.charCodeAt(s);var l=new Blob([c],{type:i});l.name=n,l.lastModified=r,t(l)}))}function loadImage(e){return new Promise((function(n,r){var t=new Image;t.onload=function(){return n(t)},t.onerror=function(e){return r(e)},t.src=e}))}function drawImageInCanvas(e){var n=_slicedToArray(getNewCanvasAndCtx(e.width,e.height),2),r=n[0];return n[1].drawImage(e,0,0,r.width,r.height),r}function drawFileInCanvas(e){return new Promise((function(n,r){var t,o,i=function $Try_1_Post(){try{return o=drawImageInCanvas(t),n([t,o])}catch(e){return r(e)}},a=function $Try_1_Catch(n){try{return getDataUrlFromFile(e).then((function(e){try{return loadImage(e).then((function(e){try{return t=e,i()}catch(e){return r(e)}}),r)}catch(e){return r(e)}}),r)}catch(e){return r(e)}};try{return createImageBitmap(e).then((function(e){try{return t=e,i()}catch(e){return a()}}),a)}catch(e){a()}}))}function canvasToFile(e,n,r,t){var o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1;return new Promise((function(i,a){var s;return"function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas?e.convertToBlob({type:n,quality:o}).then(function(e){try{return(s=e).name=r,s.lastModified=t,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a):getFilefromDataUrl(e.toDataURL(n,o),r,t).then(function(e){try{return s=e,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a);function $If_4(){return i(s)}}))}function getExifOrientation(e){return new Promise((function(n,r){var t=new CustomFileReader;t.onload=function(e){var r=new DataView(e.target.result);if(65496!=r.getUint16(0,!1))return n(-2);for(var t=r.byteLength,o=2;oi||o>i)){var s=_slicedToArray(getNewCanvasAndCtx(t,o),2);a=s[0],r=s[1],t>o?(a.width=i,a.height=o/t*i):(a.width=t/o*i,a.height=i),r.drawImage(e,0,0,a.width,a.height),cleanupCanvasMemory(e)}return a}function followExifOrientation(e,n){var r=e.width,t=e.height,o=_slicedToArray(getNewCanvasAndCtx(r,t),2),i=o[0],a=o[1];switch(40&&void 0!==arguments[0]?arguments[0]:5;o+=e,"function"==typeof n.onProgress&&n.onProgress(Math.min(o,100))}function setProgress(e){o=Math.min(Math.max(e,o),100),"function"==typeof n.onProgress&&n.onProgress(o)}return o=0,i=n.maxIteration||10,a=1024*n.maxSizeMB*1024,incProgress(),drawFileInCanvas(e).then(function(o){try{var y=_slicedToArray(o,2);return y[0],s=y[1],incProgress(),c=handleMaxWidthOrHeight(s,n),incProgress(),new Promise((function(r,t){var o;if(!(o=n.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return t(e)}}.bind(this),t);function $If_2(){return r(o)}return $If_2.call(this)})).then(function(o){try{return n.exifOrientation=o,incProgress(),l=followExifOrientation(c,n.exifOrientation),incProgress(),u=1,canvasToFile(l,n.fileType||e.type,e.name,e.lastModified,u).then(function(o){try{{if(f=o,incProgress(),f.size<=a)return setProgress(100),r(f);var y;function $Loop_3(){if(i--&&g>a){var r,o,s=_slicedToArray(getNewCanvasAndCtx(r=.9*h.width,o=.9*h.height),2);return d=s[0],s[1].drawImage(h,0,0,r,o),"image/jpeg"===e.type&&(u*=.9),canvasToFile(d,n.fileType||e.type,e.name,e.lastModified,u).then((function(e){try{return p=e,cleanupCanvasMemory(h),h=d,g=p.size,setProgress(Math.min(99,Math.floor((m-g)/(m-a)*100))),$Loop_3}catch(e){return t(e)}}),t)}return[1]}return m=f.size,g=m,h=l,(y=function(e){for(;e;){if(e.then)return void e.then(y,t);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3}else e=e.call(this)}catch(e){return t(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(h),cleanupCanvasMemory(d),cleanupCanvasMemory(c),cleanupCanvasMemory(l),cleanupCanvasMemory(s),setProgress(100),r(p)}}}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}))}e&&(Number.isInteger=Number.isInteger||function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e});var r,t,o=0;function generateLib(){return function createSourceObject(e){return URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}("\n function imageCompression (){return (".concat(imageCompression,").apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ").concat(imageCompression.getDataUrlFromFile,"\n imageCompression.getFilefromDataUrl = ").concat(imageCompression.getFilefromDataUrl,"\n imageCompression.loadImage = ").concat(imageCompression.loadImage,"\n imageCompression.drawImageInCanvas = ").concat(imageCompression.drawImageInCanvas,"\n imageCompression.drawFileInCanvas = ").concat(imageCompression.drawFileInCanvas,"\n imageCompression.canvasToFile = ").concat(imageCompression.canvasToFile,"\n imageCompression.getExifOrientation = ").concat(imageCompression.getExifOrientation,"\n imageCompression.handleMaxWidthOrHeight = ").concat(imageCompression.handleMaxWidthOrHeight,"\n imageCompression.followExifOrientation = ").concat(imageCompression.followExifOrientation,"\n imageCompression.cleanupMemory = ").concat(imageCompression.cleanupMemory,"\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ").concat(getNewCanvasAndCtx,"\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (").concat(compress,").apply(null, arguments)}\n "))}function generateWorkerScript(){return function createWorker(e){return"function"==typeof e&&(e="(".concat(f,")()")),new Worker(URL.createObjectURL(new Blob([e])))}("\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n })\n ")}function imageCompression(e,n){return new Promise((function(i,a){var s,c,l;if(n.maxSizeMB=n.maxSizeMB||Number.POSITIVE_INFINITY,c="boolean"==typeof n.useWebWorker&&n.useWebWorker,delete n.useWebWorker,!(e instanceof Blob||e instanceof CustomFile))return a(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return a(new Error("The file given is not an image"));if(l="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!c||"function"!=typeof Worker||l)return compress(e,n).then(function(e){try{return s=e,$If_3.call(this)}catch(e){return a(e)}}.bind(this),a);var u=function(){try{return $If_3.call(this)}catch(e){return a(e)}}.bind(this),f=function $Try_1_Catch(r){try{return compress(e,n).then((function(e){try{return s=e,u()}catch(e){return a(e)}}),a)}catch(e){return a(e)}};try{return function compressOnWebWorker(e,n){return new Promise((function(i,a){return new Promise((function(s,c){var l=o++;return r||(r=generateLib()),t||(t=generateWorkerScript()),t.addEventListener("message",(function handler(e){if(e.data.id===l){if(void 0!==e.data.progress&&e.data.progress<100)return void n.onProgress(e.data.progress);t.removeEventListener("message",handler),e.data.error&&a(new Error(e.data.error)),i(e.data.file)}})),t.postMessage({file:e,id:l,imageCompressionLibUrl:r,options:_objectSpread2({},n,{onProgress:void 0})}),s()}))}))}(e,n).then((function(e){try{return s=e,u()}catch(e){return f()}}),f)}catch(e){f()}function $If_3(){try{s.name=e.name,s.lastModified=e.lastModified}catch(e){}return i(s)}}))}return imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupMemory=cleanupCanvasMemory,imageCompression.version="1.0.8",imageCompression})); +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).imageCompression=n()}(this,(function(){"use strict";function _defineProperty(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function ownKeys(e,n){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),r.push.apply(r,t)}return r}function _objectSpread2(e){for(var n=1;n2&&void 0!==arguments[2]?arguments[2]:Date.now();return new Promise((function(t){for(var i=e.split(","),o=i[0].match(/:(.*?);/)[1],a=atob(i[1]),s=a.length,c=new Uint8Array(s);s--;)c[s]=a.charCodeAt(s);var l=new Blob([c],{type:o});l.name=n,l.lastModified=r,t(l)}))}function loadImage(e){return new Promise((function(n,r){var t=new Image;t.onload=function(){return n(t)},t.onerror=function(e){return r(e)},t.src=e}))}function drawImageInCanvas(e){var n=_slicedToArray(getNewCanvasAndCtx(e.width,e.height),2),r=n[0];return n[1].drawImage(e,0,0,r.width,r.height),r}function drawFileInCanvas(e){return new Promise((function(n,r){var t,i,o=function $Try_1_Post(){try{return i=drawImageInCanvas(t),n([t,i])}catch(e){return r(e)}},a=function $Try_1_Catch(n){try{return getDataUrlFromFile(e).then((function(e){try{return loadImage(e).then((function(e){try{return t=e,o()}catch(e){return r(e)}}),r)}catch(e){return r(e)}}),r)}catch(e){return r(e)}};try{return createImageBitmap(e).then((function(e){try{return t=e,o()}catch(e){return a()}}),a)}catch(e){a()}}))}function canvasToFile(e,n,r,t){var i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1;return new Promise((function(o,a){var s;return"function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas?e.convertToBlob({type:n,quality:i}).then(function(e){try{return(s=e).name=r,s.lastModified=t,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a):getFilefromDataUrl(e.toDataURL(n,i),r,t).then(function(e){try{return s=e,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a);function $If_4(){return o(s)}}))}function getExifOrientation(e){return new Promise((function(n,r){var t=new CustomFileReader;t.onload=function(e){var r=new DataView(e.target.result);if(65496!=r.getUint16(0,!1))return n(-2);for(var t=r.byteLength,i=2;io||i>o)){var s=_slicedToArray(getNewCanvasAndCtx(t,i),2);a=s[0],r=s[1],t>i?(a.width=o,a.height=i/t*o):(a.width=t/i*o,a.height=o),r.drawImage(e,0,0,a.width,a.height),cleanupCanvasMemory(e)}return a}function followExifOrientation(e,n){var r=e.width,t=e.height,i=_slicedToArray(getNewCanvasAndCtx(r,t),2),o=i[0],a=i[1];switch(40&&void 0!==arguments[0]?arguments[0]:5;i+=e,"function"==typeof n.onProgress&&n.onProgress(Math.min(i,100))}function setProgress(e){i=Math.min(Math.max(e,i),100),"function"==typeof n.onProgress&&n.onProgress(i)}return i=0,o=n.maxIteration||10,a=1024*n.maxSizeMB*1024,incProgress(),drawFileInCanvas(e).then(function(i){try{var C=_slicedToArray(i,2);return C[0],s=C[1],incProgress(),c=handleMaxWidthOrHeight(s,n),incProgress(),new Promise((function(r,t){var i;if(!(i=n.exifOrientation))return getExifOrientation(e).then(function(e){try{return i=e,$If_2.call(this)}catch(e){return t(e)}}.bind(this),t);function $If_2(){return r(i)}return $If_2.call(this)})).then(function(i){try{return n.exifOrientation=i,incProgress(),l=followExifOrientation(c,n.exifOrientation),incProgress(),u=1,canvasToFile(l,n.fileType||e.type,e.name,e.lastModified,u).then(function(i){try{{if(f=i,incProgress(),m=f.size>a,g=f.size>e.size,!m&&!g)return setProgress(100),r(f);var C;function $Loop_3(){if(o--&&(h>a||h>d)){var r,i,s=_slicedToArray(getNewCanvasAndCtx(r=m?.95*w.width:w.width,i=m?.95*w.height:w.height),2);return v=s[0],s[1].drawImage(w,0,0,r,i),"image/jpeg"===e.type&&(u*=.95),canvasToFile(v,n.fileType||e.type,e.name,e.lastModified,u).then((function(e){try{return y=e,cleanupCanvasMemory(w),w=v,h=y.size,setProgress(Math.min(99,Math.floor((p-h)/(p-a)*100))),$Loop_3}catch(e){return t(e)}}),t)}return[1]}return d=e.size,p=f.size,h=p,w=l,(C=function(e){for(;e;){if(e.then)return void e.then(C,t);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3}else e=e.call(this)}catch(e){return t(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(w),cleanupCanvasMemory(v),cleanupCanvasMemory(c),cleanupCanvasMemory(l),cleanupCanvasMemory(s),setProgress(100),r(y)}}}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}))}e&&(Number.isInteger=Number.isInteger||function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e});var r,t,i=0;function generateLib(){return function createSourceObject(e){return URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}("\n function imageCompression (){return (".concat(imageCompression,").apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ").concat(imageCompression.getDataUrlFromFile,"\n imageCompression.getFilefromDataUrl = ").concat(imageCompression.getFilefromDataUrl,"\n imageCompression.loadImage = ").concat(imageCompression.loadImage,"\n imageCompression.drawImageInCanvas = ").concat(imageCompression.drawImageInCanvas,"\n imageCompression.drawFileInCanvas = ").concat(imageCompression.drawFileInCanvas,"\n imageCompression.canvasToFile = ").concat(imageCompression.canvasToFile,"\n imageCompression.getExifOrientation = ").concat(imageCompression.getExifOrientation,"\n imageCompression.handleMaxWidthOrHeight = ").concat(imageCompression.handleMaxWidthOrHeight,"\n imageCompression.followExifOrientation = ").concat(imageCompression.followExifOrientation,"\n imageCompression.cleanupMemory = ").concat(imageCompression.cleanupMemory,"\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ").concat(getNewCanvasAndCtx,"\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (").concat(compress,").apply(null, arguments)}\n "))}function generateWorkerScript(){return function createWorker(e){return"function"==typeof e&&(e="(".concat(f,")()")),new Worker(URL.createObjectURL(new Blob([e])))}("\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n })\n ")}function imageCompression(e,n){return new Promise((function(o,a){var s,c,l;if(n.maxSizeMB=n.maxSizeMB||Number.POSITIVE_INFINITY,c="boolean"==typeof n.useWebWorker&&n.useWebWorker,delete n.useWebWorker,!(e instanceof Blob||e instanceof CustomFile))return a(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return a(new Error("The file given is not an image"));if(l="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!c||"function"!=typeof Worker||l)return compress(e,n).then(function(e){try{return s=e,$If_3.call(this)}catch(e){return a(e)}}.bind(this),a);var u=function(){try{return $If_3.call(this)}catch(e){return a(e)}}.bind(this),f=function $Try_1_Catch(r){try{return compress(e,n).then((function(e){try{return s=e,u()}catch(e){return a(e)}}),a)}catch(e){return a(e)}};try{return function compressOnWebWorker(e,n){return new Promise((function(o,a){return new Promise((function(s,c){var l=i++;return r||(r=generateLib()),t||(t=generateWorkerScript()),t.addEventListener("message",(function handler(e){if(e.data.id===l){if(void 0!==e.data.progress&&e.data.progress<100)return void n.onProgress(e.data.progress);t.removeEventListener("message",handler),e.data.error&&a(new Error(e.data.error)),o(e.data.file)}})),t.postMessage({file:e,id:l,imageCompressionLibUrl:r,options:_objectSpread2({},n,{onProgress:void 0})}),s()}))}))}(e,n).then((function(e){try{return s=e,u()}catch(e){return f()}}),f)}catch(e){f()}function $If_3(){try{s.name=e.name,s.lastModified=e.lastModified}catch(e){}return o(s)}}))}return imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupMemory=cleanupCanvasMemory,imageCompression.version="1.0.8",imageCompression})); //# sourceMappingURL=browser-image-compression.js.map diff --git a/dist/browser-image-compression.js.map b/dist/browser-image-compression.js.map index 9cab7fd..e99b9f9 100644 --- a/dist/browser-image-compression.js.map +++ b/dist/browser-image-compression.js.map @@ -1 +1 @@ -{"version":3,"file":"browser-image-compression.js","sources":["../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["const isBrowser = typeof window !== 'undefined' // change browser environment to support SSR\n\n// add support for cordova-plugin-file\nconst moduleMapper = isBrowser && window.cordova && window.cordova.require && window.cordova.require('cordova/modulemapper')\nexport const CustomFile = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'File')) || File)\nexport const CustomFileReader = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'FileReader')) || FileReader)\n\n/**\n * getDataUrlFromFile\n *\n * @param {File} file\n * @returns {Promise}\n */\nexport function getDataUrlFromFile (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = () => resolve(reader.result)\n reader.onerror = (e) => reject(e)\n reader.readAsDataURL(file)\n })\n}\n\n/**\n * getFilefromDataUrl\n *\n * @param {string} dataurl\n * @param {string} filename\n * @param {number} [lastModified=Date.now()]\n * @returns {Promise}\n */\nexport function getFilefromDataUrl (dataurl, filename, lastModified = Date.now()) {\n return new Promise((resolve) => {\n const arr = dataurl.split(',')\n const mime = arr[0].match(/:(.*?);/)[1]\n const bstr = atob(arr[1])\n let n = bstr.length\n const u8arr = new Uint8Array(n)\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n)\n }\n const file = new Blob([u8arr], { type: mime })\n file.name = filename\n file.lastModified = lastModified\n resolve(file)\n\n // Safari has issue with File constructor not being able to POST in FormData\n // https://github.com/Donaldcwl/browser-image-compression/issues/8\n // https://bugs.webkit.org/show_bug.cgi?id=165081\n // let file\n // try {\n // file = new File([u8arr], filename, { type: mime }) // Edge do not support File constructor\n // } catch (e) {\n // file = new Blob([u8arr], { type: mime })\n // file.name = filename\n // file.lastModified = lastModified\n // }\n // resolve(file)\n })\n}\n\n/**\n * loadImage\n *\n * @param {string} src\n * @returns {Promise}\n */\nexport function loadImage (src) {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = (e) => reject(e)\n img.src = src\n })\n}\n\n/**\n * drawImageInCanvas\n *\n * @param {HTMLImageElement} img\n * @returns {HTMLCanvasElement}\n */\nexport function drawImageInCanvas (img) {\n const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\n/**\n * drawFileInCanvas\n *\n * @param {File} file\n * @returns {Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement]>}\n */\nexport async function drawFileInCanvas (file) {\n let img\n try {\n img = await createImageBitmap(file)\n } catch (e) {\n const dataUrl = await getDataUrlFromFile(file)\n img = await loadImage(dataUrl)\n }\n const canvas = drawImageInCanvas(img)\n return [img, canvas]\n}\n\n/**\n * canvasToFile\n *\n * @param canvas\n * @param {string} fileType\n * @param {string} fileName\n * @param {number} fileLastModified\n * @param {number} [quality]\n * @returns {Promise}\n */\nexport async function canvasToFile (canvas, fileType, fileName, fileLastModified, quality = 1) {\n let file\n if (typeof OffscreenCanvas === 'function' && canvas instanceof OffscreenCanvas) {\n file = await canvas.convertToBlob({ type: fileType, quality })\n file.name = fileName\n file.lastModified = fileLastModified\n } else {\n const dataUrl = canvas.toDataURL(fileType, quality)\n file = await getFilefromDataUrl(dataUrl, fileName, fileLastModified)\n }\n return file\n}\n\n/**\n * getExifOrientation\n * get image exif orientation info\n * source: https://stackoverflow.com/a/32490603/10395024\n *\n * @param {File} file\n * @returns {Promise} - orientation id, see https://i.stack.imgur.com/VGsAj.gif\n */\nexport function getExifOrientation (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = (e) => {\n const view = new DataView(e.target.result)\n if (view.getUint16(0, false) != 0xFFD8) {\n return resolve(-2)\n }\n const length = view.byteLength\n let offset = 2\n while (offset < length) {\n if (view.getUint16(offset + 2, false) <= 8) return resolve(-1)\n const marker = view.getUint16(offset, false)\n offset += 2\n if (marker == 0xFFE1) {\n if (view.getUint32(offset += 2, false) != 0x45786966) {\n return resolve(-1)\n }\n\n var little = view.getUint16(offset += 6, false) == 0x4949\n offset += view.getUint32(offset + 4, little)\n var tags = view.getUint16(offset, little)\n offset += 2\n for (var i = 0; i < tags; i++) {\n if (view.getUint16(offset + (i * 12), little) == 0x0112) {\n return resolve(view.getUint16(offset + (i * 12) + 8, little))\n }\n }\n } else if ((marker & 0xFF00) != 0xFF00) {\n break\n } else {\n offset += view.getUint16(offset, false)\n }\n }\n return resolve(-1)\n }\n reader.onerror = (e) => reject(e)\n reader.readAsArrayBuffer(file)\n })\n}\n\n/**\n *\n * @param {HTMLCanvasElement} canvas\n * @param options\n * @returns {HTMLCanvasElement>}\n */\nexport function handleMaxWidthOrHeight (canvas, options) {\n const width = canvas.width\n const height = canvas.height\n const maxWidthOrHeight = options.maxWidthOrHeight\n\n const needToHandle = Number.isInteger(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight)\n\n let newCanvas = canvas\n let ctx\n\n if (needToHandle) {\n [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n if (width > height) {\n newCanvas.width = maxWidthOrHeight\n newCanvas.height = (height / width) * maxWidthOrHeight\n } else {\n newCanvas.width = (width / height) * maxWidthOrHeight\n newCanvas.height = maxWidthOrHeight\n }\n ctx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height)\n\n cleanupCanvasMemory(canvas)\n }\n\n return newCanvas\n}\n\n/**\n * followExifOrientation\n * source: https://stackoverflow.com/a/40867559/10395024\n *\n * @param {HTMLCanvasElement} canvas\n * @param {number} exifOrientation\n * @returns {HTMLCanvasElement} canvas\n */\nexport function followExifOrientation (canvas, exifOrientation) {\n const width = canvas.width\n const height = canvas.height\n\n const [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n\n // set proper canvas dimensions before transform & export\n if (4 < exifOrientation && exifOrientation < 9) {\n newCanvas.width = height\n newCanvas.height = width\n } else {\n newCanvas.width = width\n newCanvas.height = height\n }\n\n // transform context before drawing image\n switch (exifOrientation) {\n case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;\n case 3: ctx.transform(-1, 0, 0, -1, width, height); break;\n case 4: ctx.transform(1, 0, 0, -1, 0, height); break;\n case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;\n case 6: ctx.transform(0, 1, -1, 0, height, 0); break;\n case 7: ctx.transform(0, -1, -1, 0, height, width); break;\n case 8: ctx.transform(0, -1, 1, 0, 0, width); break;\n default: break;\n }\n\n ctx.drawImage(canvas, 0, 0, width, height)\n\n cleanupCanvasMemory(canvas)\n\n return newCanvas\n}\n\n/**\n * get new Canvas and it's context\n * @param width\n * @param height\n * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}\n */\nexport function getNewCanvasAndCtx (width, height) {\n let canvas\n let ctx\n try {\n canvas = new OffscreenCanvas(width, height)\n ctx = canvas.getContext('2d')\n if (ctx === null) {\n throw new Error('getContext of OffscreenCanvas returns null')\n }\n } catch (e) {\n canvas = document.createElement('canvas')\n ctx = canvas.getContext('2d')\n }\n canvas.width = width\n canvas.height = height\n return [canvas, ctx]\n}\n\n/**\n * clear Canvas memory\n * @param canvas\n * @returns null\n */\nexport function cleanupCanvasMemory (canvas) {\n canvas.width = 0\n canvas.height = 0\n}\n\nif (isBrowser) {\n// Polyfill for Number.isInteger\n Number.isInteger = Number.isInteger || function (value) {\n return typeof value === 'number' &&\n isFinite(value) &&\n Math.floor(value) === value\n }\n}\n","import {\n canvasToFile,\n cleanupCanvasMemory,\n drawFileInCanvas,\n followExifOrientation,\n getExifOrientation,\n getNewCanvasAndCtx,\n handleMaxWidthOrHeight\n} from './utils'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nexport default async function compress (file, options) {\n let progress = 0\n\n function incProgress (inc = 5) {\n progress += inc\n if (typeof options.onProgress === 'function') {\n options.onProgress(Math.min(progress, 100))\n }\n }\n\n function setProgress (p) {\n progress = Math.min(Math.max(p, progress), 100)\n if (typeof options.onProgress === 'function') {\n options.onProgress(progress)\n }\n }\n\n let remainingTrials = options.maxIteration || 10\n\n const maxSizeByte = options.maxSizeMB * 1024 * 1024\n\n incProgress()\n\n // drawFileInCanvas\n let [img, origCanvas] = await drawFileInCanvas(file)\n\n incProgress()\n\n // handleMaxWidthOrHeight\n const maxWidthOrHeightFixedCanvas = handleMaxWidthOrHeight(origCanvas, options)\n\n incProgress()\n\n // exifOrientation\n options.exifOrientation = options.exifOrientation || await getExifOrientation(file)\n incProgress()\n const orientationFixedCanvas = followExifOrientation(maxWidthOrHeightFixedCanvas, options.exifOrientation)\n incProgress()\n\n let quality = 1\n\n let tempFile = await canvasToFile(orientationFixedCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n incProgress()\n // check if we need to compress or resize\n if (tempFile.size <= maxSizeByte) {\n // no need to compress\n setProgress(100)\n return tempFile\n }\n\n const originalSize = tempFile.size\n let currentSize = originalSize\n let compressedFile\n let newCanvas, ctx\n let canvas = orientationFixedCanvas\n while (remainingTrials-- && currentSize > maxSizeByte) {\n const newWidth = canvas.width * 0.9\n const newHeight = canvas.height * 0.9;\n [newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight)\n\n ctx.drawImage(canvas, 0, 0, newWidth, newHeight)\n\n if (file.type === 'image/jpeg') {\n quality *= 0.9\n }\n compressedFile = await canvasToFile(newCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n\n cleanupCanvasMemory(canvas)\n\n canvas = newCanvas\n\n currentSize = compressedFile.size\n setProgress(Math.min(99, Math.floor((originalSize - currentSize) / (originalSize - maxSizeByte) * 100)))\n }\n\n // garbage clean canvas for safari\n // ref: https://bugs.webkit.org/show_bug.cgi?id=195325\n cleanupCanvasMemory(canvas)\n cleanupCanvasMemory(newCanvas)\n cleanupCanvasMemory(maxWidthOrHeightFixedCanvas)\n cleanupCanvasMemory(orientationFixedCanvas)\n cleanupCanvasMemory(origCanvas)\n\n setProgress(100)\n return compressedFile\n}\n","import lib from './index'\nimport compress from './image-compression'\nimport { getNewCanvasAndCtx } from './utils'\n\nlet cnt = 0\nlet imageCompressionLibUrl\nlet worker\n\nfunction createWorker (script) {\n if (typeof script === 'function') {\n script = `(${f})()`\n }\n return new Worker(URL.createObjectURL(new Blob([script])))\n}\n\nfunction createSourceObject (str) {\n return URL.createObjectURL(new Blob([str], { type: 'application/javascript' }))\n}\n\nfunction generateLib () {\n // prepare the lib to be used inside WebWorker\n return createSourceObject(`\n function imageCompression (){return (${lib}).apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ${lib.getDataUrlFromFile}\n imageCompression.getFilefromDataUrl = ${lib.getFilefromDataUrl}\n imageCompression.loadImage = ${lib.loadImage}\n imageCompression.drawImageInCanvas = ${lib.drawImageInCanvas}\n imageCompression.drawFileInCanvas = ${lib.drawFileInCanvas}\n imageCompression.canvasToFile = ${lib.canvasToFile}\n imageCompression.getExifOrientation = ${lib.getExifOrientation}\n imageCompression.handleMaxWidthOrHeight = ${lib.handleMaxWidthOrHeight}\n imageCompression.followExifOrientation = ${lib.followExifOrientation}\n imageCompression.cleanupMemory = ${lib.cleanupMemory}\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ${getNewCanvasAndCtx}\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (${compress}).apply(null, arguments)}\n `)\n}\n\nfunction generateWorkerScript () {\n // code to be run in the WebWorker\n return createWorker(`\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\\\n' + e.stack, id })\n }\n })\n `)\n}\n\nexport function compressOnWebWorker (file, options) {\n return new Promise(async (resolve, reject) => {\n let id = cnt++\n\n if (!imageCompressionLibUrl) {\n imageCompressionLibUrl = generateLib()\n }\n\n if (!worker) {\n worker = generateWorkerScript()\n }\n\n function handler (e) {\n if (e.data.id === id) {\n if (e.data.progress !== undefined && e.data.progress < 100) {\n options.onProgress(e.data.progress)\n return\n }\n worker.removeEventListener('message', handler)\n if (e.data.error) {\n reject(new Error(e.data.error))\n }\n resolve(e.data.file)\n }\n }\n\n worker.addEventListener('message', handler)\n worker.postMessage({\n file,\n id,\n imageCompressionLibUrl,\n options: { ...options, onProgress: undefined }\n })\n })\n}\n","import compress from './image-compression'\nimport {\n canvasToFile,\n drawFileInCanvas,\n drawImageInCanvas,\n getDataUrlFromFile,\n getFilefromDataUrl,\n loadImage,\n getExifOrientation,\n handleMaxWidthOrHeight,\n followExifOrientation,\n CustomFile,\n cleanupCanvasMemory\n} from './utils'\nimport { compressOnWebWorker } from './web-worker'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {boolean} [options.useWebWorker=false]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nasync function imageCompression (file, options) {\n\n let compressedFile\n\n options.maxSizeMB = options.maxSizeMB || Number.POSITIVE_INFINITY\n const useWebWorker = typeof options.useWebWorker === 'boolean' ? options.useWebWorker : false\n delete options.useWebWorker\n\n if (!(file instanceof Blob || file instanceof CustomFile)) {\n throw new Error('The file given is not an instance of Blob or File')\n } else if (!/^image/.test(file.type)) {\n throw new Error('The file given is not an image')\n }\n\n // try run in web worker, fall back to run in main thread\n const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope\n\n // if ((useWebWorker && typeof Worker === 'function') || inWebWorker) {\n // console.log('run compression in web worker')\n // } else {\n // console.log('run compression in main thread')\n // }\n\n if (useWebWorker && typeof Worker === 'function' && !inWebWorker) {\n try {\n // console.log(1)\n // \"compressOnWebWorker\" is kind of like a recursion to call \"imageCompression\" again inside web worker\n compressedFile = await compressOnWebWorker(file, options)\n } catch (e) {\n // console.warn('Run compression in web worker failed:', e, ', fall back to main thread')\n // console.log(1.5)\n compressedFile = await compress(file, options)\n }\n } else {\n // console.log(2)\n compressedFile = await compress(file, options)\n }\n\n try {\n compressedFile.name = file.name\n compressedFile.lastModified = file.lastModified\n } catch (e) {}\n\n return compressedFile\n}\n\nimageCompression.getDataUrlFromFile = getDataUrlFromFile\nimageCompression.getFilefromDataUrl = getFilefromDataUrl\nimageCompression.loadImage = loadImage\nimageCompression.drawImageInCanvas = drawImageInCanvas\nimageCompression.drawFileInCanvas = drawFileInCanvas\nimageCompression.canvasToFile = canvasToFile\nimageCompression.getExifOrientation = getExifOrientation\nimageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight\nimageCompression.followExifOrientation = followExifOrientation\nimageCompression.cleanupMemory = cleanupCanvasMemory\nimageCompression.version = '1.0.8'\n\nexport default imageCompression\n"],"names":["isBrowser","window","moduleMapper","getDataUrlFromFile","resolve","reader","dataurl","split","n","bstr","filename","file","lastModified","canvas","view","length","offset","getUint32","marker","maxWidthOrHeight","width","followExifOrientation","height","ctx","inc","drawFileInCanvas","exifOrientation","maxWidthOrHeightFixedCanvas","imageCompressionLibUrl","cnt","generateLib","script","imageCompression","getFilefromDataUrl","loadImage","drawImageInCanvas","canvasToFile","getExifOrientation"],"mappings":";;;;;;;m/CAAA,IAAMA,EAA8B,oBAAlBC,2EAKoF,uCAQ5DC,2HAiBnC,SAASC,mDACPC,6DAEqBC,uNAiCdC,EAAAC,mCACK,gBAEjBC,EAAIC,6BAEJD,8DAUYE,EACdC,EAAAC,eACAR,28BAwC2CS,qcAoB7CC,mEAEMV,mCAGQW,6DAEgBC,8GASpBF,EAAAG,UAAgBD,0GAKPA,EAAA,iCAGTE,0KA+BVC,qJAqBuBC,4CAQTP,EAAV,0CAEAA,qBASYQ,yCACZC,mPAmBuCA,wJAI9B,sHA8BbC,2BAHWD,4ZC3OTE,iLAPK,6DAUGC,iBAARd,yKAUAe,kRAGSC,yrCCjEX,IACIC,IADAC,IAcJ,SAASC,okEAPPC,gkDCyDwBpB,0zBAa1BqB,iBAAA7B,mBAAsCA,mBACtC6B,iBAAAC,sCACAD,iBAAAE,oBACAF,iBAAAG,oCACAH,iBAAAP,kCAEAO,iBAAAI,0BAxFAJ,iBAAAK"} \ No newline at end of file +{"version":3,"file":"browser-image-compression.js","sources":["../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["const isBrowser = typeof window !== 'undefined' // change browser environment to support SSR\n\n// add support for cordova-plugin-file\nconst moduleMapper = isBrowser && window.cordova && window.cordova.require && window.cordova.require('cordova/modulemapper')\nexport const CustomFile = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'File')) || File)\nexport const CustomFileReader = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'FileReader')) || FileReader)\n\n/**\n * getDataUrlFromFile\n *\n * @param {File} file\n * @returns {Promise}\n */\nexport function getDataUrlFromFile (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = () => resolve(reader.result)\n reader.onerror = (e) => reject(e)\n reader.readAsDataURL(file)\n })\n}\n\n/**\n * getFilefromDataUrl\n *\n * @param {string} dataurl\n * @param {string} filename\n * @param {number} [lastModified=Date.now()]\n * @returns {Promise}\n */\nexport function getFilefromDataUrl (dataurl, filename, lastModified = Date.now()) {\n return new Promise((resolve) => {\n const arr = dataurl.split(',')\n const mime = arr[0].match(/:(.*?);/)[1]\n const bstr = atob(arr[1])\n let n = bstr.length\n const u8arr = new Uint8Array(n)\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n)\n }\n const file = new Blob([u8arr], { type: mime })\n file.name = filename\n file.lastModified = lastModified\n resolve(file)\n\n // Safari has issue with File constructor not being able to POST in FormData\n // https://github.com/Donaldcwl/browser-image-compression/issues/8\n // https://bugs.webkit.org/show_bug.cgi?id=165081\n // let file\n // try {\n // file = new File([u8arr], filename, { type: mime }) // Edge do not support File constructor\n // } catch (e) {\n // file = new Blob([u8arr], { type: mime })\n // file.name = filename\n // file.lastModified = lastModified\n // }\n // resolve(file)\n })\n}\n\n/**\n * loadImage\n *\n * @param {string} src\n * @returns {Promise}\n */\nexport function loadImage (src) {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = (e) => reject(e)\n img.src = src\n })\n}\n\n/**\n * drawImageInCanvas\n *\n * @param {HTMLImageElement} img\n * @returns {HTMLCanvasElement}\n */\nexport function drawImageInCanvas (img) {\n const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\n/**\n * drawFileInCanvas\n *\n * @param {File} file\n * @returns {Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement]>}\n */\nexport async function drawFileInCanvas (file) {\n let img\n try {\n img = await createImageBitmap(file)\n } catch (e) {\n const dataUrl = await getDataUrlFromFile(file)\n img = await loadImage(dataUrl)\n }\n const canvas = drawImageInCanvas(img)\n return [img, canvas]\n}\n\n/**\n * canvasToFile\n *\n * @param canvas\n * @param {string} fileType\n * @param {string} fileName\n * @param {number} fileLastModified\n * @param {number} [quality]\n * @returns {Promise}\n */\nexport async function canvasToFile (canvas, fileType, fileName, fileLastModified, quality = 1) {\n let file\n if (typeof OffscreenCanvas === 'function' && canvas instanceof OffscreenCanvas) {\n file = await canvas.convertToBlob({ type: fileType, quality })\n file.name = fileName\n file.lastModified = fileLastModified\n } else {\n const dataUrl = canvas.toDataURL(fileType, quality)\n file = await getFilefromDataUrl(dataUrl, fileName, fileLastModified)\n }\n return file\n}\n\n/**\n * getExifOrientation\n * get image exif orientation info\n * source: https://stackoverflow.com/a/32490603/10395024\n *\n * @param {File} file\n * @returns {Promise} - orientation id, see https://i.stack.imgur.com/VGsAj.gif\n */\nexport function getExifOrientation (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = (e) => {\n const view = new DataView(e.target.result)\n if (view.getUint16(0, false) != 0xFFD8) {\n return resolve(-2)\n }\n const length = view.byteLength\n let offset = 2\n while (offset < length) {\n if (view.getUint16(offset + 2, false) <= 8) return resolve(-1)\n const marker = view.getUint16(offset, false)\n offset += 2\n if (marker == 0xFFE1) {\n if (view.getUint32(offset += 2, false) != 0x45786966) {\n return resolve(-1)\n }\n\n var little = view.getUint16(offset += 6, false) == 0x4949\n offset += view.getUint32(offset + 4, little)\n var tags = view.getUint16(offset, little)\n offset += 2\n for (var i = 0; i < tags; i++) {\n if (view.getUint16(offset + (i * 12), little) == 0x0112) {\n return resolve(view.getUint16(offset + (i * 12) + 8, little))\n }\n }\n } else if ((marker & 0xFF00) != 0xFF00) {\n break\n } else {\n offset += view.getUint16(offset, false)\n }\n }\n return resolve(-1)\n }\n reader.onerror = (e) => reject(e)\n reader.readAsArrayBuffer(file)\n })\n}\n\n/**\n *\n * @param {HTMLCanvasElement} canvas\n * @param options\n * @returns {HTMLCanvasElement>}\n */\nexport function handleMaxWidthOrHeight (canvas, options) {\n const width = canvas.width\n const height = canvas.height\n const maxWidthOrHeight = options.maxWidthOrHeight\n\n const needToHandle = Number.isFinite(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight)\n\n let newCanvas = canvas\n let ctx\n\n if (needToHandle) {\n [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n if (width > height) {\n newCanvas.width = maxWidthOrHeight\n newCanvas.height = (height / width) * maxWidthOrHeight\n } else {\n newCanvas.width = (width / height) * maxWidthOrHeight\n newCanvas.height = maxWidthOrHeight\n }\n ctx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height)\n\n cleanupCanvasMemory(canvas)\n }\n\n return newCanvas\n}\n\n/**\n * followExifOrientation\n * source: https://stackoverflow.com/a/40867559/10395024\n *\n * @param {HTMLCanvasElement} canvas\n * @param {number} exifOrientation\n * @returns {HTMLCanvasElement} canvas\n */\nexport function followExifOrientation (canvas, exifOrientation) {\n const width = canvas.width\n const height = canvas.height\n\n const [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n\n // set proper canvas dimensions before transform & export\n if (4 < exifOrientation && exifOrientation < 9) {\n newCanvas.width = height\n newCanvas.height = width\n } else {\n newCanvas.width = width\n newCanvas.height = height\n }\n\n // transform context before drawing image\n switch (exifOrientation) {\n case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;\n case 3: ctx.transform(-1, 0, 0, -1, width, height); break;\n case 4: ctx.transform(1, 0, 0, -1, 0, height); break;\n case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;\n case 6: ctx.transform(0, 1, -1, 0, height, 0); break;\n case 7: ctx.transform(0, -1, -1, 0, height, width); break;\n case 8: ctx.transform(0, -1, 1, 0, 0, width); break;\n default: break;\n }\n\n ctx.drawImage(canvas, 0, 0, width, height)\n\n cleanupCanvasMemory(canvas)\n\n return newCanvas\n}\n\n/**\n * get new Canvas and it's context\n * @param width\n * @param height\n * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}\n */\nexport function getNewCanvasAndCtx (width, height) {\n let canvas\n let ctx\n try {\n canvas = new OffscreenCanvas(width, height)\n ctx = canvas.getContext('2d')\n if (ctx === null) {\n throw new Error('getContext of OffscreenCanvas returns null')\n }\n } catch (e) {\n canvas = document.createElement('canvas')\n ctx = canvas.getContext('2d')\n }\n canvas.width = width\n canvas.height = height\n return [canvas, ctx]\n}\n\n/**\n * clear Canvas memory\n * @param canvas\n * @returns null\n */\nexport function cleanupCanvasMemory (canvas) {\n canvas.width = 0\n canvas.height = 0\n}\n\nif (isBrowser) {\n// Polyfill for Number.isInteger\n Number.isInteger = Number.isInteger || function (value) {\n return typeof value === 'number' &&\n isFinite(value) &&\n Math.floor(value) === value\n }\n}\n","import {\n canvasToFile,\n cleanupCanvasMemory,\n drawFileInCanvas,\n followExifOrientation,\n getExifOrientation,\n getNewCanvasAndCtx,\n handleMaxWidthOrHeight\n} from './utils'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nexport default async function compress (file, options) {\n let progress = 0\n\n function incProgress (inc = 5) {\n progress += inc\n if (typeof options.onProgress === 'function') {\n options.onProgress(Math.min(progress, 100))\n }\n }\n\n function setProgress (p) {\n progress = Math.min(Math.max(p, progress), 100)\n if (typeof options.onProgress === 'function') {\n options.onProgress(progress)\n }\n }\n\n let remainingTrials = options.maxIteration || 10\n\n const maxSizeByte = options.maxSizeMB * 1024 * 1024\n\n incProgress()\n\n // drawFileInCanvas\n let [img, origCanvas] = await drawFileInCanvas(file)\n\n incProgress()\n\n // handleMaxWidthOrHeight\n const maxWidthOrHeightFixedCanvas = handleMaxWidthOrHeight(origCanvas, options)\n\n incProgress()\n\n // exifOrientation\n options.exifOrientation = options.exifOrientation || await getExifOrientation(file)\n incProgress()\n const orientationFixedCanvas = followExifOrientation(maxWidthOrHeightFixedCanvas, options.exifOrientation)\n incProgress()\n\n let quality = 1\n\n let tempFile = await canvasToFile(orientationFixedCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n incProgress()\n\n const origExceedMaxSize = tempFile.size > maxSizeByte\n const sizeBecomeLarger = tempFile.size > file.size\n\n // check if we need to compress or resize\n if (!origExceedMaxSize && !sizeBecomeLarger) {\n // no need to compress\n setProgress(100)\n return tempFile\n }\n\n const sourceSize = file.size\n const renderedSize = tempFile.size\n let currentSize = renderedSize\n let compressedFile\n let newCanvas, ctx\n let canvas = orientationFixedCanvas\n while (remainingTrials-- && (currentSize > maxSizeByte || currentSize > sourceSize)) {\n const newWidth = origExceedMaxSize ? canvas.width * 0.95 : canvas.width\n const newHeight = origExceedMaxSize ? canvas.height * 0.95 : canvas.height;\n [newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight)\n\n ctx.drawImage(canvas, 0, 0, newWidth, newHeight)\n\n if (file.type === 'image/jpeg') {\n quality *= 0.95\n }\n compressedFile = await canvasToFile(newCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n\n cleanupCanvasMemory(canvas)\n\n canvas = newCanvas\n\n currentSize = compressedFile.size\n setProgress(Math.min(99, Math.floor((renderedSize - currentSize) / (renderedSize - maxSizeByte) * 100)))\n }\n\n // garbage clean canvas for safari\n // ref: https://bugs.webkit.org/show_bug.cgi?id=195325\n cleanupCanvasMemory(canvas)\n cleanupCanvasMemory(newCanvas)\n cleanupCanvasMemory(maxWidthOrHeightFixedCanvas)\n cleanupCanvasMemory(orientationFixedCanvas)\n cleanupCanvasMemory(origCanvas)\n\n setProgress(100)\n return compressedFile\n}\n","import lib from './index'\nimport compress from './image-compression'\nimport { getNewCanvasAndCtx } from './utils'\n\nlet cnt = 0\nlet imageCompressionLibUrl\nlet worker\n\nfunction createWorker (script) {\n if (typeof script === 'function') {\n script = `(${f})()`\n }\n return new Worker(URL.createObjectURL(new Blob([script])))\n}\n\nfunction createSourceObject (str) {\n return URL.createObjectURL(new Blob([str], { type: 'application/javascript' }))\n}\n\nfunction generateLib () {\n // prepare the lib to be used inside WebWorker\n return createSourceObject(`\n function imageCompression (){return (${lib}).apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ${lib.getDataUrlFromFile}\n imageCompression.getFilefromDataUrl = ${lib.getFilefromDataUrl}\n imageCompression.loadImage = ${lib.loadImage}\n imageCompression.drawImageInCanvas = ${lib.drawImageInCanvas}\n imageCompression.drawFileInCanvas = ${lib.drawFileInCanvas}\n imageCompression.canvasToFile = ${lib.canvasToFile}\n imageCompression.getExifOrientation = ${lib.getExifOrientation}\n imageCompression.handleMaxWidthOrHeight = ${lib.handleMaxWidthOrHeight}\n imageCompression.followExifOrientation = ${lib.followExifOrientation}\n imageCompression.cleanupMemory = ${lib.cleanupMemory}\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ${getNewCanvasAndCtx}\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (${compress}).apply(null, arguments)}\n `)\n}\n\nfunction generateWorkerScript () {\n // code to be run in the WebWorker\n return createWorker(`\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\\\n' + e.stack, id })\n }\n })\n `)\n}\n\nexport function compressOnWebWorker (file, options) {\n return new Promise(async (resolve, reject) => {\n let id = cnt++\n\n if (!imageCompressionLibUrl) {\n imageCompressionLibUrl = generateLib()\n }\n\n if (!worker) {\n worker = generateWorkerScript()\n }\n\n function handler (e) {\n if (e.data.id === id) {\n if (e.data.progress !== undefined && e.data.progress < 100) {\n options.onProgress(e.data.progress)\n return\n }\n worker.removeEventListener('message', handler)\n if (e.data.error) {\n reject(new Error(e.data.error))\n }\n resolve(e.data.file)\n }\n }\n\n worker.addEventListener('message', handler)\n worker.postMessage({\n file,\n id,\n imageCompressionLibUrl,\n options: { ...options, onProgress: undefined }\n })\n })\n}\n","import compress from './image-compression'\nimport {\n canvasToFile,\n drawFileInCanvas,\n drawImageInCanvas,\n getDataUrlFromFile,\n getFilefromDataUrl,\n loadImage,\n getExifOrientation,\n handleMaxWidthOrHeight,\n followExifOrientation,\n CustomFile,\n cleanupCanvasMemory\n} from './utils'\nimport { compressOnWebWorker } from './web-worker'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {boolean} [options.useWebWorker=false]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nasync function imageCompression (file, options) {\n\n let compressedFile\n\n options.maxSizeMB = options.maxSizeMB || Number.POSITIVE_INFINITY\n const useWebWorker = typeof options.useWebWorker === 'boolean' ? options.useWebWorker : false\n delete options.useWebWorker\n\n if (!(file instanceof Blob || file instanceof CustomFile)) {\n throw new Error('The file given is not an instance of Blob or File')\n } else if (!/^image/.test(file.type)) {\n throw new Error('The file given is not an image')\n }\n\n // try run in web worker, fall back to run in main thread\n const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope\n\n // if ((useWebWorker && typeof Worker === 'function') || inWebWorker) {\n // console.log('run compression in web worker')\n // } else {\n // console.log('run compression in main thread')\n // }\n\n if (useWebWorker && typeof Worker === 'function' && !inWebWorker) {\n try {\n // console.log(1)\n // \"compressOnWebWorker\" is kind of like a recursion to call \"imageCompression\" again inside web worker\n compressedFile = await compressOnWebWorker(file, options)\n } catch (e) {\n // console.warn('Run compression in web worker failed:', e, ', fall back to main thread')\n // console.log(1.5)\n compressedFile = await compress(file, options)\n }\n } else {\n // console.log(2)\n compressedFile = await compress(file, options)\n }\n\n try {\n compressedFile.name = file.name\n compressedFile.lastModified = file.lastModified\n } catch (e) {}\n\n return compressedFile\n}\n\nimageCompression.getDataUrlFromFile = getDataUrlFromFile\nimageCompression.getFilefromDataUrl = getFilefromDataUrl\nimageCompression.loadImage = loadImage\nimageCompression.drawImageInCanvas = drawImageInCanvas\nimageCompression.drawFileInCanvas = drawFileInCanvas\nimageCompression.canvasToFile = canvasToFile\nimageCompression.getExifOrientation = getExifOrientation\nimageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight\nimageCompression.followExifOrientation = followExifOrientation\nimageCompression.cleanupMemory = cleanupCanvasMemory\nimageCompression.version = '1.0.8'\n\nexport default imageCompression\n"],"names":["isBrowser","window","moduleMapper","getDataUrlFromFile","resolve","reader","dataurl","split","n","bstr","filename","file","lastModified","canvas","view","length","offset","getUint32","marker","maxWidthOrHeight","width","followExifOrientation","height","ctx","inc","drawFileInCanvas","exifOrientation","orientationFixedCanvas","options","fileType","imageCompressionLibUrl","cnt","generateLib","script","imageCompression","getFilefromDataUrl","loadImage","drawImageInCanvas","canvasToFile","getExifOrientation"],"mappings":";;;;;;;m/CAAA,IAAMA,EAA8B,oBAAlBC,2EAKoF,uCAQ5DC,2HAiBnC,SAASC,mDACPC,6DAEqBC,uNAiCdC,EAAAC,mCACK,gBAEjBC,EAAIC,6BAEJD,8DAUYE,EACdC,EAAAC,eACAR,28BAwC2CS,qcAoB7CC,mEAEMV,mCAGQW,6DAEgBC,8GASpBF,EAAAG,UAAgBD,0GAKPA,EAAA,iCAGTE,0KA+BVC,oJAqBuBC,4CAQTP,EAAV,0CAEAA,qBASYQ,yCACZC,mPAmBuCA,wJAI9B,sHA8BbC,2BAHWD,kaC3OTE,iLAPK,6DAUGC,iBAARd,yKAUMe,sUAWAC,EAAwBC,EAAAC,6PAsB5BhB,o8BC/FJ,IACIiB,IADAC,IAcJ,SAASC,okEAPPC,gkDCyDwBtB,0zBAa1BuB,iBAAA/B,mBAAsCA,mBACtC+B,iBAAAC,sCACAD,iBAAAE,oBACAF,iBAAAG,oCACAH,iBAAAT,kCAEAS,iBAAAI,0BAxFAJ,iBAAAK"} \ No newline at end of file diff --git a/dist/browser-image-compression.mjs b/dist/browser-image-compression.mjs index f4d0939..994305a 100644 --- a/dist/browser-image-compression.mjs +++ b/dist/browser-image-compression.mjs @@ -5,5 +5,5 @@ * https://github.com/Donaldcwl/browser-image-compression */ -function _defineProperty(e,r,n){return r in e?Object.defineProperty(e,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[r]=n,e}function ownKeys(e,r){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);r&&(t=t.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),n.push.apply(n,t)}return n}function _objectSpread2(e){for(var r=1;r2&&void 0!==arguments[2]?arguments[2]:Date.now();return new Promise((function(t){for(var o=e.split(","),i=o[0].match(/:(.*?);/)[1],a=atob(o[1]),s=a.length,c=new Uint8Array(s);s--;)c[s]=a.charCodeAt(s);var l=new Blob([c],{type:i});l.name=r,l.lastModified=n,t(l)}))}function loadImage(e){return new Promise((function(r,n){var t=new Image;t.onload=function(){return r(t)},t.onerror=function(e){return n(e)},t.src=e}))}function drawImageInCanvas(e){var r=_slicedToArray(getNewCanvasAndCtx(e.width,e.height),2),n=r[0];return r[1].drawImage(e,0,0,n.width,n.height),n}function drawFileInCanvas(e){return new Promise((function(r,n){var t,o,i=function $Try_1_Post(){try{return o=drawImageInCanvas(t),r([t,o])}catch(e){return n(e)}},a=function $Try_1_Catch(r){try{return getDataUrlFromFile(e).then((function(e){try{return loadImage(e).then((function(e){try{return t=e,i()}catch(e){return n(e)}}),n)}catch(e){return n(e)}}),n)}catch(e){return n(e)}};try{return createImageBitmap(e).then((function(e){try{return t=e,i()}catch(e){return a()}}),a)}catch(e){a()}}))}function canvasToFile(e,r,n,t){var o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1;return new Promise((function(i,a){var s;return"function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas?e.convertToBlob({type:r,quality:o}).then(function(e){try{return(s=e).name=n,s.lastModified=t,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a):getFilefromDataUrl(e.toDataURL(r,o),n,t).then(function(e){try{return s=e,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a);function $If_4(){return i(s)}}))}function getExifOrientation(e){return new Promise((function(r,n){var t=new CustomFileReader;t.onload=function(e){var n=new DataView(e.target.result);if(65496!=n.getUint16(0,!1))return r(-2);for(var t=n.byteLength,o=2;oi||o>i)){var s=_slicedToArray(getNewCanvasAndCtx(t,o),2);a=s[0],n=s[1],t>o?(a.width=i,a.height=o/t*i):(a.width=t/o*i,a.height=i),n.drawImage(e,0,0,a.width,a.height),cleanupCanvasMemory(e)}return a}function followExifOrientation(e,r){var n=e.width,t=e.height,o=_slicedToArray(getNewCanvasAndCtx(n,t),2),i=o[0],a=o[1];switch(40&&void 0!==arguments[0]?arguments[0]:5;o+=e,"function"==typeof r.onProgress&&r.onProgress(Math.min(o,100))}function setProgress(e){o=Math.min(Math.max(e,o),100),"function"==typeof r.onProgress&&r.onProgress(o)}return o=0,i=r.maxIteration||10,a=1024*r.maxSizeMB*1024,incProgress(),drawFileInCanvas(e).then(function(o){try{var v=_slicedToArray(o,2);return v[0],s=v[1],incProgress(),c=handleMaxWidthOrHeight(s,r),incProgress(),new Promise((function(n,t){var o;if(!(o=r.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return t(e)}}.bind(this),t);function $If_2(){return n(o)}return $If_2.call(this)})).then(function(o){try{return r.exifOrientation=o,incProgress(),l=followExifOrientation(c,r.exifOrientation),incProgress(),u=1,canvasToFile(l,r.fileType||e.type,e.name,e.lastModified,u).then(function(o){try{{if(m=o,incProgress(),m.size<=a)return setProgress(100),n(m);var v;function $Loop_3(){if(i--&&g>a){var n,o,s=_slicedToArray(getNewCanvasAndCtx(n=.9*h.width,o=.9*h.height),2);return d=s[0],s[1].drawImage(h,0,0,n,o),"image/jpeg"===e.type&&(u*=.9),canvasToFile(d,r.fileType||e.type,e.name,e.lastModified,u).then((function(e){try{return p=e,cleanupCanvasMemory(h),h=d,g=p.size,setProgress(Math.min(99,Math.floor((f-g)/(f-a)*100))),$Loop_3}catch(e){return t(e)}}),t)}return[1]}return f=m.size,g=f,h=l,(v=function(e){for(;e;){if(e.then)return void e.then(v,t);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3}else e=e.call(this)}catch(e){return t(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(h),cleanupCanvasMemory(d),cleanupCanvasMemory(c),cleanupCanvasMemory(l),cleanupCanvasMemory(s),setProgress(100),n(p)}}}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}))}e&&(Number.isInteger=Number.isInteger||function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e});var n,t,o=0;function generateLib(){return function createSourceObject(e){return URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}("\n function imageCompression (){return (".concat(imageCompression,").apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ").concat(imageCompression.getDataUrlFromFile,"\n imageCompression.getFilefromDataUrl = ").concat(imageCompression.getFilefromDataUrl,"\n imageCompression.loadImage = ").concat(imageCompression.loadImage,"\n imageCompression.drawImageInCanvas = ").concat(imageCompression.drawImageInCanvas,"\n imageCompression.drawFileInCanvas = ").concat(imageCompression.drawFileInCanvas,"\n imageCompression.canvasToFile = ").concat(imageCompression.canvasToFile,"\n imageCompression.getExifOrientation = ").concat(imageCompression.getExifOrientation,"\n imageCompression.handleMaxWidthOrHeight = ").concat(imageCompression.handleMaxWidthOrHeight,"\n imageCompression.followExifOrientation = ").concat(imageCompression.followExifOrientation,"\n imageCompression.cleanupMemory = ").concat(imageCompression.cleanupMemory,"\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ").concat(getNewCanvasAndCtx,"\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (").concat(compress,").apply(null, arguments)}\n "))}function generateWorkerScript(){return function createWorker(e){return"function"==typeof e&&(e="(".concat(f,")()")),new Worker(URL.createObjectURL(new Blob([e])))}("\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n })\n ")}function imageCompression(e,r){return new Promise((function(i,a){var s,c,l;if(r.maxSizeMB=r.maxSizeMB||Number.POSITIVE_INFINITY,c="boolean"==typeof r.useWebWorker&&r.useWebWorker,delete r.useWebWorker,!(e instanceof Blob||e instanceof CustomFile))return a(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return a(new Error("The file given is not an image"));if(l="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!c||"function"!=typeof Worker||l)return compress(e,r).then(function(e){try{return s=e,$If_3.call(this)}catch(e){return a(e)}}.bind(this),a);var u=function(){try{return $If_3.call(this)}catch(e){return a(e)}}.bind(this),m=function $Try_1_Catch(n){try{return compress(e,r).then((function(e){try{return s=e,u()}catch(e){return a(e)}}),a)}catch(e){return a(e)}};try{return function compressOnWebWorker(e,r){return new Promise((function(i,a){return new Promise((function(s,c){var l=o++;return n||(n=generateLib()),t||(t=generateWorkerScript()),t.addEventListener("message",(function handler(e){if(e.data.id===l){if(void 0!==e.data.progress&&e.data.progress<100)return void r.onProgress(e.data.progress);t.removeEventListener("message",handler),e.data.error&&a(new Error(e.data.error)),i(e.data.file)}})),t.postMessage({file:e,id:l,imageCompressionLibUrl:n,options:_objectSpread2({},r,{onProgress:void 0})}),s()}))}))}(e,r).then((function(e){try{return s=e,u()}catch(e){return m()}}),m)}catch(e){m()}function $If_3(){try{s.name=e.name,s.lastModified=e.lastModified}catch(e){}return i(s)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupMemory=cleanupCanvasMemory,imageCompression.version="1.0.8";export default imageCompression; +function _defineProperty(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function ownKeys(e,n){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),r.push.apply(r,t)}return r}function _objectSpread2(e){for(var n=1;n2&&void 0!==arguments[2]?arguments[2]:Date.now();return new Promise((function(t){for(var i=e.split(","),o=i[0].match(/:(.*?);/)[1],a=atob(i[1]),s=a.length,c=new Uint8Array(s);s--;)c[s]=a.charCodeAt(s);var l=new Blob([c],{type:o});l.name=n,l.lastModified=r,t(l)}))}function loadImage(e){return new Promise((function(n,r){var t=new Image;t.onload=function(){return n(t)},t.onerror=function(e){return r(e)},t.src=e}))}function drawImageInCanvas(e){var n=_slicedToArray(getNewCanvasAndCtx(e.width,e.height),2),r=n[0];return n[1].drawImage(e,0,0,r.width,r.height),r}function drawFileInCanvas(e){return new Promise((function(n,r){var t,i,o=function $Try_1_Post(){try{return i=drawImageInCanvas(t),n([t,i])}catch(e){return r(e)}},a=function $Try_1_Catch(n){try{return getDataUrlFromFile(e).then((function(e){try{return loadImage(e).then((function(e){try{return t=e,o()}catch(e){return r(e)}}),r)}catch(e){return r(e)}}),r)}catch(e){return r(e)}};try{return createImageBitmap(e).then((function(e){try{return t=e,o()}catch(e){return a()}}),a)}catch(e){a()}}))}function canvasToFile(e,n,r,t){var i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1;return new Promise((function(o,a){var s;return"function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas?e.convertToBlob({type:n,quality:i}).then(function(e){try{return(s=e).name=r,s.lastModified=t,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a):getFilefromDataUrl(e.toDataURL(n,i),r,t).then(function(e){try{return s=e,$If_4.call(this)}catch(e){return a(e)}}.bind(this),a);function $If_4(){return o(s)}}))}function getExifOrientation(e){return new Promise((function(n,r){var t=new CustomFileReader;t.onload=function(e){var r=new DataView(e.target.result);if(65496!=r.getUint16(0,!1))return n(-2);for(var t=r.byteLength,i=2;io||i>o)){var s=_slicedToArray(getNewCanvasAndCtx(t,i),2);a=s[0],r=s[1],t>i?(a.width=o,a.height=i/t*o):(a.width=t/i*o,a.height=o),r.drawImage(e,0,0,a.width,a.height),cleanupCanvasMemory(e)}return a}function followExifOrientation(e,n){var r=e.width,t=e.height,i=_slicedToArray(getNewCanvasAndCtx(r,t),2),o=i[0],a=i[1];switch(40&&void 0!==arguments[0]?arguments[0]:5;i+=e,"function"==typeof n.onProgress&&n.onProgress(Math.min(i,100))}function setProgress(e){i=Math.min(Math.max(e,i),100),"function"==typeof n.onProgress&&n.onProgress(i)}return i=0,o=n.maxIteration||10,a=1024*n.maxSizeMB*1024,incProgress(),drawFileInCanvas(e).then(function(i){try{var C=_slicedToArray(i,2);return C[0],s=C[1],incProgress(),c=handleMaxWidthOrHeight(s,n),incProgress(),new Promise((function(r,t){var i;if(!(i=n.exifOrientation))return getExifOrientation(e).then(function(e){try{return i=e,$If_2.call(this)}catch(e){return t(e)}}.bind(this),t);function $If_2(){return r(i)}return $If_2.call(this)})).then(function(i){try{return n.exifOrientation=i,incProgress(),l=followExifOrientation(c,n.exifOrientation),incProgress(),u=1,canvasToFile(l,n.fileType||e.type,e.name,e.lastModified,u).then(function(i){try{{if(m=i,incProgress(),f=m.size>a,g=m.size>e.size,!f&&!g)return setProgress(100),r(m);var C;function $Loop_3(){if(o--&&(h>a||h>p)){var r,i,s=_slicedToArray(getNewCanvasAndCtx(r=f?.95*y.width:y.width,i=f?.95*y.height:y.height),2);return w=s[0],s[1].drawImage(y,0,0,r,i),"image/jpeg"===e.type&&(u*=.95),canvasToFile(w,n.fileType||e.type,e.name,e.lastModified,u).then((function(e){try{return v=e,cleanupCanvasMemory(y),y=w,h=v.size,setProgress(Math.min(99,Math.floor((d-h)/(d-a)*100))),$Loop_3}catch(e){return t(e)}}),t)}return[1]}return p=e.size,d=m.size,h=d,y=l,(C=function(e){for(;e;){if(e.then)return void e.then(C,t);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3}else e=e.call(this)}catch(e){return t(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(y),cleanupCanvasMemory(w),cleanupCanvasMemory(c),cleanupCanvasMemory(l),cleanupCanvasMemory(s),setProgress(100),r(v)}}}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}catch(e){return t(e)}}.bind(this),t)}))}e&&(Number.isInteger=Number.isInteger||function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e});var r,t,i=0;function generateLib(){return function createSourceObject(e){return URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}("\n function imageCompression (){return (".concat(imageCompression,").apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ").concat(imageCompression.getDataUrlFromFile,"\n imageCompression.getFilefromDataUrl = ").concat(imageCompression.getFilefromDataUrl,"\n imageCompression.loadImage = ").concat(imageCompression.loadImage,"\n imageCompression.drawImageInCanvas = ").concat(imageCompression.drawImageInCanvas,"\n imageCompression.drawFileInCanvas = ").concat(imageCompression.drawFileInCanvas,"\n imageCompression.canvasToFile = ").concat(imageCompression.canvasToFile,"\n imageCompression.getExifOrientation = ").concat(imageCompression.getExifOrientation,"\n imageCompression.handleMaxWidthOrHeight = ").concat(imageCompression.handleMaxWidthOrHeight,"\n imageCompression.followExifOrientation = ").concat(imageCompression.followExifOrientation,"\n imageCompression.cleanupMemory = ").concat(imageCompression.cleanupMemory,"\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ").concat(getNewCanvasAndCtx,"\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (").concat(compress,").apply(null, arguments)}\n "))}function generateWorkerScript(){return function createWorker(e){return"function"==typeof e&&(e="(".concat(f,")()")),new Worker(URL.createObjectURL(new Blob([e])))}("\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n })\n ")}function imageCompression(e,n){return new Promise((function(o,a){var s,c,l;if(n.maxSizeMB=n.maxSizeMB||Number.POSITIVE_INFINITY,c="boolean"==typeof n.useWebWorker&&n.useWebWorker,delete n.useWebWorker,!(e instanceof Blob||e instanceof CustomFile))return a(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return a(new Error("The file given is not an image"));if(l="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!c||"function"!=typeof Worker||l)return compress(e,n).then(function(e){try{return s=e,$If_3.call(this)}catch(e){return a(e)}}.bind(this),a);var u=function(){try{return $If_3.call(this)}catch(e){return a(e)}}.bind(this),m=function $Try_1_Catch(r){try{return compress(e,n).then((function(e){try{return s=e,u()}catch(e){return a(e)}}),a)}catch(e){return a(e)}};try{return function compressOnWebWorker(e,n){return new Promise((function(o,a){return new Promise((function(s,c){var l=i++;return r||(r=generateLib()),t||(t=generateWorkerScript()),t.addEventListener("message",(function handler(e){if(e.data.id===l){if(void 0!==e.data.progress&&e.data.progress<100)return void n.onProgress(e.data.progress);t.removeEventListener("message",handler),e.data.error&&a(new Error(e.data.error)),o(e.data.file)}})),t.postMessage({file:e,id:l,imageCompressionLibUrl:r,options:_objectSpread2({},n,{onProgress:void 0})}),s()}))}))}(e,n).then((function(e){try{return s=e,u()}catch(e){return m()}}),m)}catch(e){m()}function $If_3(){try{s.name=e.name,s.lastModified=e.lastModified}catch(e){}return o(s)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupMemory=cleanupCanvasMemory,imageCompression.version="1.0.8";export default imageCompression; //# sourceMappingURL=browser-image-compression.mjs.map diff --git a/dist/browser-image-compression.mjs.map b/dist/browser-image-compression.mjs.map index 870f14c..c21cef5 100644 --- a/dist/browser-image-compression.mjs.map +++ b/dist/browser-image-compression.mjs.map @@ -1 +1 @@ -{"version":3,"file":"browser-image-compression.mjs","sources":["../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["const isBrowser = typeof window !== 'undefined' // change browser environment to support SSR\n\n// add support for cordova-plugin-file\nconst moduleMapper = isBrowser && window.cordova && window.cordova.require && window.cordova.require('cordova/modulemapper')\nexport const CustomFile = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'File')) || File)\nexport const CustomFileReader = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'FileReader')) || FileReader)\n\n/**\n * getDataUrlFromFile\n *\n * @param {File} file\n * @returns {Promise}\n */\nexport function getDataUrlFromFile (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = () => resolve(reader.result)\n reader.onerror = (e) => reject(e)\n reader.readAsDataURL(file)\n })\n}\n\n/**\n * getFilefromDataUrl\n *\n * @param {string} dataurl\n * @param {string} filename\n * @param {number} [lastModified=Date.now()]\n * @returns {Promise}\n */\nexport function getFilefromDataUrl (dataurl, filename, lastModified = Date.now()) {\n return new Promise((resolve) => {\n const arr = dataurl.split(',')\n const mime = arr[0].match(/:(.*?);/)[1]\n const bstr = atob(arr[1])\n let n = bstr.length\n const u8arr = new Uint8Array(n)\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n)\n }\n const file = new Blob([u8arr], { type: mime })\n file.name = filename\n file.lastModified = lastModified\n resolve(file)\n\n // Safari has issue with File constructor not being able to POST in FormData\n // https://github.com/Donaldcwl/browser-image-compression/issues/8\n // https://bugs.webkit.org/show_bug.cgi?id=165081\n // let file\n // try {\n // file = new File([u8arr], filename, { type: mime }) // Edge do not support File constructor\n // } catch (e) {\n // file = new Blob([u8arr], { type: mime })\n // file.name = filename\n // file.lastModified = lastModified\n // }\n // resolve(file)\n })\n}\n\n/**\n * loadImage\n *\n * @param {string} src\n * @returns {Promise}\n */\nexport function loadImage (src) {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = (e) => reject(e)\n img.src = src\n })\n}\n\n/**\n * drawImageInCanvas\n *\n * @param {HTMLImageElement} img\n * @returns {HTMLCanvasElement}\n */\nexport function drawImageInCanvas (img) {\n const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\n/**\n * drawFileInCanvas\n *\n * @param {File} file\n * @returns {Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement]>}\n */\nexport async function drawFileInCanvas (file) {\n let img\n try {\n img = await createImageBitmap(file)\n } catch (e) {\n const dataUrl = await getDataUrlFromFile(file)\n img = await loadImage(dataUrl)\n }\n const canvas = drawImageInCanvas(img)\n return [img, canvas]\n}\n\n/**\n * canvasToFile\n *\n * @param canvas\n * @param {string} fileType\n * @param {string} fileName\n * @param {number} fileLastModified\n * @param {number} [quality]\n * @returns {Promise}\n */\nexport async function canvasToFile (canvas, fileType, fileName, fileLastModified, quality = 1) {\n let file\n if (typeof OffscreenCanvas === 'function' && canvas instanceof OffscreenCanvas) {\n file = await canvas.convertToBlob({ type: fileType, quality })\n file.name = fileName\n file.lastModified = fileLastModified\n } else {\n const dataUrl = canvas.toDataURL(fileType, quality)\n file = await getFilefromDataUrl(dataUrl, fileName, fileLastModified)\n }\n return file\n}\n\n/**\n * getExifOrientation\n * get image exif orientation info\n * source: https://stackoverflow.com/a/32490603/10395024\n *\n * @param {File} file\n * @returns {Promise} - orientation id, see https://i.stack.imgur.com/VGsAj.gif\n */\nexport function getExifOrientation (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = (e) => {\n const view = new DataView(e.target.result)\n if (view.getUint16(0, false) != 0xFFD8) {\n return resolve(-2)\n }\n const length = view.byteLength\n let offset = 2\n while (offset < length) {\n if (view.getUint16(offset + 2, false) <= 8) return resolve(-1)\n const marker = view.getUint16(offset, false)\n offset += 2\n if (marker == 0xFFE1) {\n if (view.getUint32(offset += 2, false) != 0x45786966) {\n return resolve(-1)\n }\n\n var little = view.getUint16(offset += 6, false) == 0x4949\n offset += view.getUint32(offset + 4, little)\n var tags = view.getUint16(offset, little)\n offset += 2\n for (var i = 0; i < tags; i++) {\n if (view.getUint16(offset + (i * 12), little) == 0x0112) {\n return resolve(view.getUint16(offset + (i * 12) + 8, little))\n }\n }\n } else if ((marker & 0xFF00) != 0xFF00) {\n break\n } else {\n offset += view.getUint16(offset, false)\n }\n }\n return resolve(-1)\n }\n reader.onerror = (e) => reject(e)\n reader.readAsArrayBuffer(file)\n })\n}\n\n/**\n *\n * @param {HTMLCanvasElement} canvas\n * @param options\n * @returns {HTMLCanvasElement>}\n */\nexport function handleMaxWidthOrHeight (canvas, options) {\n const width = canvas.width\n const height = canvas.height\n const maxWidthOrHeight = options.maxWidthOrHeight\n\n const needToHandle = Number.isInteger(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight)\n\n let newCanvas = canvas\n let ctx\n\n if (needToHandle) {\n [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n if (width > height) {\n newCanvas.width = maxWidthOrHeight\n newCanvas.height = (height / width) * maxWidthOrHeight\n } else {\n newCanvas.width = (width / height) * maxWidthOrHeight\n newCanvas.height = maxWidthOrHeight\n }\n ctx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height)\n\n cleanupCanvasMemory(canvas)\n }\n\n return newCanvas\n}\n\n/**\n * followExifOrientation\n * source: https://stackoverflow.com/a/40867559/10395024\n *\n * @param {HTMLCanvasElement} canvas\n * @param {number} exifOrientation\n * @returns {HTMLCanvasElement} canvas\n */\nexport function followExifOrientation (canvas, exifOrientation) {\n const width = canvas.width\n const height = canvas.height\n\n const [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n\n // set proper canvas dimensions before transform & export\n if (4 < exifOrientation && exifOrientation < 9) {\n newCanvas.width = height\n newCanvas.height = width\n } else {\n newCanvas.width = width\n newCanvas.height = height\n }\n\n // transform context before drawing image\n switch (exifOrientation) {\n case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;\n case 3: ctx.transform(-1, 0, 0, -1, width, height); break;\n case 4: ctx.transform(1, 0, 0, -1, 0, height); break;\n case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;\n case 6: ctx.transform(0, 1, -1, 0, height, 0); break;\n case 7: ctx.transform(0, -1, -1, 0, height, width); break;\n case 8: ctx.transform(0, -1, 1, 0, 0, width); break;\n default: break;\n }\n\n ctx.drawImage(canvas, 0, 0, width, height)\n\n cleanupCanvasMemory(canvas)\n\n return newCanvas\n}\n\n/**\n * get new Canvas and it's context\n * @param width\n * @param height\n * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}\n */\nexport function getNewCanvasAndCtx (width, height) {\n let canvas\n let ctx\n try {\n canvas = new OffscreenCanvas(width, height)\n ctx = canvas.getContext('2d')\n if (ctx === null) {\n throw new Error('getContext of OffscreenCanvas returns null')\n }\n } catch (e) {\n canvas = document.createElement('canvas')\n ctx = canvas.getContext('2d')\n }\n canvas.width = width\n canvas.height = height\n return [canvas, ctx]\n}\n\n/**\n * clear Canvas memory\n * @param canvas\n * @returns null\n */\nexport function cleanupCanvasMemory (canvas) {\n canvas.width = 0\n canvas.height = 0\n}\n\nif (isBrowser) {\n// Polyfill for Number.isInteger\n Number.isInteger = Number.isInteger || function (value) {\n return typeof value === 'number' &&\n isFinite(value) &&\n Math.floor(value) === value\n }\n}\n","import {\n canvasToFile,\n cleanupCanvasMemory,\n drawFileInCanvas,\n followExifOrientation,\n getExifOrientation,\n getNewCanvasAndCtx,\n handleMaxWidthOrHeight\n} from './utils'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nexport default async function compress (file, options) {\n let progress = 0\n\n function incProgress (inc = 5) {\n progress += inc\n if (typeof options.onProgress === 'function') {\n options.onProgress(Math.min(progress, 100))\n }\n }\n\n function setProgress (p) {\n progress = Math.min(Math.max(p, progress), 100)\n if (typeof options.onProgress === 'function') {\n options.onProgress(progress)\n }\n }\n\n let remainingTrials = options.maxIteration || 10\n\n const maxSizeByte = options.maxSizeMB * 1024 * 1024\n\n incProgress()\n\n // drawFileInCanvas\n let [img, origCanvas] = await drawFileInCanvas(file)\n\n incProgress()\n\n // handleMaxWidthOrHeight\n const maxWidthOrHeightFixedCanvas = handleMaxWidthOrHeight(origCanvas, options)\n\n incProgress()\n\n // exifOrientation\n options.exifOrientation = options.exifOrientation || await getExifOrientation(file)\n incProgress()\n const orientationFixedCanvas = followExifOrientation(maxWidthOrHeightFixedCanvas, options.exifOrientation)\n incProgress()\n\n let quality = 1\n\n let tempFile = await canvasToFile(orientationFixedCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n incProgress()\n // check if we need to compress or resize\n if (tempFile.size <= maxSizeByte) {\n // no need to compress\n setProgress(100)\n return tempFile\n }\n\n const originalSize = tempFile.size\n let currentSize = originalSize\n let compressedFile\n let newCanvas, ctx\n let canvas = orientationFixedCanvas\n while (remainingTrials-- && currentSize > maxSizeByte) {\n const newWidth = canvas.width * 0.9\n const newHeight = canvas.height * 0.9;\n [newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight)\n\n ctx.drawImage(canvas, 0, 0, newWidth, newHeight)\n\n if (file.type === 'image/jpeg') {\n quality *= 0.9\n }\n compressedFile = await canvasToFile(newCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n\n cleanupCanvasMemory(canvas)\n\n canvas = newCanvas\n\n currentSize = compressedFile.size\n setProgress(Math.min(99, Math.floor((originalSize - currentSize) / (originalSize - maxSizeByte) * 100)))\n }\n\n // garbage clean canvas for safari\n // ref: https://bugs.webkit.org/show_bug.cgi?id=195325\n cleanupCanvasMemory(canvas)\n cleanupCanvasMemory(newCanvas)\n cleanupCanvasMemory(maxWidthOrHeightFixedCanvas)\n cleanupCanvasMemory(orientationFixedCanvas)\n cleanupCanvasMemory(origCanvas)\n\n setProgress(100)\n return compressedFile\n}\n","import lib from './index'\nimport compress from './image-compression'\nimport { getNewCanvasAndCtx } from './utils'\n\nlet cnt = 0\nlet imageCompressionLibUrl\nlet worker\n\nfunction createWorker (script) {\n if (typeof script === 'function') {\n script = `(${f})()`\n }\n return new Worker(URL.createObjectURL(new Blob([script])))\n}\n\nfunction createSourceObject (str) {\n return URL.createObjectURL(new Blob([str], { type: 'application/javascript' }))\n}\n\nfunction generateLib () {\n // prepare the lib to be used inside WebWorker\n return createSourceObject(`\n function imageCompression (){return (${lib}).apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ${lib.getDataUrlFromFile}\n imageCompression.getFilefromDataUrl = ${lib.getFilefromDataUrl}\n imageCompression.loadImage = ${lib.loadImage}\n imageCompression.drawImageInCanvas = ${lib.drawImageInCanvas}\n imageCompression.drawFileInCanvas = ${lib.drawFileInCanvas}\n imageCompression.canvasToFile = ${lib.canvasToFile}\n imageCompression.getExifOrientation = ${lib.getExifOrientation}\n imageCompression.handleMaxWidthOrHeight = ${lib.handleMaxWidthOrHeight}\n imageCompression.followExifOrientation = ${lib.followExifOrientation}\n imageCompression.cleanupMemory = ${lib.cleanupMemory}\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ${getNewCanvasAndCtx}\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (${compress}).apply(null, arguments)}\n `)\n}\n\nfunction generateWorkerScript () {\n // code to be run in the WebWorker\n return createWorker(`\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\\\n' + e.stack, id })\n }\n })\n `)\n}\n\nexport function compressOnWebWorker (file, options) {\n return new Promise(async (resolve, reject) => {\n let id = cnt++\n\n if (!imageCompressionLibUrl) {\n imageCompressionLibUrl = generateLib()\n }\n\n if (!worker) {\n worker = generateWorkerScript()\n }\n\n function handler (e) {\n if (e.data.id === id) {\n if (e.data.progress !== undefined && e.data.progress < 100) {\n options.onProgress(e.data.progress)\n return\n }\n worker.removeEventListener('message', handler)\n if (e.data.error) {\n reject(new Error(e.data.error))\n }\n resolve(e.data.file)\n }\n }\n\n worker.addEventListener('message', handler)\n worker.postMessage({\n file,\n id,\n imageCompressionLibUrl,\n options: { ...options, onProgress: undefined }\n })\n })\n}\n","import compress from './image-compression'\nimport {\n canvasToFile,\n drawFileInCanvas,\n drawImageInCanvas,\n getDataUrlFromFile,\n getFilefromDataUrl,\n loadImage,\n getExifOrientation,\n handleMaxWidthOrHeight,\n followExifOrientation,\n CustomFile,\n cleanupCanvasMemory\n} from './utils'\nimport { compressOnWebWorker } from './web-worker'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {boolean} [options.useWebWorker=false]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nasync function imageCompression (file, options) {\n\n let compressedFile\n\n options.maxSizeMB = options.maxSizeMB || Number.POSITIVE_INFINITY\n const useWebWorker = typeof options.useWebWorker === 'boolean' ? options.useWebWorker : false\n delete options.useWebWorker\n\n if (!(file instanceof Blob || file instanceof CustomFile)) {\n throw new Error('The file given is not an instance of Blob or File')\n } else if (!/^image/.test(file.type)) {\n throw new Error('The file given is not an image')\n }\n\n // try run in web worker, fall back to run in main thread\n const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope\n\n // if ((useWebWorker && typeof Worker === 'function') || inWebWorker) {\n // console.log('run compression in web worker')\n // } else {\n // console.log('run compression in main thread')\n // }\n\n if (useWebWorker && typeof Worker === 'function' && !inWebWorker) {\n try {\n // console.log(1)\n // \"compressOnWebWorker\" is kind of like a recursion to call \"imageCompression\" again inside web worker\n compressedFile = await compressOnWebWorker(file, options)\n } catch (e) {\n // console.warn('Run compression in web worker failed:', e, ', fall back to main thread')\n // console.log(1.5)\n compressedFile = await compress(file, options)\n }\n } else {\n // console.log(2)\n compressedFile = await compress(file, options)\n }\n\n try {\n compressedFile.name = file.name\n compressedFile.lastModified = file.lastModified\n } catch (e) {}\n\n return compressedFile\n}\n\nimageCompression.getDataUrlFromFile = getDataUrlFromFile\nimageCompression.getFilefromDataUrl = getFilefromDataUrl\nimageCompression.loadImage = loadImage\nimageCompression.drawImageInCanvas = drawImageInCanvas\nimageCompression.drawFileInCanvas = drawFileInCanvas\nimageCompression.canvasToFile = canvasToFile\nimageCompression.getExifOrientation = getExifOrientation\nimageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight\nimageCompression.followExifOrientation = followExifOrientation\nimageCompression.cleanupMemory = cleanupCanvasMemory\nimageCompression.version = '1.0.8'\n\nexport default imageCompression\n"],"names":["isBrowser","window","moduleMapper","getDataUrlFromFile","resolve","reader","dataurl","split","n","bstr","filename","file","lastModified","canvas","view","length","offset","getUint32","marker","maxWidthOrHeight","width","followExifOrientation","height","ctx","inc","drawFileInCanvas","exifOrientation","maxWidthOrHeightFixedCanvas","imageCompressionLibUrl","cnt","generateLib","script","imageCompression","getFilefromDataUrl","loadImage","drawImageInCanvas","canvasToFile","getExifOrientation"],"mappings":";;;;;;;4yCAAA,IAAMA,EAA8B,oBAAlBC,2EAKoF,uCAQ5DC,2HAiBnC,SAASC,mDACPC,6DAEqBC,uNAiCdC,EAAAC,mCACK,gBAEjBC,EAAIC,6BAEJD,8DAUYE,EACdC,EAAAC,eACAR,28BAwC2CS,qcAoB7CC,mEAEMV,mCAGQW,6DAEgBC,8GASpBF,EAAAG,UAAgBD,0GAKPA,EAAA,iCAGTE,0KA+BVC,qJAqBuBC,4CAQTP,EAAV,0CAEAA,qBASYQ,yCACZC,mPAmBuCA,wJAI9B,sHA8BbC,2BAHWD,4ZC3OTE,iLAPK,6DAUGC,iBAARd,yKAUAe,kRAGSC,yrCCjEX,IACIC,IADAC,IAcJ,SAASC,okEAPPC,gkDCyDwBpB,mzBAa1BqB,iBAAA7B,mBAAsCA,mBACtC6B,iBAAAC,sCACAD,iBAAAE,oBACAF,iBAAAG,oCACAH,iBAAAP,kCAEAO,iBAAAI,0BAxFAJ,iBAAAK"} \ No newline at end of file +{"version":3,"file":"browser-image-compression.mjs","sources":["../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["const isBrowser = typeof window !== 'undefined' // change browser environment to support SSR\n\n// add support for cordova-plugin-file\nconst moduleMapper = isBrowser && window.cordova && window.cordova.require && window.cordova.require('cordova/modulemapper')\nexport const CustomFile = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'File')) || File)\nexport const CustomFileReader = isBrowser && ((moduleMapper && moduleMapper.getOriginalSymbol(window, 'FileReader')) || FileReader)\n\n/**\n * getDataUrlFromFile\n *\n * @param {File} file\n * @returns {Promise}\n */\nexport function getDataUrlFromFile (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = () => resolve(reader.result)\n reader.onerror = (e) => reject(e)\n reader.readAsDataURL(file)\n })\n}\n\n/**\n * getFilefromDataUrl\n *\n * @param {string} dataurl\n * @param {string} filename\n * @param {number} [lastModified=Date.now()]\n * @returns {Promise}\n */\nexport function getFilefromDataUrl (dataurl, filename, lastModified = Date.now()) {\n return new Promise((resolve) => {\n const arr = dataurl.split(',')\n const mime = arr[0].match(/:(.*?);/)[1]\n const bstr = atob(arr[1])\n let n = bstr.length\n const u8arr = new Uint8Array(n)\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n)\n }\n const file = new Blob([u8arr], { type: mime })\n file.name = filename\n file.lastModified = lastModified\n resolve(file)\n\n // Safari has issue with File constructor not being able to POST in FormData\n // https://github.com/Donaldcwl/browser-image-compression/issues/8\n // https://bugs.webkit.org/show_bug.cgi?id=165081\n // let file\n // try {\n // file = new File([u8arr], filename, { type: mime }) // Edge do not support File constructor\n // } catch (e) {\n // file = new Blob([u8arr], { type: mime })\n // file.name = filename\n // file.lastModified = lastModified\n // }\n // resolve(file)\n })\n}\n\n/**\n * loadImage\n *\n * @param {string} src\n * @returns {Promise}\n */\nexport function loadImage (src) {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = (e) => reject(e)\n img.src = src\n })\n}\n\n/**\n * drawImageInCanvas\n *\n * @param {HTMLImageElement} img\n * @returns {HTMLCanvasElement}\n */\nexport function drawImageInCanvas (img) {\n const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\n/**\n * drawFileInCanvas\n *\n * @param {File} file\n * @returns {Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement]>}\n */\nexport async function drawFileInCanvas (file) {\n let img\n try {\n img = await createImageBitmap(file)\n } catch (e) {\n const dataUrl = await getDataUrlFromFile(file)\n img = await loadImage(dataUrl)\n }\n const canvas = drawImageInCanvas(img)\n return [img, canvas]\n}\n\n/**\n * canvasToFile\n *\n * @param canvas\n * @param {string} fileType\n * @param {string} fileName\n * @param {number} fileLastModified\n * @param {number} [quality]\n * @returns {Promise}\n */\nexport async function canvasToFile (canvas, fileType, fileName, fileLastModified, quality = 1) {\n let file\n if (typeof OffscreenCanvas === 'function' && canvas instanceof OffscreenCanvas) {\n file = await canvas.convertToBlob({ type: fileType, quality })\n file.name = fileName\n file.lastModified = fileLastModified\n } else {\n const dataUrl = canvas.toDataURL(fileType, quality)\n file = await getFilefromDataUrl(dataUrl, fileName, fileLastModified)\n }\n return file\n}\n\n/**\n * getExifOrientation\n * get image exif orientation info\n * source: https://stackoverflow.com/a/32490603/10395024\n *\n * @param {File} file\n * @returns {Promise} - orientation id, see https://i.stack.imgur.com/VGsAj.gif\n */\nexport function getExifOrientation (file) {\n return new Promise((resolve, reject) => {\n const reader = new CustomFileReader()\n reader.onload = (e) => {\n const view = new DataView(e.target.result)\n if (view.getUint16(0, false) != 0xFFD8) {\n return resolve(-2)\n }\n const length = view.byteLength\n let offset = 2\n while (offset < length) {\n if (view.getUint16(offset + 2, false) <= 8) return resolve(-1)\n const marker = view.getUint16(offset, false)\n offset += 2\n if (marker == 0xFFE1) {\n if (view.getUint32(offset += 2, false) != 0x45786966) {\n return resolve(-1)\n }\n\n var little = view.getUint16(offset += 6, false) == 0x4949\n offset += view.getUint32(offset + 4, little)\n var tags = view.getUint16(offset, little)\n offset += 2\n for (var i = 0; i < tags; i++) {\n if (view.getUint16(offset + (i * 12), little) == 0x0112) {\n return resolve(view.getUint16(offset + (i * 12) + 8, little))\n }\n }\n } else if ((marker & 0xFF00) != 0xFF00) {\n break\n } else {\n offset += view.getUint16(offset, false)\n }\n }\n return resolve(-1)\n }\n reader.onerror = (e) => reject(e)\n reader.readAsArrayBuffer(file)\n })\n}\n\n/**\n *\n * @param {HTMLCanvasElement} canvas\n * @param options\n * @returns {HTMLCanvasElement>}\n */\nexport function handleMaxWidthOrHeight (canvas, options) {\n const width = canvas.width\n const height = canvas.height\n const maxWidthOrHeight = options.maxWidthOrHeight\n\n const needToHandle = Number.isFinite(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight)\n\n let newCanvas = canvas\n let ctx\n\n if (needToHandle) {\n [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n if (width > height) {\n newCanvas.width = maxWidthOrHeight\n newCanvas.height = (height / width) * maxWidthOrHeight\n } else {\n newCanvas.width = (width / height) * maxWidthOrHeight\n newCanvas.height = maxWidthOrHeight\n }\n ctx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height)\n\n cleanupCanvasMemory(canvas)\n }\n\n return newCanvas\n}\n\n/**\n * followExifOrientation\n * source: https://stackoverflow.com/a/40867559/10395024\n *\n * @param {HTMLCanvasElement} canvas\n * @param {number} exifOrientation\n * @returns {HTMLCanvasElement} canvas\n */\nexport function followExifOrientation (canvas, exifOrientation) {\n const width = canvas.width\n const height = canvas.height\n\n const [newCanvas, ctx] = getNewCanvasAndCtx(width, height)\n\n // set proper canvas dimensions before transform & export\n if (4 < exifOrientation && exifOrientation < 9) {\n newCanvas.width = height\n newCanvas.height = width\n } else {\n newCanvas.width = width\n newCanvas.height = height\n }\n\n // transform context before drawing image\n switch (exifOrientation) {\n case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;\n case 3: ctx.transform(-1, 0, 0, -1, width, height); break;\n case 4: ctx.transform(1, 0, 0, -1, 0, height); break;\n case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;\n case 6: ctx.transform(0, 1, -1, 0, height, 0); break;\n case 7: ctx.transform(0, -1, -1, 0, height, width); break;\n case 8: ctx.transform(0, -1, 1, 0, 0, width); break;\n default: break;\n }\n\n ctx.drawImage(canvas, 0, 0, width, height)\n\n cleanupCanvasMemory(canvas)\n\n return newCanvas\n}\n\n/**\n * get new Canvas and it's context\n * @param width\n * @param height\n * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}\n */\nexport function getNewCanvasAndCtx (width, height) {\n let canvas\n let ctx\n try {\n canvas = new OffscreenCanvas(width, height)\n ctx = canvas.getContext('2d')\n if (ctx === null) {\n throw new Error('getContext of OffscreenCanvas returns null')\n }\n } catch (e) {\n canvas = document.createElement('canvas')\n ctx = canvas.getContext('2d')\n }\n canvas.width = width\n canvas.height = height\n return [canvas, ctx]\n}\n\n/**\n * clear Canvas memory\n * @param canvas\n * @returns null\n */\nexport function cleanupCanvasMemory (canvas) {\n canvas.width = 0\n canvas.height = 0\n}\n\nif (isBrowser) {\n// Polyfill for Number.isInteger\n Number.isInteger = Number.isInteger || function (value) {\n return typeof value === 'number' &&\n isFinite(value) &&\n Math.floor(value) === value\n }\n}\n","import {\n canvasToFile,\n cleanupCanvasMemory,\n drawFileInCanvas,\n followExifOrientation,\n getExifOrientation,\n getNewCanvasAndCtx,\n handleMaxWidthOrHeight\n} from './utils'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nexport default async function compress (file, options) {\n let progress = 0\n\n function incProgress (inc = 5) {\n progress += inc\n if (typeof options.onProgress === 'function') {\n options.onProgress(Math.min(progress, 100))\n }\n }\n\n function setProgress (p) {\n progress = Math.min(Math.max(p, progress), 100)\n if (typeof options.onProgress === 'function') {\n options.onProgress(progress)\n }\n }\n\n let remainingTrials = options.maxIteration || 10\n\n const maxSizeByte = options.maxSizeMB * 1024 * 1024\n\n incProgress()\n\n // drawFileInCanvas\n let [img, origCanvas] = await drawFileInCanvas(file)\n\n incProgress()\n\n // handleMaxWidthOrHeight\n const maxWidthOrHeightFixedCanvas = handleMaxWidthOrHeight(origCanvas, options)\n\n incProgress()\n\n // exifOrientation\n options.exifOrientation = options.exifOrientation || await getExifOrientation(file)\n incProgress()\n const orientationFixedCanvas = followExifOrientation(maxWidthOrHeightFixedCanvas, options.exifOrientation)\n incProgress()\n\n let quality = 1\n\n let tempFile = await canvasToFile(orientationFixedCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n incProgress()\n\n const origExceedMaxSize = tempFile.size > maxSizeByte\n const sizeBecomeLarger = tempFile.size > file.size\n\n // check if we need to compress or resize\n if (!origExceedMaxSize && !sizeBecomeLarger) {\n // no need to compress\n setProgress(100)\n return tempFile\n }\n\n const sourceSize = file.size\n const renderedSize = tempFile.size\n let currentSize = renderedSize\n let compressedFile\n let newCanvas, ctx\n let canvas = orientationFixedCanvas\n while (remainingTrials-- && (currentSize > maxSizeByte || currentSize > sourceSize)) {\n const newWidth = origExceedMaxSize ? canvas.width * 0.95 : canvas.width\n const newHeight = origExceedMaxSize ? canvas.height * 0.95 : canvas.height;\n [newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight)\n\n ctx.drawImage(canvas, 0, 0, newWidth, newHeight)\n\n if (file.type === 'image/jpeg') {\n quality *= 0.95\n }\n compressedFile = await canvasToFile(newCanvas, options.fileType || file.type, file.name, file.lastModified, quality)\n\n cleanupCanvasMemory(canvas)\n\n canvas = newCanvas\n\n currentSize = compressedFile.size\n setProgress(Math.min(99, Math.floor((renderedSize - currentSize) / (renderedSize - maxSizeByte) * 100)))\n }\n\n // garbage clean canvas for safari\n // ref: https://bugs.webkit.org/show_bug.cgi?id=195325\n cleanupCanvasMemory(canvas)\n cleanupCanvasMemory(newCanvas)\n cleanupCanvasMemory(maxWidthOrHeightFixedCanvas)\n cleanupCanvasMemory(orientationFixedCanvas)\n cleanupCanvasMemory(origCanvas)\n\n setProgress(100)\n return compressedFile\n}\n","import lib from './index'\nimport compress from './image-compression'\nimport { getNewCanvasAndCtx } from './utils'\n\nlet cnt = 0\nlet imageCompressionLibUrl\nlet worker\n\nfunction createWorker (script) {\n if (typeof script === 'function') {\n script = `(${f})()`\n }\n return new Worker(URL.createObjectURL(new Blob([script])))\n}\n\nfunction createSourceObject (str) {\n return URL.createObjectURL(new Blob([str], { type: 'application/javascript' }))\n}\n\nfunction generateLib () {\n // prepare the lib to be used inside WebWorker\n return createSourceObject(`\n function imageCompression (){return (${lib}).apply(null, arguments)}\n\n imageCompression.getDataUrlFromFile = ${lib.getDataUrlFromFile}\n imageCompression.getFilefromDataUrl = ${lib.getFilefromDataUrl}\n imageCompression.loadImage = ${lib.loadImage}\n imageCompression.drawImageInCanvas = ${lib.drawImageInCanvas}\n imageCompression.drawFileInCanvas = ${lib.drawFileInCanvas}\n imageCompression.canvasToFile = ${lib.canvasToFile}\n imageCompression.getExifOrientation = ${lib.getExifOrientation}\n imageCompression.handleMaxWidthOrHeight = ${lib.handleMaxWidthOrHeight}\n imageCompression.followExifOrientation = ${lib.followExifOrientation}\n imageCompression.cleanupMemory = ${lib.cleanupMemory}\n\n getDataUrlFromFile = imageCompression.getDataUrlFromFile\n getFilefromDataUrl = imageCompression.getFilefromDataUrl\n loadImage = imageCompression.loadImage\n drawImageInCanvas = imageCompression.drawImageInCanvas\n drawFileInCanvas = imageCompression.drawFileInCanvas\n canvasToFile = imageCompression.canvasToFile\n getExifOrientation = imageCompression.getExifOrientation\n handleMaxWidthOrHeight = imageCompression.handleMaxWidthOrHeight\n followExifOrientation = imageCompression.followExifOrientation\n cleanupMemory = imageCompression.cleanupMemory\n\n getNewCanvasAndCtx = ${getNewCanvasAndCtx}\n \n CustomFileReader = FileReader\n \n CustomFile = File\n \n function _slicedToArray(arr, n) { return arr }\n \n function _typeof(a) { return typeof a }\n\n function compress (){return (${compress}).apply(null, arguments)}\n `)\n}\n\nfunction generateWorkerScript () {\n // code to be run in the WebWorker\n return createWorker(`\n let scriptImported = false\n self.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\\\n' + e.stack, id })\n }\n })\n `)\n}\n\nexport function compressOnWebWorker (file, options) {\n return new Promise(async (resolve, reject) => {\n let id = cnt++\n\n if (!imageCompressionLibUrl) {\n imageCompressionLibUrl = generateLib()\n }\n\n if (!worker) {\n worker = generateWorkerScript()\n }\n\n function handler (e) {\n if (e.data.id === id) {\n if (e.data.progress !== undefined && e.data.progress < 100) {\n options.onProgress(e.data.progress)\n return\n }\n worker.removeEventListener('message', handler)\n if (e.data.error) {\n reject(new Error(e.data.error))\n }\n resolve(e.data.file)\n }\n }\n\n worker.addEventListener('message', handler)\n worker.postMessage({\n file,\n id,\n imageCompressionLibUrl,\n options: { ...options, onProgress: undefined }\n })\n })\n}\n","import compress from './image-compression'\nimport {\n canvasToFile,\n drawFileInCanvas,\n drawImageInCanvas,\n getDataUrlFromFile,\n getFilefromDataUrl,\n loadImage,\n getExifOrientation,\n handleMaxWidthOrHeight,\n followExifOrientation,\n CustomFile,\n cleanupCanvasMemory\n} from './utils'\nimport { compressOnWebWorker } from './web-worker'\n\n/**\n * Compress an image file.\n *\n * @param {File} file\n * @param {Object} options - { maxSizeMB=Number.POSITIVE_INFINITY, maxWidthOrHeight, useWebWorker=false, maxIteration = 10, exifOrientation, fileType }\n * @param {number} [options.maxSizeMB=Number.POSITIVE_INFINITY]\n * @param {number} [options.maxWidthOrHeight=undefined]\n * @param {boolean} [options.useWebWorker=false]\n * @param {number} [options.maxIteration=10]\n * @param {number} [options.exifOrientation] - default to be the exif orientation from the image file\n * @param {Function} [options.onProgress] - a function takes one progress argument (progress from 0 to 100)\n * @param {string} [options.fileType] - default to be the original mime type from the image file\n * @returns {Promise}\n */\nasync function imageCompression (file, options) {\n\n let compressedFile\n\n options.maxSizeMB = options.maxSizeMB || Number.POSITIVE_INFINITY\n const useWebWorker = typeof options.useWebWorker === 'boolean' ? options.useWebWorker : false\n delete options.useWebWorker\n\n if (!(file instanceof Blob || file instanceof CustomFile)) {\n throw new Error('The file given is not an instance of Blob or File')\n } else if (!/^image/.test(file.type)) {\n throw new Error('The file given is not an image')\n }\n\n // try run in web worker, fall back to run in main thread\n const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope\n\n // if ((useWebWorker && typeof Worker === 'function') || inWebWorker) {\n // console.log('run compression in web worker')\n // } else {\n // console.log('run compression in main thread')\n // }\n\n if (useWebWorker && typeof Worker === 'function' && !inWebWorker) {\n try {\n // console.log(1)\n // \"compressOnWebWorker\" is kind of like a recursion to call \"imageCompression\" again inside web worker\n compressedFile = await compressOnWebWorker(file, options)\n } catch (e) {\n // console.warn('Run compression in web worker failed:', e, ', fall back to main thread')\n // console.log(1.5)\n compressedFile = await compress(file, options)\n }\n } else {\n // console.log(2)\n compressedFile = await compress(file, options)\n }\n\n try {\n compressedFile.name = file.name\n compressedFile.lastModified = file.lastModified\n } catch (e) {}\n\n return compressedFile\n}\n\nimageCompression.getDataUrlFromFile = getDataUrlFromFile\nimageCompression.getFilefromDataUrl = getFilefromDataUrl\nimageCompression.loadImage = loadImage\nimageCompression.drawImageInCanvas = drawImageInCanvas\nimageCompression.drawFileInCanvas = drawFileInCanvas\nimageCompression.canvasToFile = canvasToFile\nimageCompression.getExifOrientation = getExifOrientation\nimageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight\nimageCompression.followExifOrientation = followExifOrientation\nimageCompression.cleanupMemory = cleanupCanvasMemory\nimageCompression.version = '1.0.8'\n\nexport default imageCompression\n"],"names":["isBrowser","window","moduleMapper","getDataUrlFromFile","resolve","reader","dataurl","split","n","bstr","filename","file","lastModified","canvas","view","length","offset","getUint32","marker","maxWidthOrHeight","width","followExifOrientation","height","ctx","inc","drawFileInCanvas","exifOrientation","orientationFixedCanvas","options","fileType","imageCompressionLibUrl","cnt","generateLib","script","imageCompression","getFilefromDataUrl","loadImage","drawImageInCanvas","canvasToFile","getExifOrientation"],"mappings":";;;;;;;4yCAAA,IAAMA,EAA8B,oBAAlBC,2EAKoF,uCAQ5DC,2HAiBnC,SAASC,mDACPC,6DAEqBC,uNAiCdC,EAAAC,mCACK,gBAEjBC,EAAIC,6BAEJD,8DAUYE,EACdC,EAAAC,eACAR,28BAwC2CS,qcAoB7CC,mEAEMV,mCAGQW,6DAEgBC,8GASpBF,EAAAG,UAAgBD,0GAKPA,EAAA,iCAGTE,0KA+BVC,oJAqBuBC,4CAQTP,EAAV,0CAEAA,qBASYQ,yCACZC,mPAmBuCA,wJAI9B,sHA8BbC,2BAHWD,kaC3OTE,iLAPK,6DAUGC,iBAARd,yKAUMe,sUAWAC,EAAwBC,EAAAC,6PAsB5BhB,o8BC/FJ,IACIiB,IADAC,IAcJ,SAASC,okEAPPC,gkDCyDwBtB,mzBAa1BuB,iBAAA/B,mBAAsCA,mBACtC+B,iBAAAC,sCACAD,iBAAAE,oBACAF,iBAAAG,oCACAH,iBAAAT,kCAEAS,iBAAAI,0BAxFAJ,iBAAAK"} \ No newline at end of file diff --git a/example/basic.html b/example/basic.html index 347cbd3..f7bb9d7 100644 --- a/example/basic.html +++ b/example/basic.html @@ -68,8 +68,8 @@ console.log('ExifOrientation', await imageCompression.getExifOrientation(file)) var options = { - maxSizeMB: document.querySelector('#maxSizeMB').value, - maxWidthOrHeight: document.querySelector('#maxWidthOrHeight').value, + maxSizeMB: parseFloat(document.querySelector('#maxSizeMB').value), + maxWidthOrHeight: parseFloat(document.querySelector('#maxWidthOrHeight').value), useWebWorker: useWebWorker, onProgress: onProgress } diff --git a/example/development.html b/example/development.html index f5c8c7d..b9d4548 100644 --- a/example/development.html +++ b/example/development.html @@ -68,8 +68,8 @@ console.log('ExifOrientation', await imageCompression.getExifOrientation(file)) var options = { - maxSizeMB: document.querySelector('#maxSizeMB').value, - maxWidthOrHeight: document.querySelector('#maxWidthOrHeight').value, + maxSizeMB: parseFloat(document.querySelector('#maxSizeMB').value), + maxWidthOrHeight: parseFloat(document.querySelector('#maxWidthOrHeight').value), useWebWorker: useWebWorker, onProgress: onProgress } diff --git a/lib/image-compression.js b/lib/image-compression.js index 53028e2..effa8ae 100644 --- a/lib/image-compression.js +++ b/lib/image-compression.js @@ -64,27 +64,32 @@ export default async function compress (file, options) { let tempFile = await canvasToFile(orientationFixedCanvas, options.fileType || file.type, file.name, file.lastModified, quality) incProgress() + + const origExceedMaxSize = tempFile.size > maxSizeByte + const sizeBecomeLarger = tempFile.size > file.size + // check if we need to compress or resize - if (tempFile.size <= maxSizeByte) { + if (!origExceedMaxSize && !sizeBecomeLarger) { // no need to compress setProgress(100) return tempFile } - const originalSize = tempFile.size - let currentSize = originalSize + const sourceSize = file.size + const renderedSize = tempFile.size + let currentSize = renderedSize let compressedFile let newCanvas, ctx let canvas = orientationFixedCanvas - while (remainingTrials-- && currentSize > maxSizeByte) { - const newWidth = canvas.width * 0.9 - const newHeight = canvas.height * 0.9; + while (remainingTrials-- && (currentSize > maxSizeByte || currentSize > sourceSize)) { + const newWidth = origExceedMaxSize ? canvas.width * 0.95 : canvas.width + const newHeight = origExceedMaxSize ? canvas.height * 0.95 : canvas.height; [newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight) ctx.drawImage(canvas, 0, 0, newWidth, newHeight) if (file.type === 'image/jpeg') { - quality *= 0.9 + quality *= 0.95 } compressedFile = await canvasToFile(newCanvas, options.fileType || file.type, file.name, file.lastModified, quality) @@ -93,7 +98,7 @@ export default async function compress (file, options) { canvas = newCanvas currentSize = compressedFile.size - setProgress(Math.min(99, Math.floor((originalSize - currentSize) / (originalSize - maxSizeByte) * 100))) + setProgress(Math.min(99, Math.floor((renderedSize - currentSize) / (renderedSize - maxSizeByte) * 100))) } // garbage clean canvas for safari diff --git a/lib/utils.js b/lib/utils.js index 91bb5b9..49da2ac 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -186,7 +186,7 @@ export function handleMaxWidthOrHeight (canvas, options) { const height = canvas.height const maxWidthOrHeight = options.maxWidthOrHeight - const needToHandle = Number.isInteger(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight) + const needToHandle = Number.isFinite(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight) let newCanvas = canvas let ctx