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.
@@ -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 { merge, fromEvent, takeUntil, first } from 'rxjs';
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
- return file.arrayBuffer()
616
- .then(arrayBuffer => this.checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, file.type, cropperSettings));
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
- return fetch(url)
629
- .then(res => res.arrayBuffer())
630
- .then(buffer => this.loadImageFromArrayBuffer(buffer, cropperSettings));
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\:([^\;]+)\;base64,/gmi, '');
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
- return new Promise((resolve, reject) => {
648
- const blob = new Blob([arrayBuffer]);
649
- const objectUrl = URL.createObjectURL(blob);
650
- const originalImage = new Image();
651
- originalImage.onload = () => resolve({
652
- originalImage,
653
- originalObjectUrl: objectUrl,
654
- originalArrayBuffer: arrayBuffer
655
- });
656
- originalImage.onerror = reject;
657
- originalImage.src = objectUrl;
658
- }).then((res) => this.transformImageFromArrayBuffer(res, cropperSettings));
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 = await getTransformationsFromExifData(autoRotate ? -1 : res.originalArrayBuffer);
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
- width: loadedImage.original.image.naturalWidth,
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,