compressorjs-next 1.1.2 → 2.0.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/README.md +4 -16
- package/dist/compressor.common.js +108 -151
- package/dist/compressor.esm.js +108 -151
- package/dist/compressor.js +108 -151
- package/dist/compressor.min.js +2 -2
- package/package.json +7 -5
- package/src/defaults.js +0 -7
- package/src/index.js +124 -76
- package/src/utilities.js +1 -58
- package/types/index.d.ts +0 -1
package/dist/compressor.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Compressor.js Next
|
|
2
|
+
* Compressor.js Next v2.0.0
|
|
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()) {
|
|
@@ -663,9 +575,8 @@ class Compressor {
|
|
|
663
575
|
return;
|
|
664
576
|
}
|
|
665
577
|
const isJPEGImage = mimeType === 'image/jpeg';
|
|
666
|
-
const checkOrientation = isJPEGImage && options.checkOrientation;
|
|
667
578
|
const retainExif = isJPEGImage && options.retainExif;
|
|
668
|
-
if (
|
|
579
|
+
if (!retainExif) {
|
|
669
580
|
this.url = URL.createObjectURL(file);
|
|
670
581
|
this.load({
|
|
671
582
|
url: this.url
|
|
@@ -676,36 +587,16 @@ class Compressor {
|
|
|
676
587
|
reader.onload = ({
|
|
677
588
|
target
|
|
678
589
|
}) => {
|
|
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);
|
|
590
|
+
if (this.aborted) return;
|
|
591
|
+
|
|
592
|
+
// Normalize EXIF orientation to 1 before extracting, since the browser
|
|
593
|
+
// handles rotation natively via `image-orientation: from-image`
|
|
594
|
+
resetOrientation(target.result);
|
|
595
|
+
this.exif = getExif(target.result);
|
|
596
|
+
this.url = URL.createObjectURL(file);
|
|
597
|
+
this.load({
|
|
598
|
+
url: this.url
|
|
599
|
+
});
|
|
709
600
|
};
|
|
710
601
|
reader.onabort = () => {
|
|
711
602
|
this.fail(new Error('Aborted to read the image with FileReader.'));
|
|
@@ -716,11 +607,7 @@ class Compressor {
|
|
|
716
607
|
reader.onloadend = () => {
|
|
717
608
|
this.reader = null;
|
|
718
609
|
};
|
|
719
|
-
|
|
720
|
-
reader.readAsArrayBuffer(file);
|
|
721
|
-
} else {
|
|
722
|
-
reader.readAsDataURL(file);
|
|
723
|
-
}
|
|
610
|
+
reader.readAsArrayBuffer(file);
|
|
724
611
|
}
|
|
725
612
|
}
|
|
726
613
|
load(data) {
|
|
@@ -753,10 +640,7 @@ class Compressor {
|
|
|
753
640
|
}
|
|
754
641
|
draw({
|
|
755
642
|
naturalWidth,
|
|
756
|
-
naturalHeight
|
|
757
|
-
rotate = 0,
|
|
758
|
-
scaleX = 1,
|
|
759
|
-
scaleY = 1
|
|
643
|
+
naturalHeight
|
|
760
644
|
}) {
|
|
761
645
|
const {
|
|
762
646
|
file,
|
|
@@ -765,7 +649,6 @@ class Compressor {
|
|
|
765
649
|
} = this;
|
|
766
650
|
const canvas = document.createElement('canvas');
|
|
767
651
|
const context = canvas.getContext('2d');
|
|
768
|
-
const is90DegreesRotated = Math.abs(rotate) % 180 === 90;
|
|
769
652
|
const resizable = (options.resize === 'contain' || options.resize === 'cover') && isPositiveNumber(options.width) && isPositiveNumber(options.height);
|
|
770
653
|
let maxWidth = Math.max(options.maxWidth, 0) || Infinity;
|
|
771
654
|
let maxHeight = Math.max(options.maxHeight, 0) || Infinity;
|
|
@@ -776,11 +659,6 @@ class Compressor {
|
|
|
776
659
|
width,
|
|
777
660
|
height
|
|
778
661
|
} = options;
|
|
779
|
-
if (is90DegreesRotated) {
|
|
780
|
-
[maxWidth, maxHeight] = [maxHeight, maxWidth];
|
|
781
|
-
[minWidth, minHeight] = [minHeight, minWidth];
|
|
782
|
-
[width, height] = [height, width];
|
|
783
|
-
}
|
|
784
662
|
if (resizable) {
|
|
785
663
|
aspectRatio = width / height;
|
|
786
664
|
}
|
|
@@ -821,10 +699,6 @@ class Compressor {
|
|
|
821
699
|
}
|
|
822
700
|
width = Math.floor(normalizeDecimalNumber(Math.min(Math.max(width, minWidth), maxWidth)));
|
|
823
701
|
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
702
|
const params = [];
|
|
829
703
|
if (resizable) {
|
|
830
704
|
const {
|
|
@@ -842,10 +716,7 @@ class Compressor {
|
|
|
842
716
|
const srcY = (naturalHeight - srcHeight) / 2;
|
|
843
717
|
params.push(srcX, srcY, srcWidth, srcHeight);
|
|
844
718
|
}
|
|
845
|
-
params.push(
|
|
846
|
-
if (is90DegreesRotated) {
|
|
847
|
-
[width, height] = [height, width];
|
|
848
|
-
}
|
|
719
|
+
params.push(0, 0, width, height);
|
|
849
720
|
canvas.width = width;
|
|
850
721
|
canvas.height = height;
|
|
851
722
|
if (!isImageType(options.mimeType)) {
|
|
@@ -872,12 +743,7 @@ class Compressor {
|
|
|
872
743
|
if (this.aborted) {
|
|
873
744
|
return;
|
|
874
745
|
}
|
|
875
|
-
context.save();
|
|
876
|
-
context.translate(width / 2, height / 2);
|
|
877
|
-
context.rotate(rotate * Math.PI / 180);
|
|
878
|
-
context.scale(scaleX, scaleY);
|
|
879
746
|
context.drawImage(image, ...params);
|
|
880
|
-
context.restore();
|
|
881
747
|
if (options.drew) {
|
|
882
748
|
options.drew.call(this, context, canvas);
|
|
883
749
|
}
|
|
@@ -893,12 +759,46 @@ class Compressor {
|
|
|
893
759
|
});
|
|
894
760
|
if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
|
|
895
761
|
const next = arrayBuffer => {
|
|
762
|
+
if (this.aborted) return;
|
|
896
763
|
const withExif = insertExif(arrayBuffer, this.exif);
|
|
897
764
|
done(uint8ArrayToBlob(withExif, options.mimeType));
|
|
898
765
|
};
|
|
899
766
|
if (blob.arrayBuffer) {
|
|
900
767
|
blob.arrayBuffer().then(next).catch(() => {
|
|
901
|
-
|
|
768
|
+
if (this.aborted) return;
|
|
769
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
770
|
+
});
|
|
771
|
+
} else {
|
|
772
|
+
const reader = new FileReader();
|
|
773
|
+
this.reader = reader;
|
|
774
|
+
reader.onload = ({
|
|
775
|
+
target
|
|
776
|
+
}) => {
|
|
777
|
+
next(target.result);
|
|
778
|
+
};
|
|
779
|
+
reader.onabort = () => {
|
|
780
|
+
this.fail(new Error('Aborted to read the compressed image with FileReader.'));
|
|
781
|
+
};
|
|
782
|
+
reader.onerror = () => {
|
|
783
|
+
this.fail(new Error('Failed to read the compressed image with FileReader.'));
|
|
784
|
+
};
|
|
785
|
+
reader.onloadend = () => {
|
|
786
|
+
this.reader = null;
|
|
787
|
+
};
|
|
788
|
+
reader.readAsArrayBuffer(blob);
|
|
789
|
+
}
|
|
790
|
+
} else if (blob && isJPEGImage && !options.retainExif) {
|
|
791
|
+
// Strip any EXIF that may be present in the canvas output
|
|
792
|
+
// (most browsers strip it automatically, but WebKit preserves the
|
|
793
|
+
// source EXIF—this ensures consistent, privacy-safe output)
|
|
794
|
+
const next = arrayBuffer => {
|
|
795
|
+
if (this.aborted) return;
|
|
796
|
+
done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
|
|
797
|
+
};
|
|
798
|
+
if (blob.arrayBuffer) {
|
|
799
|
+
blob.arrayBuffer().then(next).catch(() => {
|
|
800
|
+
if (this.aborted) return;
|
|
801
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
902
802
|
});
|
|
903
803
|
} else {
|
|
904
804
|
const reader = new FileReader();
|
|
@@ -936,10 +836,12 @@ class Compressor {
|
|
|
936
836
|
options
|
|
937
837
|
} = this;
|
|
938
838
|
this.revokeUrl();
|
|
839
|
+
let strictFallback = false;
|
|
939
840
|
if (result) {
|
|
940
841
|
// Returns original file if the result is greater than it and without size-related options
|
|
941
842
|
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
843
|
result = file;
|
|
844
|
+
strictFallback = true;
|
|
943
845
|
} else {
|
|
944
846
|
const date = new Date();
|
|
945
847
|
result.lastModified = date.getTime();
|
|
@@ -955,6 +857,61 @@ class Compressor {
|
|
|
955
857
|
console.warn('Compressor.js Next: Canvas produced no output—returning the original image');
|
|
956
858
|
result = file;
|
|
957
859
|
}
|
|
860
|
+
|
|
861
|
+
// When strict returns the original file, it may still contain EXIF—strip it
|
|
862
|
+
// asynchronously so the output is consistently EXIF-free across all browsers
|
|
863
|
+
if (strictFallback && file.type === 'image/jpeg') {
|
|
864
|
+
if (file.arrayBuffer) {
|
|
865
|
+
file.arrayBuffer().then(arrayBuffer => {
|
|
866
|
+
if (this.aborted) return;
|
|
867
|
+
const stripped = uint8ArrayToBlob(stripExif(arrayBuffer), file.type);
|
|
868
|
+
stripped.name = file.name;
|
|
869
|
+
stripped.lastModified = file.lastModified;
|
|
870
|
+
this.result = stripped;
|
|
871
|
+
if (options.success) {
|
|
872
|
+
options.success.call(this, stripped);
|
|
873
|
+
}
|
|
874
|
+
}).catch(err => {
|
|
875
|
+
if (this.aborted) return;
|
|
876
|
+
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}` : ''}`);
|
|
877
|
+
this.result = file;
|
|
878
|
+
if (options.success) {
|
|
879
|
+
options.success.call(this, file);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
} else {
|
|
883
|
+
const reader = new FileReader();
|
|
884
|
+
this.reader = reader;
|
|
885
|
+
reader.onload = ({
|
|
886
|
+
target
|
|
887
|
+
}) => {
|
|
888
|
+
if (this.aborted) return;
|
|
889
|
+
const stripped = uint8ArrayToBlob(stripExif(target.result), file.type);
|
|
890
|
+
stripped.name = file.name;
|
|
891
|
+
stripped.lastModified = file.lastModified;
|
|
892
|
+
this.result = stripped;
|
|
893
|
+
if (options.success) {
|
|
894
|
+
options.success.call(this, stripped);
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
reader.onabort = () => {
|
|
898
|
+
this.fail(new Error('Aborted to read the original file with FileReader.'));
|
|
899
|
+
};
|
|
900
|
+
reader.onerror = () => {
|
|
901
|
+
if (this.aborted) return;
|
|
902
|
+
console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`);
|
|
903
|
+
this.result = file;
|
|
904
|
+
if (options.success) {
|
|
905
|
+
options.success.call(this, file);
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
reader.onloadend = () => {
|
|
909
|
+
this.reader = null;
|
|
910
|
+
};
|
|
911
|
+
reader.readAsArrayBuffer(file);
|
|
912
|
+
}
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
958
915
|
this.result = result;
|
|
959
916
|
if (options.success) {
|
|
960
917
|
options.success.call(this, result);
|