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/src/index.js
CHANGED
|
@@ -9,9 +9,7 @@ import {
|
|
|
9
9
|
isImageType,
|
|
10
10
|
isPositiveNumber,
|
|
11
11
|
normalizeDecimalNumber,
|
|
12
|
-
|
|
13
|
-
resetAndGetOrientation,
|
|
14
|
-
arrayBufferToDataURL,
|
|
12
|
+
resetOrientation,
|
|
15
13
|
getExif,
|
|
16
14
|
insertExif,
|
|
17
15
|
stripExif,
|
|
@@ -69,7 +67,6 @@ export default class Compressor {
|
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
if (!ArrayBuffer) {
|
|
72
|
-
options.checkOrientation = false;
|
|
73
70
|
options.retainExif = false;
|
|
74
71
|
}
|
|
75
72
|
|
|
@@ -86,21 +83,20 @@ export default class Compressor {
|
|
|
86
83
|
reader.onload = ({ target }) => {
|
|
87
84
|
if (this.aborted) return;
|
|
88
85
|
|
|
89
|
-
let
|
|
86
|
+
let blob;
|
|
90
87
|
|
|
91
88
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
result = uint8ArrayToBlob(stripped, mimeType);
|
|
89
|
+
blob = uint8ArrayToBlob(stripExif(target.result), mimeType);
|
|
95
90
|
} catch {
|
|
96
91
|
this.fail(new Error('Failed to process the image data.'));
|
|
97
92
|
return;
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
const date = new Date();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
const result = new File([blob], file.name || '', {
|
|
97
|
+
type: mimeType,
|
|
98
|
+
lastModified: date.getTime(),
|
|
99
|
+
});
|
|
104
100
|
|
|
105
101
|
this.result = result;
|
|
106
102
|
|
|
@@ -136,10 +132,9 @@ export default class Compressor {
|
|
|
136
132
|
}
|
|
137
133
|
|
|
138
134
|
const isJPEGImage = mimeType === 'image/jpeg';
|
|
139
|
-
const checkOrientation = isJPEGImage && options.checkOrientation;
|
|
140
135
|
const retainExif = isJPEGImage && options.retainExif;
|
|
141
136
|
|
|
142
|
-
if (
|
|
137
|
+
if (!retainExif) {
|
|
143
138
|
this.url = URL.createObjectURL(file);
|
|
144
139
|
this.load({
|
|
145
140
|
url: this.url,
|
|
@@ -149,41 +144,16 @@ export default class Compressor {
|
|
|
149
144
|
|
|
150
145
|
this.reader = reader;
|
|
151
146
|
reader.onload = ({ target }) => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Object.assign(data, parseOrientation(orientation));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (retainExif) {
|
|
167
|
-
this.exif = getExif(result);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (checkOrientation || retainExif) {
|
|
171
|
-
if (
|
|
172
|
-
!URL
|
|
173
|
-
|
|
174
|
-
// Generate a new URL with the default orientation value (1)
|
|
175
|
-
|| orientation > 1
|
|
176
|
-
) {
|
|
177
|
-
data.url = arrayBufferToDataURL(result, mimeType);
|
|
178
|
-
} else {
|
|
179
|
-
this.url = URL.createObjectURL(file);
|
|
180
|
-
data.url = this.url;
|
|
181
|
-
}
|
|
182
|
-
} else {
|
|
183
|
-
data.url = result;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this.load(data);
|
|
147
|
+
if (this.aborted) return;
|
|
148
|
+
|
|
149
|
+
// Normalize EXIF orientation to 1 before extracting, since the browser
|
|
150
|
+
// handles rotation natively via `image-orientation: from-image`
|
|
151
|
+
resetOrientation(target.result);
|
|
152
|
+
this.exif = getExif(target.result);
|
|
153
|
+
this.url = URL.createObjectURL(file);
|
|
154
|
+
this.load({
|
|
155
|
+
url: this.url,
|
|
156
|
+
});
|
|
187
157
|
};
|
|
188
158
|
reader.onabort = () => {
|
|
189
159
|
this.fail(new Error('Aborted to read the image with FileReader.'));
|
|
@@ -194,12 +164,7 @@ export default class Compressor {
|
|
|
194
164
|
reader.onloadend = () => {
|
|
195
165
|
this.reader = null;
|
|
196
166
|
};
|
|
197
|
-
|
|
198
|
-
if (checkOrientation || retainExif) {
|
|
199
|
-
reader.readAsArrayBuffer(file);
|
|
200
|
-
} else {
|
|
201
|
-
reader.readAsDataURL(file);
|
|
202
|
-
}
|
|
167
|
+
reader.readAsArrayBuffer(file);
|
|
203
168
|
}
|
|
204
169
|
}
|
|
205
170
|
|
|
@@ -231,17 +196,10 @@ export default class Compressor {
|
|
|
231
196
|
image.src = data.url;
|
|
232
197
|
}
|
|
233
198
|
|
|
234
|
-
draw({
|
|
235
|
-
naturalWidth,
|
|
236
|
-
naturalHeight,
|
|
237
|
-
rotate = 0,
|
|
238
|
-
scaleX = 1,
|
|
239
|
-
scaleY = 1,
|
|
240
|
-
}) {
|
|
199
|
+
draw({ naturalWidth, naturalHeight }) {
|
|
241
200
|
const { file, image, options } = this;
|
|
242
201
|
const canvas = document.createElement('canvas');
|
|
243
202
|
const context = canvas.getContext('2d');
|
|
244
|
-
const is90DegreesRotated = Math.abs(rotate) % 180 === 90;
|
|
245
203
|
const resizable = (options.resize === 'contain' || options.resize === 'cover') && isPositiveNumber(options.width) && isPositiveNumber(options.height);
|
|
246
204
|
let maxWidth = Math.max(options.maxWidth, 0) || Infinity;
|
|
247
205
|
let maxHeight = Math.max(options.maxHeight, 0) || Infinity;
|
|
@@ -250,12 +208,6 @@ export default class Compressor {
|
|
|
250
208
|
let aspectRatio = naturalWidth / naturalHeight;
|
|
251
209
|
let { width, height } = options;
|
|
252
210
|
|
|
253
|
-
if (is90DegreesRotated) {
|
|
254
|
-
[maxWidth, maxHeight] = [maxHeight, maxWidth];
|
|
255
|
-
[minWidth, minHeight] = [minHeight, minWidth];
|
|
256
|
-
[width, height] = [height, width];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
211
|
if (resizable) {
|
|
260
212
|
aspectRatio = width / height;
|
|
261
213
|
}
|
|
@@ -288,10 +240,6 @@ export default class Compressor {
|
|
|
288
240
|
width = Math.floor(normalizeDecimalNumber(Math.min(Math.max(width, minWidth), maxWidth)));
|
|
289
241
|
height = Math.floor(normalizeDecimalNumber(Math.min(Math.max(height, minHeight), maxHeight)));
|
|
290
242
|
|
|
291
|
-
const destX = -width / 2;
|
|
292
|
-
const destY = -height / 2;
|
|
293
|
-
const destWidth = width;
|
|
294
|
-
const destHeight = height;
|
|
295
243
|
const params = [];
|
|
296
244
|
|
|
297
245
|
if (resizable) {
|
|
@@ -309,11 +257,7 @@ export default class Compressor {
|
|
|
309
257
|
params.push(srcX, srcY, srcWidth, srcHeight);
|
|
310
258
|
}
|
|
311
259
|
|
|
312
|
-
params.push(
|
|
313
|
-
|
|
314
|
-
if (is90DegreesRotated) {
|
|
315
|
-
[width, height] = [height, width];
|
|
316
|
-
}
|
|
260
|
+
params.push(0, 0, width, height);
|
|
317
261
|
|
|
318
262
|
canvas.width = width;
|
|
319
263
|
canvas.height = height;
|
|
@@ -349,12 +293,7 @@ export default class Compressor {
|
|
|
349
293
|
return;
|
|
350
294
|
}
|
|
351
295
|
|
|
352
|
-
context.save();
|
|
353
|
-
context.translate(width / 2, height / 2);
|
|
354
|
-
context.rotate((rotate * Math.PI) / 180);
|
|
355
|
-
context.scale(scaleX, scaleY);
|
|
356
296
|
context.drawImage(image, ...params);
|
|
357
|
-
context.restore();
|
|
358
297
|
|
|
359
298
|
if (options.drew) {
|
|
360
299
|
options.drew.call(this, context, canvas);
|
|
@@ -374,13 +313,47 @@ export default class Compressor {
|
|
|
374
313
|
|
|
375
314
|
if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
|
|
376
315
|
const next = (arrayBuffer) => {
|
|
316
|
+
if (this.aborted) return;
|
|
377
317
|
const withExif = insertExif(arrayBuffer, this.exif);
|
|
378
318
|
done(uint8ArrayToBlob(withExif, options.mimeType));
|
|
379
319
|
};
|
|
380
320
|
|
|
381
321
|
if (blob.arrayBuffer) {
|
|
382
322
|
blob.arrayBuffer().then(next).catch(() => {
|
|
383
|
-
|
|
323
|
+
if (this.aborted) return;
|
|
324
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
325
|
+
});
|
|
326
|
+
} else {
|
|
327
|
+
const reader = new FileReader();
|
|
328
|
+
|
|
329
|
+
this.reader = reader;
|
|
330
|
+
reader.onload = ({ target }) => {
|
|
331
|
+
next(target.result);
|
|
332
|
+
};
|
|
333
|
+
reader.onabort = () => {
|
|
334
|
+
this.fail(new Error('Aborted to read the compressed image with FileReader.'));
|
|
335
|
+
};
|
|
336
|
+
reader.onerror = () => {
|
|
337
|
+
this.fail(new Error('Failed to read the compressed image with FileReader.'));
|
|
338
|
+
};
|
|
339
|
+
reader.onloadend = () => {
|
|
340
|
+
this.reader = null;
|
|
341
|
+
};
|
|
342
|
+
reader.readAsArrayBuffer(blob);
|
|
343
|
+
}
|
|
344
|
+
} else if (blob && isJPEGImage && !options.retainExif) {
|
|
345
|
+
// Strip any EXIF that may be present in the canvas output
|
|
346
|
+
// (most browsers strip it automatically, but WebKit preserves the
|
|
347
|
+
// source EXIF—this ensures consistent, privacy-safe output)
|
|
348
|
+
const next = (arrayBuffer) => {
|
|
349
|
+
if (this.aborted) return;
|
|
350
|
+
done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (blob.arrayBuffer) {
|
|
354
|
+
blob.arrayBuffer().then(next).catch(() => {
|
|
355
|
+
if (this.aborted) return;
|
|
356
|
+
this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
|
|
384
357
|
});
|
|
385
358
|
} else {
|
|
386
359
|
const reader = new FileReader();
|
|
@@ -418,6 +391,8 @@ export default class Compressor {
|
|
|
418
391
|
|
|
419
392
|
this.revokeUrl();
|
|
420
393
|
|
|
394
|
+
let strictFallback = false;
|
|
395
|
+
|
|
421
396
|
if (result) {
|
|
422
397
|
// Returns original file if the result is greater than it and without size-related options
|
|
423
398
|
if (
|
|
@@ -435,19 +410,23 @@ export default class Compressor {
|
|
|
435
410
|
)
|
|
436
411
|
) {
|
|
437
412
|
result = file;
|
|
413
|
+
strictFallback = true;
|
|
438
414
|
} else {
|
|
439
415
|
const date = new Date();
|
|
440
|
-
|
|
441
|
-
result.lastModified = date.getTime();
|
|
442
|
-
result.name = file.name;
|
|
416
|
+
let name = file.name || '';
|
|
443
417
|
|
|
444
418
|
// Convert the extension to match its type
|
|
445
|
-
if (
|
|
446
|
-
|
|
419
|
+
if (name && result.type !== file.type) {
|
|
420
|
+
name = name.replace(
|
|
447
421
|
REGEXP_EXTENSION,
|
|
448
422
|
imageTypeToExtension(result.type),
|
|
449
423
|
);
|
|
450
424
|
}
|
|
425
|
+
|
|
426
|
+
result = new File([result], name, {
|
|
427
|
+
type: result.type,
|
|
428
|
+
lastModified: date.getTime(),
|
|
429
|
+
});
|
|
451
430
|
}
|
|
452
431
|
} else {
|
|
453
432
|
// Returns original file if the result is null in some cases
|
|
@@ -455,6 +434,81 @@ export default class Compressor {
|
|
|
455
434
|
result = file;
|
|
456
435
|
}
|
|
457
436
|
|
|
437
|
+
// When strict returns the original file, it may still contain EXIF—strip it
|
|
438
|
+
// asynchronously so the output is consistently EXIF-free across all browsers
|
|
439
|
+
if (strictFallback && file.type === 'image/jpeg') {
|
|
440
|
+
if (file.arrayBuffer) {
|
|
441
|
+
file.arrayBuffer().then((arrayBuffer) => {
|
|
442
|
+
if (this.aborted) return;
|
|
443
|
+
|
|
444
|
+
const strippedBlob = uint8ArrayToBlob(stripExif(arrayBuffer), file.type);
|
|
445
|
+
const stripped = new File([strippedBlob], file.name || '', {
|
|
446
|
+
type: file.type,
|
|
447
|
+
lastModified: file.lastModified || Date.now(),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
this.result = stripped;
|
|
451
|
+
|
|
452
|
+
if (options.success) {
|
|
453
|
+
options.success.call(this, stripped);
|
|
454
|
+
}
|
|
455
|
+
}).catch((err) => {
|
|
456
|
+
if (this.aborted) return;
|
|
457
|
+
|
|
458
|
+
console.warn(
|
|
459
|
+
`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`,
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
this.result = file;
|
|
463
|
+
|
|
464
|
+
if (options.success) {
|
|
465
|
+
options.success.call(this, file);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
} else {
|
|
469
|
+
const reader = new FileReader();
|
|
470
|
+
|
|
471
|
+
this.reader = reader;
|
|
472
|
+
reader.onload = ({ target }) => {
|
|
473
|
+
if (this.aborted) return;
|
|
474
|
+
|
|
475
|
+
const strippedBlob = uint8ArrayToBlob(stripExif(target.result), file.type);
|
|
476
|
+
const stripped = new File([strippedBlob], file.name || '', {
|
|
477
|
+
type: file.type,
|
|
478
|
+
lastModified: file.lastModified || Date.now(),
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
this.result = stripped;
|
|
482
|
+
|
|
483
|
+
if (options.success) {
|
|
484
|
+
options.success.call(this, stripped);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
reader.onabort = () => {
|
|
488
|
+
this.fail(new Error('Aborted to read the original file with FileReader.'));
|
|
489
|
+
};
|
|
490
|
+
reader.onerror = () => {
|
|
491
|
+
if (this.aborted) return;
|
|
492
|
+
|
|
493
|
+
console.warn(
|
|
494
|
+
`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
this.result = file;
|
|
498
|
+
|
|
499
|
+
if (options.success) {
|
|
500
|
+
options.success.call(this, file);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
reader.onloadend = () => {
|
|
504
|
+
this.reader = null;
|
|
505
|
+
};
|
|
506
|
+
reader.readAsArrayBuffer(file);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
458
512
|
this.result = result;
|
|
459
513
|
|
|
460
514
|
if (options.success) {
|
package/src/utilities.js
CHANGED
|
@@ -90,7 +90,7 @@ export function arrayBufferToDataURL(arrayBuffer, mimeType) {
|
|
|
90
90
|
* @param {ArrayBuffer} arrayBuffer - The array buffer to read.
|
|
91
91
|
* @returns {number} The read orientation value.
|
|
92
92
|
*/
|
|
93
|
-
export function
|
|
93
|
+
export function resetOrientation(arrayBuffer) {
|
|
94
94
|
const dataView = new DataView(arrayBuffer);
|
|
95
95
|
let orientation;
|
|
96
96
|
|
|
@@ -164,63 +164,6 @@ export function resetAndGetOrientation(arrayBuffer) {
|
|
|
164
164
|
return orientation;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
/**
|
|
168
|
-
* Parse Exif Orientation value.
|
|
169
|
-
* @param {number} orientation - The orientation to parse.
|
|
170
|
-
* @returns {Object} The parsed result.
|
|
171
|
-
*/
|
|
172
|
-
export function parseOrientation(orientation) {
|
|
173
|
-
let rotate = 0;
|
|
174
|
-
let scaleX = 1;
|
|
175
|
-
let scaleY = 1;
|
|
176
|
-
|
|
177
|
-
switch (orientation) {
|
|
178
|
-
// Flip horizontal
|
|
179
|
-
case 2:
|
|
180
|
-
scaleX = -1;
|
|
181
|
-
break;
|
|
182
|
-
|
|
183
|
-
// Rotate left 180°
|
|
184
|
-
case 3:
|
|
185
|
-
rotate = -180;
|
|
186
|
-
break;
|
|
187
|
-
|
|
188
|
-
// Flip vertical
|
|
189
|
-
case 4:
|
|
190
|
-
scaleY = -1;
|
|
191
|
-
break;
|
|
192
|
-
|
|
193
|
-
// Flip vertical and rotate right 90°
|
|
194
|
-
case 5:
|
|
195
|
-
rotate = 90;
|
|
196
|
-
scaleY = -1;
|
|
197
|
-
break;
|
|
198
|
-
|
|
199
|
-
// Rotate right 90°
|
|
200
|
-
case 6:
|
|
201
|
-
rotate = 90;
|
|
202
|
-
break;
|
|
203
|
-
|
|
204
|
-
// Flip horizontal and rotate right 90°
|
|
205
|
-
case 7:
|
|
206
|
-
rotate = 90;
|
|
207
|
-
scaleX = -1;
|
|
208
|
-
break;
|
|
209
|
-
|
|
210
|
-
// Rotate left 90°
|
|
211
|
-
case 8:
|
|
212
|
-
rotate = -90;
|
|
213
|
-
break;
|
|
214
|
-
|
|
215
|
-
default:
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
rotate,
|
|
220
|
-
scaleX,
|
|
221
|
-
scaleY,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
167
|
|
|
225
168
|
let cachedCanvasReliable;
|
|
226
169
|
|
package/types/index.d.ts
CHANGED
/package/{LICENSE → LICENSE.md}
RENAMED
|
File without changes
|