compressorjs 1.2.1 → 1.4.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/LICENSE +21 -21
- package/README.md +76 -39
- package/dist/compressor.common.js +172 -111
- package/dist/compressor.esm.js +172 -111
- package/dist/compressor.js +172 -111
- package/dist/compressor.min.js +3 -3
- package/package.json +50 -45
- package/src/defaults.js +9 -9
- package/src/index.js +54 -23
- package/src/utilities.js +51 -7
- package/types/index.d.ts +10 -7
package/src/defaults.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
/**
|
|
3
|
-
* Indicates
|
|
3
|
+
* Indicates whether to output the original image instead of the compressed one
|
|
4
4
|
* when the size of the compressed image is greater than the original one's
|
|
5
5
|
* @type {boolean}
|
|
6
6
|
*/
|
|
7
7
|
strict: true,
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Indicates
|
|
10
|
+
* Indicates whether to read the image's Exif Orientation information,
|
|
11
11
|
* and then rotate or flip the image automatically.
|
|
12
12
|
* @type {boolean}
|
|
13
13
|
*/
|
|
14
14
|
checkOrientation: true,
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Indicates
|
|
17
|
+
* Indicates whether to retain the image's Exif information after compression.
|
|
18
18
|
* @type {boolean}
|
|
19
19
|
*/
|
|
20
20
|
retainExif: false,
|
|
@@ -83,7 +83,7 @@ export default {
|
|
|
83
83
|
/**
|
|
84
84
|
* Files whose file type is included in this list,
|
|
85
85
|
* and whose file size exceeds the `convertSize` value will be converted to JPEGs.
|
|
86
|
-
* @type {string
|
|
86
|
+
* @type {string|string[]}
|
|
87
87
|
*/
|
|
88
88
|
convertTypes: ['image/png'],
|
|
89
89
|
|
|
@@ -95,9 +95,9 @@ export default {
|
|
|
95
95
|
convertSize: 5000000,
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* The hook function to execute before
|
|
98
|
+
* The hook function to execute before drawing the image into the canvas for compression.
|
|
99
99
|
* @type {Function}
|
|
100
|
-
* @param {CanvasRenderingContext2D} context - The
|
|
100
|
+
* @param {CanvasRenderingContext2D} context - The 2D rendering context of the canvas.
|
|
101
101
|
* @param {HTMLCanvasElement} canvas - The canvas for compression.
|
|
102
102
|
* @example
|
|
103
103
|
* function (context, canvas) {
|
|
@@ -107,9 +107,9 @@ export default {
|
|
|
107
107
|
beforeDraw: null,
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
* The hook function to execute after
|
|
110
|
+
* The hook function to execute after drawing the image into the canvas for compression.
|
|
111
111
|
* @type {Function}
|
|
112
|
-
* @param {CanvasRenderingContext2D} context - The
|
|
112
|
+
* @param {CanvasRenderingContext2D} context - The 2D rendering context of the canvas.
|
|
113
113
|
* @param {HTMLCanvasElement} canvas - The canvas for compression.
|
|
114
114
|
* @example
|
|
115
115
|
* function (context, canvas) {
|
|
@@ -119,7 +119,7 @@ export default {
|
|
|
119
119
|
drew: null,
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* The hook function to execute when
|
|
122
|
+
* The hook function to execute when the image is compressed successfully.
|
|
123
123
|
* @type {Function}
|
|
124
124
|
* @param {File} file - The compressed image File object.
|
|
125
125
|
* @example
|
package/src/index.js
CHANGED
|
@@ -7,14 +7,15 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
arrayBufferToDataURL,
|
|
9
9
|
getAdjustedSizes,
|
|
10
|
+
getExif,
|
|
10
11
|
imageTypeToExtension,
|
|
12
|
+
insertExif,
|
|
13
|
+
isCanvasAvailable,
|
|
11
14
|
isImageType,
|
|
12
15
|
isPositiveNumber,
|
|
13
16
|
normalizeDecimalNumber,
|
|
14
17
|
parseOrientation,
|
|
15
18
|
resetAndGetOrientation,
|
|
16
|
-
getExif,
|
|
17
|
-
insertExif,
|
|
18
19
|
} from './utilities';
|
|
19
20
|
|
|
20
21
|
const { ArrayBuffer, FileReader } = WINDOW;
|
|
@@ -28,11 +29,11 @@ const AnotherCompressor = WINDOW.Compressor;
|
|
|
28
29
|
*/
|
|
29
30
|
export default class Compressor {
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
-
* @param {File|Blob} file - The target image file
|
|
33
|
-
* @param {Object} [options] - The options
|
|
32
|
+
* Create a new Compressor.
|
|
33
|
+
* @param {File|Blob} file - The target image file to compress.
|
|
34
|
+
* @param {Object} [options={}] - The configuration options.
|
|
34
35
|
*/
|
|
35
|
-
constructor(file, options) {
|
|
36
|
+
constructor(file, options = {}) {
|
|
36
37
|
this.file = file;
|
|
37
38
|
this.exif = [];
|
|
38
39
|
this.image = new Image();
|
|
@@ -140,11 +141,19 @@ export default class Compressor {
|
|
|
140
141
|
const { file, image } = this;
|
|
141
142
|
|
|
142
143
|
image.onload = () => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
if (isCanvasAvailable()) {
|
|
145
|
+
this.draw({
|
|
146
|
+
...data,
|
|
147
|
+
naturalWidth: image.naturalWidth,
|
|
148
|
+
naturalHeight: image.naturalHeight,
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
this.done({
|
|
152
|
+
naturalWidth: image.naturalWidth,
|
|
153
|
+
naturalHeight: image.naturalHeight,
|
|
154
|
+
result: null,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
148
157
|
};
|
|
149
158
|
image.onabort = () => {
|
|
150
159
|
this.fail(new Error('Aborted to load the image.'));
|
|
@@ -378,19 +387,31 @@ export default class Compressor {
|
|
|
378
387
|
) {
|
|
379
388
|
result = file;
|
|
380
389
|
} else {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
result.lastModified = date.getTime();
|
|
384
|
-
result.lastModifiedDate = date;
|
|
385
|
-
result.name = file.name;
|
|
390
|
+
let fileName = file.name;
|
|
386
391
|
|
|
387
392
|
// Convert the extension to match its type
|
|
388
|
-
if (
|
|
389
|
-
|
|
393
|
+
if (fileName && result.type !== file.type) {
|
|
394
|
+
fileName = fileName.replace(
|
|
390
395
|
REGEXP_EXTENSION,
|
|
391
396
|
imageTypeToExtension(result.type),
|
|
392
397
|
);
|
|
393
398
|
}
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
// Convert the resulting Blob object into a File object for modern browsers.
|
|
402
|
+
result = new File([result], fileName, {
|
|
403
|
+
type: result.type,
|
|
404
|
+
});
|
|
405
|
+
} catch (error) {
|
|
406
|
+
// Fallback to Blob if the File constructor is not supported.
|
|
407
|
+
const date = new Date();
|
|
408
|
+
|
|
409
|
+
result.name = fileName;
|
|
410
|
+
|
|
411
|
+
// The last modified date is not accurate, but it's better than nothing.
|
|
412
|
+
result.lastModified = date.getTime();
|
|
413
|
+
result.lastModifiedDate = date;
|
|
414
|
+
}
|
|
394
415
|
}
|
|
395
416
|
} else {
|
|
396
417
|
// Returns original file if the result is null in some cases.
|
|
@@ -430,12 +451,13 @@ export default class Compressor {
|
|
|
430
451
|
}
|
|
431
452
|
|
|
432
453
|
/**
|
|
433
|
-
*
|
|
434
|
-
* @
|
|
454
|
+
* Creates a new Compressor instance.
|
|
455
|
+
* @param {File|Blob} file - The target image file to compress.
|
|
456
|
+
* @param {Object} [options={}] - The configuration options.
|
|
457
|
+
* @returns {Compressor} A new Compressor instance.
|
|
435
458
|
*/
|
|
436
|
-
static
|
|
437
|
-
|
|
438
|
-
return Compressor;
|
|
459
|
+
static create(file, options) {
|
|
460
|
+
return new Compressor(file, options);
|
|
439
461
|
}
|
|
440
462
|
|
|
441
463
|
/**
|
|
@@ -445,4 +467,13 @@ export default class Compressor {
|
|
|
445
467
|
static setDefaults(options) {
|
|
446
468
|
Object.assign(DEFAULTS, options);
|
|
447
469
|
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Get the no conflict compressor class.
|
|
473
|
+
* @returns {Compressor} The compressor class.
|
|
474
|
+
*/
|
|
475
|
+
static noConflict() {
|
|
476
|
+
window.Compressor = AnotherCompressor;
|
|
477
|
+
return Compressor;
|
|
478
|
+
}
|
|
448
479
|
}
|
package/src/utilities.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
WINDOW,
|
|
3
|
-
} from './constants';
|
|
1
|
+
import { WINDOW } from './constants';
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Check if the given value is a positive number.
|
|
@@ -23,9 +21,9 @@ export function toArray(value) {
|
|
|
23
21
|
const REGEXP_IMAGE_TYPE = /^image\/.+$/;
|
|
24
22
|
|
|
25
23
|
/**
|
|
26
|
-
* Check if the given value is
|
|
24
|
+
* Check if the given value is an image MIME type.
|
|
27
25
|
* @param {*} value - The value to check.
|
|
28
|
-
* @returns {boolean} Returns `true` if the given is
|
|
26
|
+
* @returns {boolean} Returns `true` if the given value is an image MIME type, else `false`.
|
|
29
27
|
*/
|
|
30
28
|
export function isImageType(value) {
|
|
31
29
|
return REGEXP_IMAGE_TYPE.test(value);
|
|
@@ -34,7 +32,7 @@ export function isImageType(value) {
|
|
|
34
32
|
/**
|
|
35
33
|
* Convert image type to extension.
|
|
36
34
|
* @param {string} value - The image type to convert.
|
|
37
|
-
* @returns {
|
|
35
|
+
* @returns {string} Returns the image extension.
|
|
38
36
|
*/
|
|
39
37
|
export function imageTypeToExtension(value) {
|
|
40
38
|
let extension = isImageType(value) ? value.substr(6) : '';
|
|
@@ -68,12 +66,58 @@ export function getStringFromCharCode(dataView, start, length) {
|
|
|
68
66
|
return str;
|
|
69
67
|
}
|
|
70
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Check if `canvas.getContext('2d').getImageData` is available,
|
|
71
|
+
* Firefox randomizes the output of that function in `privacy.resistFingerprinting` mode (#137)
|
|
72
|
+
* @link https://github.com/nodeca/pica/blob/master/lib/utils.js
|
|
73
|
+
* @returns {boolean} Returns `true` if it is available, else `false`.
|
|
74
|
+
*/
|
|
75
|
+
export function isCanvasAvailable() {
|
|
76
|
+
try {
|
|
77
|
+
const canvas = document.createElement('canvas');
|
|
78
|
+
|
|
79
|
+
canvas.width = 2;
|
|
80
|
+
canvas.height = 1;
|
|
81
|
+
|
|
82
|
+
const context = canvas.getContext('2d');
|
|
83
|
+
|
|
84
|
+
if (!context) return false;
|
|
85
|
+
|
|
86
|
+
// Create 2x1 image data containing RGBA values for two pixels
|
|
87
|
+
const imageData = context.createImageData(2, 1);
|
|
88
|
+
|
|
89
|
+
// First pixel: R=12, G=23, B=34, A=255
|
|
90
|
+
imageData.data[0] = 12;
|
|
91
|
+
imageData.data[1] = 23;
|
|
92
|
+
imageData.data[2] = 34;
|
|
93
|
+
imageData.data[3] = 255;
|
|
94
|
+
|
|
95
|
+
// Second pixel: R=45, G=56, B=67, A=255
|
|
96
|
+
imageData.data[4] = 45;
|
|
97
|
+
imageData.data[5] = 56;
|
|
98
|
+
imageData.data[6] = 67;
|
|
99
|
+
imageData.data[7] = 255;
|
|
100
|
+
|
|
101
|
+
context.putImageData(imageData, 0, 0);
|
|
102
|
+
|
|
103
|
+
const readBack = context.getImageData(0, 0, 2, 1);
|
|
104
|
+
|
|
105
|
+
// Expected pixel data (matching the written values)
|
|
106
|
+
const expected = [12, 23, 34, 255, 45, 56, 67, 255];
|
|
107
|
+
|
|
108
|
+
// Compare element by element to ensure write and read consistency
|
|
109
|
+
return readBack.data.every((value, index) => value === expected[index]);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
71
115
|
const { btoa } = WINDOW;
|
|
72
116
|
|
|
73
117
|
/**
|
|
74
118
|
* Transform array buffer to Data URL.
|
|
75
119
|
* @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
|
|
76
|
-
* @param {string} mimeType - The
|
|
120
|
+
* @param {string} mimeType - The MIME type of the Data URL.
|
|
77
121
|
* @returns {string} The result Data URL.
|
|
78
122
|
*/
|
|
79
123
|
export function arrayBufferToDataURL(arrayBuffer, mimeType) {
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
declare namespace Compressor {
|
|
2
|
-
|
|
2
|
+
type ResizeMode = 'contain' | 'cover' | 'none';
|
|
3
|
+
|
|
4
|
+
interface Options {
|
|
3
5
|
strict?: boolean;
|
|
4
6
|
checkOrientation?: boolean;
|
|
5
7
|
retainExif?: boolean;
|
|
@@ -9,22 +11,23 @@ declare namespace Compressor {
|
|
|
9
11
|
minHeight?: number;
|
|
10
12
|
width?: number;
|
|
11
13
|
height?: number;
|
|
12
|
-
resize?:
|
|
14
|
+
resize?: ResizeMode;
|
|
13
15
|
quality?: number;
|
|
14
16
|
mimeType?: string;
|
|
15
17
|
convertTypes?: string | string[];
|
|
16
18
|
convertSize?: number;
|
|
17
|
-
beforeDraw
|
|
18
|
-
drew
|
|
19
|
-
success
|
|
20
|
-
error
|
|
19
|
+
beforeDraw?: ((this: Compressor, context: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => void) | null;
|
|
20
|
+
drew?: ((this: Compressor, context: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => void) | null;
|
|
21
|
+
success?: ((this: Compressor, file: File) => void) | null;
|
|
22
|
+
error?: ((this: Compressor, error: Error) => void) | null;
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
declare class Compressor {
|
|
25
27
|
constructor(file: File | Blob, options?: Compressor.Options);
|
|
26
28
|
abort(): void;
|
|
27
|
-
static
|
|
29
|
+
static create(file: File | Blob, options?: Compressor.Options): Compressor;
|
|
30
|
+
static noConflict(): typeof Compressor;
|
|
28
31
|
static setDefaults(options: Compressor.Options): void;
|
|
29
32
|
}
|
|
30
33
|
|