ngx-image-cropper 7.1.1 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/component/image-cropper.component.mjs +3 -2
- package/esm2020/lib/services/load-image.service.mjs +63 -34
- package/fesm2015/ngx-image-cropper.mjs +71 -32
- package/fesm2015/ngx-image-cropper.mjs.map +1 -1
- package/fesm2020/ngx-image-cropper.mjs +64 -34
- package/fesm2020/ngx-image-cropper.mjs.map +1 -1
- package/lib/services/load-image.service.d.ts +2 -1
- package/package.json +1 -1
|
@@ -2,7 +2,8 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { Injectable, EventEmitter, isDevMode, Component, ChangeDetectionStrategy, Optional, Inject, ViewChild, Input, HostBinding, Output, HostListener, NgModule } from '@angular/core';
|
|
3
3
|
import * as i4 from '@angular/platform-browser';
|
|
4
4
|
import { HAMMER_LOADER } from '@angular/platform-browser';
|
|
5
|
-
import {
|
|
5
|
+
import { takeUntil, first } from 'rxjs/operators';
|
|
6
|
+
import { merge, fromEvent } from 'rxjs';
|
|
6
7
|
import * as i5 from '@angular/common';
|
|
7
8
|
import { CommonModule } from '@angular/common';
|
|
8
9
|
|
|
@@ -611,30 +612,30 @@ class LoadImageService {
|
|
|
611
612
|
constructor() {
|
|
612
613
|
this.autoRotateSupported = supportsAutomaticRotation();
|
|
613
614
|
}
|
|
614
|
-
loadImageFile(file, cropperSettings) {
|
|
615
|
-
|
|
616
|
-
|
|
615
|
+
async loadImageFile(file, cropperSettings) {
|
|
616
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
617
|
+
return await this.checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, file.type, cropperSettings);
|
|
617
618
|
}
|
|
618
619
|
checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, imageType, cropperSettings) {
|
|
619
620
|
if (!this.isValidImageType(imageType)) {
|
|
620
621
|
return Promise.reject(new Error('Invalid image type'));
|
|
621
622
|
}
|
|
622
|
-
return this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings);
|
|
623
|
+
return this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings, imageType);
|
|
623
624
|
}
|
|
624
625
|
isValidImageType(type) {
|
|
625
|
-
return /image\/(png|jpg|jpeg|bmp|gif|tiff|webp|x-icon|vnd.microsoft.icon)/.test(type);
|
|
626
|
+
return /image\/(png|jpg|jpeg|bmp|gif|tiff|svg|webp|x-icon|vnd.microsoft.icon)/.test(type);
|
|
626
627
|
}
|
|
627
|
-
loadImageFromURL(url, cropperSettings) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
628
|
+
async loadImageFromURL(url, cropperSettings) {
|
|
629
|
+
const res = await fetch(url);
|
|
630
|
+
const buffer = await res.arrayBuffer();
|
|
631
|
+
return await this.loadImageFromArrayBuffer(buffer, cropperSettings);
|
|
631
632
|
}
|
|
632
633
|
loadBase64Image(imageBase64, cropperSettings) {
|
|
633
634
|
const arrayBuffer = this.base64ToArrayBuffer(imageBase64);
|
|
634
635
|
return this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings);
|
|
635
636
|
}
|
|
636
637
|
base64ToArrayBuffer(imageBase64) {
|
|
637
|
-
imageBase64 = imageBase64.replace(/^data
|
|
638
|
+
imageBase64 = imageBase64.replace(/^data:([^;]+);base64,/gmi, '');
|
|
638
639
|
const binaryString = atob(imageBase64);
|
|
639
640
|
const len = binaryString.length;
|
|
640
641
|
const bytes = new Uint8Array(len);
|
|
@@ -643,23 +644,55 @@ class LoadImageService {
|
|
|
643
644
|
}
|
|
644
645
|
return bytes.buffer;
|
|
645
646
|
}
|
|
646
|
-
loadImageFromArrayBuffer(arrayBuffer, cropperSettings) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
647
|
+
async loadImageFromArrayBuffer(arrayBuffer, cropperSettings, imageType) {
|
|
648
|
+
const res = await new Promise(async (resolve, reject) => {
|
|
649
|
+
try {
|
|
650
|
+
const blob = new Blob([arrayBuffer], imageType ? { type: imageType } : undefined);
|
|
651
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
652
|
+
const originalImage = new Image();
|
|
653
|
+
const isSvg = imageType === 'image/svg+xml';
|
|
654
|
+
const originalImageSize = isSvg ? await this.getSvgImageSize(blob) : undefined;
|
|
655
|
+
originalImage.onload = () => resolve({
|
|
656
|
+
originalImage,
|
|
657
|
+
originalImageSize,
|
|
658
|
+
originalObjectUrl: objectUrl,
|
|
659
|
+
originalArrayBuffer: arrayBuffer
|
|
660
|
+
});
|
|
661
|
+
originalImage.onerror = reject;
|
|
662
|
+
originalImage.src = objectUrl;
|
|
663
|
+
}
|
|
664
|
+
catch (e) {
|
|
665
|
+
reject(e);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
return await this.transformImageFromArrayBuffer(res, cropperSettings, res.originalImageSize != null);
|
|
669
|
+
}
|
|
670
|
+
async getSvgImageSize(blob) {
|
|
671
|
+
const parser = new DOMParser();
|
|
672
|
+
const doc = parser.parseFromString(await blob.text(), 'image/svg+xml');
|
|
673
|
+
const svgElement = doc.querySelector('svg');
|
|
674
|
+
if (!svgElement) {
|
|
675
|
+
throw Error('Failed to parse SVG image');
|
|
676
|
+
}
|
|
677
|
+
const widthAttr = svgElement.getAttribute('width');
|
|
678
|
+
const heightAttr = svgElement.getAttribute('height');
|
|
679
|
+
if (widthAttr && heightAttr) {
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
const viewBoxAttr = svgElement.getAttribute('viewBox')
|
|
683
|
+
|| svgElement.getAttribute('viewbox');
|
|
684
|
+
if (viewBoxAttr) {
|
|
685
|
+
const viewBox = viewBoxAttr.split(' ');
|
|
686
|
+
return {
|
|
687
|
+
width: +viewBox[2],
|
|
688
|
+
height: +viewBox[3]
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
throw Error('Failed to load SVG image. SVG must have width + height or viewBox definition.');
|
|
659
692
|
}
|
|
660
|
-
async transformImageFromArrayBuffer(res, cropperSettings) {
|
|
693
|
+
async transformImageFromArrayBuffer(res, cropperSettings, forceTransform = false) {
|
|
661
694
|
const autoRotate = await this.autoRotateSupported;
|
|
662
|
-
const exifTransform =
|
|
695
|
+
const exifTransform = getTransformationsFromExifData(autoRotate ? -1 : res.originalArrayBuffer);
|
|
663
696
|
if (!res.originalImage || !res.originalImage.complete) {
|
|
664
697
|
return Promise.reject(new Error('No image loaded'));
|
|
665
698
|
}
|
|
@@ -667,22 +700,19 @@ class LoadImageService {
|
|
|
667
700
|
original: {
|
|
668
701
|
objectUrl: res.originalObjectUrl,
|
|
669
702
|
image: res.originalImage,
|
|
670
|
-
size: {
|
|
703
|
+
size: res.originalImageSize ?? {
|
|
671
704
|
width: res.originalImage.naturalWidth,
|
|
672
705
|
height: res.originalImage.naturalHeight
|
|
673
706
|
}
|
|
674
707
|
},
|
|
675
708
|
exifTransform
|
|
676
709
|
};
|
|
677
|
-
return this.transformLoadedImage(loadedImage, cropperSettings);
|
|
710
|
+
return this.transformLoadedImage(loadedImage, cropperSettings, forceTransform);
|
|
678
711
|
}
|
|
679
|
-
async transformLoadedImage(loadedImage, cropperSettings) {
|
|
712
|
+
async transformLoadedImage(loadedImage, cropperSettings, forceTransform = false) {
|
|
680
713
|
const canvasRotation = cropperSettings.canvasRotation + loadedImage.exifTransform.rotate;
|
|
681
|
-
const originalSize =
|
|
682
|
-
|
|
683
|
-
height: loadedImage.original.image.naturalHeight
|
|
684
|
-
};
|
|
685
|
-
if (canvasRotation === 0 && !loadedImage.exifTransform.flip && !cropperSettings.containWithinAspectRatio) {
|
|
714
|
+
const originalSize = loadedImage.original.size;
|
|
715
|
+
if (!forceTransform && canvasRotation === 0 && !loadedImage.exifTransform.flip && !cropperSettings.containWithinAspectRatio) {
|
|
686
716
|
return {
|
|
687
717
|
original: {
|
|
688
718
|
objectUrl: loadedImage.original.objectUrl,
|