compressorjs-next 1.1.2 → 2.0.1
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/README.md +5 -17
- package/dist/compressor.common.js +125 -160
- package/dist/compressor.esm.js +125 -160
- package/dist/compressor.js +125 -160
- package/dist/compressor.min.js +2 -2
- package/package.json +8 -6
- package/src/defaults.js +0 -7
- package/src/index.js +142 -88
- package/src/utilities.js +1 -58
- package/types/index.d.ts +0 -1
- /package/{LICENSE → LICENSE.md} +0 -0
package/dist/compressor.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Compressor.js Next
|
|
2
|
+
* Compressor.js Next v2.0.1
|
|
3
3
|
* https://github.com/j9t/compressorjs-next
|
|
4
4
|
*
|
|
5
5
|
* Copyright 2018–2024 Chen Fengyuan
|
|
@@ -14,12 +14,6 @@ var DEFAULTS = {
|
|
|
14
14
|
* @type {boolean}
|
|
15
15
|
*/
|
|
16
16
|
strict: true,
|
|
17
|
-
/**
|
|
18
|
-
* Indicates if read the image’s Exif Orientation information,
|
|
19
|
-
* and then rotate or flip the image automatically.
|
|
20
|
-
* @type {boolean}
|
|
21
|
-
*/
|
|
22
|
-
checkOrientation: true,
|
|
23
17
|
/**
|
|
24
18
|
* Indicates if retain the image’s Exif information after compressed.
|
|
25
19
|
* @type {boolean}
|
|
@@ -185,40 +179,13 @@ function getStringFromCharCode(dataView, start, length) {
|
|
|
185
179
|
}
|
|
186
180
|
return str;
|
|
187
181
|
}
|
|
188
|
-
const {
|
|
189
|
-
btoa
|
|
190
|
-
} = WINDOW;
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Transform array buffer to Data URL.
|
|
194
|
-
* @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
|
|
195
|
-
* @param {string} mimeType - The mime type of the Data URL.
|
|
196
|
-
* @returns {string} The result Data URL.
|
|
197
|
-
*/
|
|
198
|
-
function arrayBufferToDataURL(arrayBuffer, mimeType) {
|
|
199
|
-
const uint8 = new Uint8Array(arrayBuffer);
|
|
200
|
-
const {
|
|
201
|
-
length
|
|
202
|
-
} = uint8;
|
|
203
|
-
const chunkSize = 8192;
|
|
204
|
-
let binary = '';
|
|
205
|
-
for (let i = 0; i < length; i += chunkSize) {
|
|
206
|
-
const end = Math.min(i + chunkSize, length);
|
|
207
|
-
let chunk = '';
|
|
208
|
-
for (let j = i; j < end; j += 1) {
|
|
209
|
-
chunk += fromCharCode(uint8[j]);
|
|
210
|
-
}
|
|
211
|
-
binary += chunk;
|
|
212
|
-
}
|
|
213
|
-
return `data:${mimeType};base64,${btoa(binary)}`;
|
|
214
|
-
}
|
|
215
182
|
|
|
216
183
|
/**
|
|
217
184
|
* Get orientation value from given array buffer.
|
|
218
185
|
* @param {ArrayBuffer} arrayBuffer - The array buffer to read.
|
|
219
186
|
* @returns {number} The read orientation value.
|
|
220
187
|
*/
|
|
221
|
-
function
|
|
188
|
+
function resetOrientation(arrayBuffer) {
|
|
222
189
|
const dataView = new DataView(arrayBuffer);
|
|
223
190
|
let orientation;
|
|
224
191
|
|
|
@@ -280,60 +247,6 @@ function resetAndGetOrientation(arrayBuffer) {
|
|
|
280
247
|
}
|
|
281
248
|
return orientation;
|
|
282
249
|
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Parse Exif Orientation value.
|
|
286
|
-
* @param {number} orientation - The orientation to parse.
|
|
287
|
-
* @returns {Object} The parsed result.
|
|
288
|
-
*/
|
|
289
|
-
function parseOrientation(orientation) {
|
|
290
|
-
let rotate = 0;
|
|
291
|
-
let scaleX = 1;
|
|
292
|
-
let scaleY = 1;
|
|
293
|
-
switch (orientation) {
|
|
294
|
-
// Flip horizontal
|
|
295
|
-
case 2:
|
|
296
|
-
scaleX = -1;
|
|
297
|
-
break;
|
|
298
|
-
|
|
299
|
-
// Rotate left 180°
|
|
300
|
-
case 3:
|
|
301
|
-
rotate = -180;
|
|
302
|
-
break;
|
|
303
|
-
|
|
304
|
-
// Flip vertical
|
|
305
|
-
case 4:
|
|
306
|
-
scaleY = -1;
|
|
307
|
-
break;
|
|
308
|
-
|
|
309
|
-
// Flip vertical and rotate right 90°
|
|
310
|
-
case 5:
|
|
311
|
-
rotate = 90;
|
|
312
|
-
scaleY = -1;
|
|
313
|
-
break;
|
|
314
|
-
|
|
315
|
-
// Rotate right 90°
|
|
316
|
-
case 6:
|
|
317
|
-
rotate = 90;
|
|
318
|
-
break;
|
|
319
|
-
|
|
320
|
-
// Flip horizontal and rotate right 90°
|
|
321
|
-
case 7:
|
|
322
|
-
rotate = 90;
|
|
323
|
-
scaleX = -1;
|
|
324
|
-
break;
|
|
325
|
-
|
|
326
|
-
// Rotate left 90°
|
|
327
|
-
case 8:
|
|
328
|
-
rotate = -90;
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
rotate,
|
|
333
|
-
scaleX,
|
|
334
|
-
scaleY
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
250
|
let cachedCanvasReliable;
|
|
338
251
|
|
|
339
252
|
/**
|
|
@@ -607,7 +520,6 @@ class Compressor {
|
|
|
607
520
|
return;
|
|
608
521
|
}
|
|
609
522
|
if (!ArrayBuffer) {
|
|
610
|
-
options.checkOrientation = false;
|
|
611
523
|
options.retainExif = false;
|
|
612
524
|
}
|
|
613
525
|
if (!isCanvasReliable()) {
|
|
@@ -623,17 +535,18 @@ class Compressor {
|
|
|
623
535
|
target
|
|
624
536
|
}) => {
|
|
625
537
|
if (this.aborted) return;
|
|
626
|
-
let
|
|
538
|
+
let blob;
|
|
627
539
|
try {
|
|
628
|
-
|
|
629
|
-
result = uint8ArrayToBlob(stripped, mimeType);
|
|
540
|
+
blob = uint8ArrayToBlob(stripExif(target.result), mimeType);
|
|
630
541
|
} catch {
|
|
631
542
|
this.fail(new Error('Failed to process the image data.'));
|
|
632
543
|
return;
|
|
633
544
|
}
|
|
634
545
|
const date = new Date();
|
|
635
|
-
result
|
|
636
|
-
|
|
546
|
+
const result = new File([blob], file.name || '', {
|
|
547
|
+
type: mimeType,
|
|
548
|
+
lastModified: date.getTime()
|
|
549
|
+
});
|
|
637
550
|
this.result = result;
|
|
638
551
|
if (options.success) {
|
|
639
552
|
options.success.call(this, result);
|
|
@@ -663,9 +576,8 @@ class Compressor {
|
|
|
663
576
|
return;
|
|
664
577
|
}
|
|
665
578
|
const isJPEGImage = mimeType === 'image/jpeg';
|
|
666
|
-
const checkOrientation = isJPEGImage && options.checkOrientation;
|
|
667
579
|
const retainExif = isJPEGImage && options.retainExif;
|
|
668
|
-
if (
|
|
580
|
+
if (!retainExif) {
|
|
669
581
|
this.url = URL.createObjectURL(file);
|
|
670
582
|
this.load({
|
|
671
583
|
url: this.url
|
|
@@ -676,36 +588,16 @@ class Compressor {
|
|
|
676
588
|
reader.onload = ({
|
|
677
589
|
target
|
|
678
590
|
}) => {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
Object.assign(data, parseOrientation(orientation));
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (retainExif) {
|
|
693
|
-
this.exif = getExif(result);
|
|
694
|
-
}
|
|
695
|
-
if (checkOrientation || retainExif) {
|
|
696
|
-
if (!URL
|
|
697
|
-
|
|
698
|
-
// Generate a new URL with the default orientation value (1)
|
|
699
|
-
|| orientation > 1) {
|
|
700
|
-
data.url = arrayBufferToDataURL(result, mimeType);
|
|
701
|
-
} else {
|
|
702
|
-
this.url = URL.createObjectURL(file);
|
|
703
|
-
data.url = this.url;
|
|
704
|
-
}
|
|
705
|
-
} else {
|
|
706
|
-
data.url = result;
|
|
707
|
-
}
|
|
708
|
-
this.load(data);
|
|
591
|
+
if (this.aborted) return;
|
|
592
|
+
|
|
593
|
+
// Normalize EXIF orientation to 1 before extracting, since the browser
|
|
594
|
+
// handles rotation natively via `image-orientation: from-image`
|
|
595
|
+
resetOrientation(target.result);
|
|
596
|
+
this.exif = getExif(target.result);
|
|
597
|
+
this.url = URL.createObjectURL(file);
|
|
598
|
+
this.load({
|
|
599
|
+
url: this.url
|
|
600
|
+
});
|
|
709
601
|
};
|
|
710
602
|
reader.onabort = () => {
|
|
711
603
|
this.fail(new Error('Aborted to read the image with FileReader.'));
|
|
@@ -716,11 +608,7 @@ class Compressor {
|
|
|
716
608
|
reader.onloadend = () => {
|
|
717
609
|
this.reader = null;
|
|
718
610
|
};
|
|
719
|
-
|
|
720
|
-
reader.readAsArrayBuffer(file);
|
|
721
|
-
} else {
|
|
722
|
-
reader.readAsDataURL(file);
|
|
723
|
-
}
|
|
611
|
+
reader.readAsArrayBuffer(file);
|
|
724
612
|
}
|
|
725
613
|
}
|
|
726
614
|
load(data) {
|
|
@@ -753,10 +641,7 @@ class Compressor {
|
|
|
753
641
|
}
|
|
754
642
|
draw({
|
|
755
643
|
naturalWidth,
|
|
756
|
-
naturalHeight
|
|
757
|
-
rotate = 0,
|
|
758
|
-
scaleX = 1,
|
|
759
|
-
scaleY = 1
|
|
644
|
+
naturalHeight
|
|
760
645
|
}) {
|
|
761
646
|
const {
|
|
762
647
|
file,
|
|
@@ -765,7 +650,6 @@ class Compressor {
|
|
|
765
650
|
} = this;
|
|
766
651
|
const canvas = document.createElement('canvas');
|
|
767
652
|
const context = canvas.getContext('2d');
|
|
768
|
-
const is90DegreesRotated = Math.abs(rotate) % 180 === 90;
|
|
769
653
|
const resizable = (options.resize === 'contain' || options.resize === 'cover') && isPositiveNumber(options.width) && isPositiveNumber(options.height);
|
|
770
654
|
let maxWidth = Math.max(options.maxWidth, 0) || Infinity;
|
|
771
655
|
let maxHeight = Math.max(options.maxHeight, 0) || Infinity;
|
|
@@ -776,11 +660,6 @@ class Compressor {
|
|
|
776
660
|
width,
|
|
777
661
|
height
|
|
778
662
|
} = options;
|
|
779
|
-
if (is90DegreesRotated) {
|
|
780
|
-
[maxWidth, maxHeight] = [maxHeight, maxWidth];
|
|
781
|
-
[minWidth, minHeight] = [minHeight, minWidth];
|
|
782
|
-
[width, height] = [height, width];
|
|
783
|
-
}
|
|
784
663
|
if (resizable) {
|
|
785
664
|
aspectRatio = width / height;
|
|
786
665
|
}
|
|
@@ -821,10 +700,6 @@ class Compressor {
|
|
|
821
700
|
}
|
|
822
701
|
width = Math.floor(normalizeDecimalNumber(Math.min(Math.max(width, minWidth), maxWidth)));
|
|
823
702
|
height = Math.floor(normalizeDecimalNumber(Math.min(Math.max(height, minHeight), maxHeight)));
|
|
824
|
-
const destX = -width / 2;
|
|
825
|
-
const destY = -height / 2;
|
|
826
|
-
const destWidth = width;
|
|
827
|
-
const destHeight = height;
|
|
828
703
|
const params = [];
|
|
829
704
|
if (resizable) {
|
|
830
705
|
const {
|
|
@@ -842,10 +717,7 @@ class Compressor {
|
|
|
842
717
|
const srcY = (naturalHeight - srcHeight) / 2;
|
|
843
718
|
params.push(srcX, srcY, srcWidth, srcHeight);
|
|
844
719
|
}
|
|
845
|
-
params.push(
|
|
846
|
-
if (is90DegreesRotated) {
|
|
847
|
-
[width, height] = [height, width];
|
|
848
|
-
}
|
|
720
|
+
params.push(0, 0, width, height);
|
|
849
721
|
canvas.width = width;
|
|
850
722
|
canvas.height = height;
|
|
851
723
|
if (!isImageType(options.mimeType)) {
|
|
@@ -872,12 +744,7 @@ class Compressor {
|
|
|
872
744
|
if (this.aborted) {
|
|
873
745
|
return;
|
|
874
746
|
}
|
|
875
|
-
context.save();
|
|
876
|
-
context.translate(width / 2, height / 2);
|
|
877
|
-
context.rotate(rotate * Math.PI / 180);
|
|
878
|
-
context.scale(scaleX, scaleY);
|
|
879
747
|
context.drawImage(image, ...params);
|
|
880
|
-
context.restore();
|
|
881
748
|
if (options.drew) {
|
|
882
749
|
options.drew.call(this, context, canvas);
|
|
883
750
|
}
|
|
@@ -893,12 +760,46 @@ class Compressor {
|
|
|
893
760
|
});
|
|
894
761
|
if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
|
|
895
762
|
const next = arrayBuffer => {
|
|
763
|
+
if (this.aborted) return;
|
|
896
764
|
const withExif = insertExif(arrayBuffer, this.exif);
|
|
897
765
|
done(uint8ArrayToBlob(withExif, options.mimeType));
|
|
898
766
|
};
|
|
899
767
|
if (blob.arrayBuffer) {
|
|
900
768
|
blob.arrayBuffer().then(next).catch(() => {
|
|
901
|
-
|
|
769
|
+
if (this.aborted) return;
|
|
770
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
771
|
+
});
|
|
772
|
+
} else {
|
|
773
|
+
const reader = new FileReader();
|
|
774
|
+
this.reader = reader;
|
|
775
|
+
reader.onload = ({
|
|
776
|
+
target
|
|
777
|
+
}) => {
|
|
778
|
+
next(target.result);
|
|
779
|
+
};
|
|
780
|
+
reader.onabort = () => {
|
|
781
|
+
this.fail(new Error('Aborted to read the compressed image with FileReader.'));
|
|
782
|
+
};
|
|
783
|
+
reader.onerror = () => {
|
|
784
|
+
this.fail(new Error('Failed to read the compressed image with FileReader.'));
|
|
785
|
+
};
|
|
786
|
+
reader.onloadend = () => {
|
|
787
|
+
this.reader = null;
|
|
788
|
+
};
|
|
789
|
+
reader.readAsArrayBuffer(blob);
|
|
790
|
+
}
|
|
791
|
+
} else if (blob && isJPEGImage && !options.retainExif) {
|
|
792
|
+
// Strip any EXIF that may be present in the canvas output
|
|
793
|
+
// (most browsers strip it automatically, but WebKit preserves the
|
|
794
|
+
// source EXIF—this ensures consistent, privacy-safe output)
|
|
795
|
+
const next = arrayBuffer => {
|
|
796
|
+
if (this.aborted) return;
|
|
797
|
+
done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
|
|
798
|
+
};
|
|
799
|
+
if (blob.arrayBuffer) {
|
|
800
|
+
blob.arrayBuffer().then(next).catch(() => {
|
|
801
|
+
if (this.aborted) return;
|
|
802
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
902
803
|
});
|
|
903
804
|
} else {
|
|
904
805
|
const reader = new FileReader();
|
|
@@ -936,25 +837,89 @@ class Compressor {
|
|
|
936
837
|
options
|
|
937
838
|
} = this;
|
|
938
839
|
this.revokeUrl();
|
|
840
|
+
let strictFallback = false;
|
|
939
841
|
if (result) {
|
|
940
842
|
// Returns original file if the result is greater than it and without size-related options
|
|
941
843
|
if (options.strict && !options.retainExif && result.size > file.size && options.mimeType === file.type && !(options.width > naturalWidth || options.height > naturalHeight || options.minWidth > naturalWidth || options.minHeight > naturalHeight || options.maxWidth < naturalWidth || options.maxHeight < naturalHeight)) {
|
|
942
844
|
result = file;
|
|
845
|
+
strictFallback = true;
|
|
943
846
|
} else {
|
|
944
847
|
const date = new Date();
|
|
945
|
-
|
|
946
|
-
result.name = file.name;
|
|
848
|
+
let name = file.name || '';
|
|
947
849
|
|
|
948
850
|
// Convert the extension to match its type
|
|
949
|
-
if (
|
|
950
|
-
|
|
851
|
+
if (name && result.type !== file.type) {
|
|
852
|
+
name = name.replace(REGEXP_EXTENSION, imageTypeToExtension(result.type));
|
|
951
853
|
}
|
|
854
|
+
result = new File([result], name, {
|
|
855
|
+
type: result.type,
|
|
856
|
+
lastModified: date.getTime()
|
|
857
|
+
});
|
|
952
858
|
}
|
|
953
859
|
} else {
|
|
954
860
|
// Returns original file if the result is null in some cases
|
|
955
861
|
console.warn('Compressor.js Next: Canvas produced no output—returning the original image');
|
|
956
862
|
result = file;
|
|
957
863
|
}
|
|
864
|
+
|
|
865
|
+
// When strict returns the original file, it may still contain EXIF—strip it
|
|
866
|
+
// asynchronously so the output is consistently EXIF-free across all browsers
|
|
867
|
+
if (strictFallback && file.type === 'image/jpeg') {
|
|
868
|
+
if (file.arrayBuffer) {
|
|
869
|
+
file.arrayBuffer().then(arrayBuffer => {
|
|
870
|
+
if (this.aborted) return;
|
|
871
|
+
const strippedBlob = uint8ArrayToBlob(stripExif(arrayBuffer), file.type);
|
|
872
|
+
const stripped = new File([strippedBlob], file.name || '', {
|
|
873
|
+
type: file.type,
|
|
874
|
+
lastModified: file.lastModified || Date.now()
|
|
875
|
+
});
|
|
876
|
+
this.result = stripped;
|
|
877
|
+
if (options.success) {
|
|
878
|
+
options.success.call(this, stripped);
|
|
879
|
+
}
|
|
880
|
+
}).catch(err => {
|
|
881
|
+
if (this.aborted) return;
|
|
882
|
+
console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`);
|
|
883
|
+
this.result = file;
|
|
884
|
+
if (options.success) {
|
|
885
|
+
options.success.call(this, file);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
} else {
|
|
889
|
+
const reader = new FileReader();
|
|
890
|
+
this.reader = reader;
|
|
891
|
+
reader.onload = ({
|
|
892
|
+
target
|
|
893
|
+
}) => {
|
|
894
|
+
if (this.aborted) return;
|
|
895
|
+
const strippedBlob = uint8ArrayToBlob(stripExif(target.result), file.type);
|
|
896
|
+
const stripped = new File([strippedBlob], file.name || '', {
|
|
897
|
+
type: file.type,
|
|
898
|
+
lastModified: file.lastModified || Date.now()
|
|
899
|
+
});
|
|
900
|
+
this.result = stripped;
|
|
901
|
+
if (options.success) {
|
|
902
|
+
options.success.call(this, stripped);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
reader.onabort = () => {
|
|
906
|
+
this.fail(new Error('Aborted to read the original file with FileReader.'));
|
|
907
|
+
};
|
|
908
|
+
reader.onerror = () => {
|
|
909
|
+
if (this.aborted) return;
|
|
910
|
+
console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`);
|
|
911
|
+
this.result = file;
|
|
912
|
+
if (options.success) {
|
|
913
|
+
options.success.call(this, file);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
reader.onloadend = () => {
|
|
917
|
+
this.reader = null;
|
|
918
|
+
};
|
|
919
|
+
reader.readAsArrayBuffer(file);
|
|
920
|
+
}
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
958
923
|
this.result = result;
|
|
959
924
|
if (options.success) {
|
|
960
925
|
options.success.call(this, result);
|