UNPKG

ngx-pica

Version:

Angular 5 module to resize images files in browser

708 lines (698 loc) 23.5 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('rxjs/Subject'), require('exif-js'), require('pica/dist/pica')) : typeof define === 'function' && define.amd ? define(['exports', '@angular/core', 'rxjs/Subject', 'exif-js', 'pica/dist/pica'], factory) : (factory((global['ngx-pica'] = {}),global.ng.core,global.Rx,global.EXIF,global.pica)); }(this, (function (exports,core,Subject,EXIF,pica) { 'use strict'; pica = pica && pica.hasOwnProperty('default') ? pica['default'] : pica; var NgxPicaErrorType; (function (NgxPicaErrorType) { NgxPicaErrorType["NO_FILES_RECEIVED"] = "NO_FILES_RECEIVED"; NgxPicaErrorType["CANVAS_CONTEXT_IDENTIFIER_NOT_SUPPORTED"] = "CANVAS_CONTEXT_IDENTIFIER_NOT_SUPPORTED"; NgxPicaErrorType["NOT_BE_ABLE_TO_COMPRESS_ENOUGH"] = "NOT_BE_ABLE_TO_COMPRESS_ENOUGH"; })(NgxPicaErrorType || (NgxPicaErrorType = {})); var NgxPicaExifService = (function () { function NgxPicaExifService() { } NgxPicaExifService.prototype.getExifOrientedImage = function (image) { return new Promise(function (resolve, reject) { EXIF.getData(image, function () { var allExifMetaData = EXIF.getAllTags(image), exifOrientation = allExifMetaData.Orientation; if (exifOrientation) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), width = image.width, height = image.height; if ([5, 6, 7, 8].indexOf(exifOrientation) > -1) { canvas.width = height; canvas.height = width; } else { canvas.width = width; canvas.height = height; } if (ctx) { // transform context before drawing image switch (exifOrientation) { case 2: ctx.transform(-1, 0, 0, 1, width, 0); break; case 3: ctx.transform(-1, 0, 0, -1, width, height); break; case 4: ctx.transform(1, 0, 0, -1, 0, height); break; case 5: ctx.transform(0, 1, 1, 0, 0, 0); break; case 6: ctx.transform(0, 1, -1, 0, height, 0); break; case 7: ctx.transform(0, -1, -1, 0, height, width); break; case 8: ctx.transform(0, -1, 1, 0, 0, width); break; default: ctx.transform(1, 0, 0, 1, 0, 0); } // Draw img into canvas ctx.drawImage(image, 0, 0, width, height); var img_1 = new Image(); img_1.width = width; img_1.height = height; img_1.onload = function () { resolve(img_1); }; img_1.src = canvas.toDataURL(); } } else { resolve(image); } }); }); }; NgxPicaExifService.decorators = [ { type: core.Injectable }, ]; /** @nocollapse */ NgxPicaExifService.ctorParameters = function () { return []; }; return NgxPicaExifService; }()); var NgxPicaService = (function () { function NgxPicaService(_ngxPicaExifService) { this._ngxPicaExifService = _ngxPicaExifService; this.picaResizer = new pica(); this.MAX_STEPS = 20; if (!this.picaResizer || !this.picaResizer.resize) { this.picaResizer = new window.pica(); } } /** * Resize images array * @param {File[]} files * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ /** * Resize images array * @param {File[]} files * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ NgxPicaService.prototype.resizeImages = /** * Resize images array * @param {File[]} files * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ function (files, width, height, options) { var _this = this; var resizedImage = new Subject.Subject(); var totalFiles = files.length; if (totalFiles > 0) { var nextFile_1 = new Subject.Subject(); var index_1 = 0; var subscription_1 = nextFile_1.subscribe(function (file) { _this.resizeImage(file, width, height, options).subscribe(function (imageResized) { index_1++; resizedImage.next(imageResized); if (index_1 < totalFiles) { nextFile_1.next(files[index_1]); } else { resizedImage.complete(); subscription_1.unsubscribe(); } }, function (err) { var ngxPicaError = { file: file, err: err }; resizedImage.error(ngxPicaError); }); }); nextFile_1.next(files[index_1]); } else { var ngxPicaError = { err: NgxPicaErrorType.NO_FILES_RECEIVED }; resizedImage.error(ngxPicaError); resizedImage.complete(); } return resizedImage.asObservable(); }; /** * Resize image file * * @param {File} file * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ /** * Resize image file * * @param {File} file * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ NgxPicaService.prototype.resizeImage = /** * Resize image file * * @param {File} file * @param {number} width * @param {number} height * @param {NgxPicaResizeOptionsInterface} options * @returns {Observable<File>} */ function (file, width, height, options) { var _this = this; var resizedImage = new Subject.Subject(); var originCanvas = document.createElement('canvas'); var ctx = originCanvas.getContext('2d'); var img = new Image(); if (ctx) { img.onload = function () { _this._ngxPicaExifService.getExifOrientedImage(img).then(function (orientedImage) { window.URL.revokeObjectURL(img.src); originCanvas.width = orientedImage.width; originCanvas.height = orientedImage.height; ctx.drawImage(orientedImage, 0, 0); var imageData = ctx.getImageData(0, 0, orientedImage.width, orientedImage.height); if (options && options.aspectRatio && options.aspectRatio.keepAspectRatio) { var ratio = 0; if (options.aspectRatio.forceMinDimensions) { ratio = Math.max(width / imageData.width, height / imageData.height); } else { ratio = Math.min(width / imageData.width, height / imageData.height); } width = Math.round(imageData.width * ratio); height = Math.round(imageData.height * ratio); } var destinationCanvas = document.createElement('canvas'); destinationCanvas.width = width; destinationCanvas.height = height; _this.picaResize(file, originCanvas, destinationCanvas, options) .catch(function (err) { return resizedImage.error(err); }) .then(function (imgResized) { resizedImage.next(imgResized); }); }); }; img.src = window.URL.createObjectURL(file); } else { resizedImage.error(NgxPicaErrorType.CANVAS_CONTEXT_IDENTIFIER_NOT_SUPPORTED); } return resizedImage.asObservable(); }; /** * Compress images array * * @param {File[]} files * @param {number} sizeInMB * @returns {Observable<File>} */ /** * Compress images array * * @param {File[]} files * @param {number} sizeInMB * @returns {Observable<File>} */ NgxPicaService.prototype.compressImages = /** * Compress images array * * @param {File[]} files * @param {number} sizeInMB * @returns {Observable<File>} */ function (files, sizeInMB) { var _this = this; var compressedImage = new Subject.Subject(); var totalFiles = files.length; if (totalFiles > 0) { var nextFile_2 = new Subject.Subject(); var index_2 = 0; var subscription_2 = nextFile_2.subscribe(function (file) { _this.compressImage(file, sizeInMB).subscribe(function (imageCompressed) { index_2++; compressedImage.next(imageCompressed); if (index_2 < totalFiles) { nextFile_2.next(files[index_2]); } else { compressedImage.complete(); subscription_2.unsubscribe(); } }, function (err) { var ngxPicaError = { file: file, err: err }; compressedImage.error(ngxPicaError); }); }); nextFile_2.next(files[index_2]); } else { var ngxPicaError = { err: NgxPicaErrorType.NO_FILES_RECEIVED }; compressedImage.error(ngxPicaError); compressedImage.complete(); } return compressedImage.asObservable(); }; /** * Compress image file * * @param {File} file * @param {number} sizeInMB * @returns {Observable<File>} */ /** * Compress image file * * @param {File} file * @param {number} sizeInMB * @returns {Observable<File>} */ NgxPicaService.prototype.compressImage = /** * Compress image file * * @param {File} file * @param {number} sizeInMB * @returns {Observable<File>} */ function (file, sizeInMB) { var _this = this; var compressedImage = new Subject.Subject(); if (this.bytesToMB(file.size) <= sizeInMB) { setTimeout(function () { compressedImage.next(file); }); } else { var originCanvas_1 = document.createElement('canvas'); var ctx_1 = originCanvas_1.getContext('2d'); var img_1 = new Image(); if (ctx_1) { img_1.onload = function () { _this._ngxPicaExifService.getExifOrientedImage(img_1).then(function (orientedImage) { window.URL.revokeObjectURL(img_1.src); originCanvas_1.width = orientedImage.width; originCanvas_1.height = orientedImage.height; ctx_1.drawImage(orientedImage, 0, 0); _this.getCompressedImage(originCanvas_1, file.type, 1, sizeInMB, 0) .catch(function (err) { return compressedImage.error(err); }) .then(function (blob) { var imgCompressed = _this.blobToFile(blob, file.name, file.type, new Date().getTime()); compressedImage.next(imgCompressed); }); }); }; img_1.src = window.URL.createObjectURL(file); } else { compressedImage.error(NgxPicaErrorType.CANVAS_CONTEXT_IDENTIFIER_NOT_SUPPORTED); } } return compressedImage.asObservable(); }; /** * Through Pica toBlob method, compress image file * * @param {HTMLCanvasElement} canvas * @param {string} type * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ /** * Through Pica toBlob method, compress image file * * @param {HTMLCanvasElement} canvas * @param {string} type * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ NgxPicaService.prototype.getCompressedImage = /** * Through Pica toBlob method, compress image file * * @param {HTMLCanvasElement} canvas * @param {string} type * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ function (canvas, type, quality, sizeInMB, step) { var _this = this; return new Promise(function (resolve, reject) { _this.picaResizer.toBlob(canvas, type, quality) .catch(function (err) { return reject(err); }) .then(function (blob) { _this.checkCompressedImageSize(canvas, blob, quality, sizeInMB, step) .catch(function (err) { return reject(err); }) .then(function (blob) { resolve(blob); }); }); }); }; /** * Check if image has been compressed enough * * @param {HTMLCanvasElement} canvas * @param {Blob} blob * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ /** * Check if image has been compressed enough * * @param {HTMLCanvasElement} canvas * @param {Blob} blob * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ NgxPicaService.prototype.checkCompressedImageSize = /** * Check if image has been compressed enough * * @param {HTMLCanvasElement} canvas * @param {Blob} blob * @param {number} quality * @param {number} sizeInMB * @param {number} step * @returns {Promise<Blob>} */ function (canvas, blob, quality, sizeInMB, step) { var _this = this; return new Promise(function (resolve, reject) { if (step > _this.MAX_STEPS) { reject(NgxPicaErrorType.NOT_BE_ABLE_TO_COMPRESS_ENOUGH); } if (_this.bytesToMB(blob.size) < sizeInMB) { resolve(blob); } else { var newQuality = quality - (quality * 0.1); var newStep = step + 1; // recursively compression resolve(_this.getCompressedImage(canvas, blob.type, newQuality, sizeInMB, newStep)); } }); }; /** * Through Pica resize method, resize image file * * @param {File} file * @param {HTMLCanvasElement} from * @param {HTMLCanvasElement} to * @param options * @returns {Promise<File>} */ /** * Through Pica resize method, resize image file * * @param {File} file * @param {HTMLCanvasElement} from * @param {HTMLCanvasElement} to * @param options * @returns {Promise<File>} */ NgxPicaService.prototype.picaResize = /** * Through Pica resize method, resize image file * * @param {File} file * @param {HTMLCanvasElement} from * @param {HTMLCanvasElement} to * @param options * @returns {Promise<File>} */ function (file, from, to, options) { var _this = this; return new Promise(function (resolve, reject) { _this.picaResizer.resize(from, to, options) .catch(function (err) { return reject(err); }) .then(function (resizedCanvas) { return _this.picaResizer.toBlob(resizedCanvas, file.type); }) .then(function (blob) { var fileResized = _this.blobToFile(blob, file.name, file.type, new Date().getTime()); resolve(fileResized); }); }); }; /** * Return new File from Blob * * @param {Blob} blob * @param {string} name * @param {string} type * @param {number} lastModified * @returns {File} */ /** * Return new File from Blob * * @param {Blob} blob * @param {string} name * @param {string} type * @param {number} lastModified * @returns {File} */ NgxPicaService.prototype.blobToFile = /** * Return new File from Blob * * @param {Blob} blob * @param {string} name * @param {string} type * @param {number} lastModified * @returns {File} */ function (blob, name, type, lastModified) { return new File([blob], name, { type: type, lastModified: lastModified }); }; /** * Convert bytes to MegaBytes * * @param {number} bytes * @returns {number} */ /** * Convert bytes to MegaBytes * * @param {number} bytes * @returns {number} */ NgxPicaService.prototype.bytesToMB = /** * Convert bytes to MegaBytes * * @param {number} bytes * @returns {number} */ function (bytes) { return bytes / 1048576; }; NgxPicaService.decorators = [ { type: core.Injectable }, ]; /** @nocollapse */ NgxPicaService.ctorParameters = function () { return [ { type: NgxPicaExifService, }, ]; }; return NgxPicaService; }()); var NgxPicaImageService = (function () { function NgxPicaImageService() { this.imageExtensions = [ "ase", "art", "bmp", "blp", "cd5", "cit", "cpt", "cr2", "cut", "dds", "dib", "djvu", "egt", "exif", "gif", "gpl", "grf", "icns", "ico", "iff", "jng", "jpeg", "jpg", "jfif", "jp2", "jps", "lbm", "max", "miff", "mng", "msp", "nitf", "ota", "pbm", "pc1", "pc2", "pc3", "pcf", "pcx", "pdn", "pgm", "PI1", "PI2", "PI3", "pict", "pct", "pnm", "pns", "ppm", "psb", "psd", "pdd", "psp", "px", "pxm", "pxr", "qfx", "raw", "rle", "sct", "sgi", "rgb", "int", "bw", "tga", "tiff", "tif", "vtf", "xbm", "xcf", "xpm", "3dv", "amf", "ai", "awg", "cgm", "cdr", "cmx", "dxf", "e2d", "egt", "eps", "fs", "gbr", "odg", "svg", "stl", "vrml", "x3d", "sxd", "v2d", "vnd", "wmf", "emf", "art", "xar", "png", "webp", "jxr", "hdp", "wdp", "cur", "ecw", "iff", "lbm", "liff", "nrrd", "pam", "pcx", "pgf", "sgi", "rgb", "rgba", "bw", "int", "inta", "sid", "ras", "sun", "tga" ]; } /** * Check if given file is an image or not * * @param {File} file * @returns {boolean} */ /** * Check if given file is an image or not * * @param {File} file * @returns {boolean} */ NgxPicaImageService.prototype.isImage = /** * Check if given file is an image or not * * @param {File} file * @returns {boolean} */ function (file) { var fileExtension = file.name.toLowerCase().substr(file.name.lastIndexOf('.') + 1); return (this.imageExtensions.indexOf(fileExtension) !== -1); }; NgxPicaImageService.decorators = [ { type: core.Injectable }, ]; /** @nocollapse */ NgxPicaImageService.ctorParameters = function () { return []; }; return NgxPicaImageService; }()); var NgxPicaModule = (function () { function NgxPicaModule() { } NgxPicaModule.decorators = [ { type: core.NgModule, args: [{ providers: [ { provide: NgxPicaService, useClass: NgxPicaService }, { provide: NgxPicaExifService, useClass: NgxPicaExifService }, { provide: NgxPicaImageService, useClass: NgxPicaImageService }, ] },] }, ]; /** @nocollapse */ NgxPicaModule.ctorParameters = function () { return []; }; return NgxPicaModule; }()); exports.NgxPicaModule = NgxPicaModule; exports.NgxPicaService = NgxPicaService; exports.NgxPicaImageService = NgxPicaImageService; Object.defineProperty(exports, '__esModule', { value: true }); })));