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/src/defaults.js CHANGED
@@ -1,20 +1,20 @@
1
1
  export default {
2
2
  /**
3
- * Indicates if output the original image instead of the compressed one
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 if read the image's Exif Orientation information,
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 if retain the image's Exif information after compressed.
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|Array}
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 draw the image into the canvas for compression.
98
+ * The hook function to execute before drawing the image into the canvas for compression.
99
99
  * @type {Function}
100
- * @param {CanvasRenderingContext2D} context - The 2d rendering context of the canvas.
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 drew the image into the canvas for compression.
110
+ * The hook function to execute after drawing the image into the canvas for compression.
111
111
  * @type {Function}
112
- * @param {CanvasRenderingContext2D} context - The 2d rendering context of the canvas.
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 success to compress the image.
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
- * The constructor of Compressor.
32
- * @param {File|Blob} file - The target image file for compressing.
33
- * @param {Object} [options] - The options for compressing.
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
- this.draw({
144
- ...data,
145
- naturalWidth: image.naturalWidth,
146
- naturalHeight: image.naturalHeight,
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
- const date = new Date();
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 (result.name && result.type !== file.type) {
389
- result.name = result.name.replace(
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
- * Get the no conflict compressor class.
434
- * @returns {Compressor} The compressor class.
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 noConflict() {
437
- window.Compressor = AnotherCompressor;
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 a mime type of image.
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 a mime type of image, else `false`.
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 {boolean} Returns the image extension.
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 mime type of the Data URL.
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
- export interface Options {
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?: 'contain' | 'cover' | 'none';
14
+ resize?: ResizeMode;
13
15
  quality?: number;
14
16
  mimeType?: string;
15
17
  convertTypes?: string | string[];
16
18
  convertSize?: number;
17
- beforeDraw?(context: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void;
18
- drew?(context: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void;
19
- success?(file: File | Blob): void;
20
- error?(error: Error): void;
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 noConflict(): Compressor;
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