diff --git a/docs/index.html b/docs/index.html
index b2d331d..c843fab 100755
--- a/docs/index.html
+++ b/docs/index.html
@@ -153,6 +153,15 @@
+
diff --git a/docs/js/main.js b/docs/js/main.js
index 3fb2821..8bce7c7 100755
--- a/docs/js/main.js
+++ b/docs/js/main.js
@@ -24,6 +24,7 @@ window.addEventListener('DOMContentLoaded', function () {
height: undefined,
resize: 'none',
quality: 0.8,
+ retainExif: false,
mimeType: '',
convertTypes: 'image/png',
convertSize: 5000000,
diff --git a/lint-staged.config.js b/lint-staged.config.js
index 9812d43..6d8524d 100644
--- a/lint-staged.config.js
+++ b/lint-staged.config.js
@@ -1,4 +1,4 @@
module.exports = {
- '{src,test}/**/*.js|*.conf*.js': 'vue-cli-service lint',
+ '{src,test}/**/*.js|*.conf*.js': 'eslint --fix',
'{src,docs}/**/*.{css,scss,html}': 'stylelint --fix',
};
diff --git a/src/defaults.js b/src/defaults.js
index a49fac2..e560cdf 100755
--- a/src/defaults.js
+++ b/src/defaults.js
@@ -67,6 +67,12 @@ export default {
*/
quality: 0.8,
+ /**
+ * If set `true`, the compressed image will retain exif.
+ * @type {boolean}
+ */
+ retainExif: false,
+
/**
* The mime type of the output image.
* By default, the original mime type of the source image file will be used.
diff --git a/src/index.js b/src/index.js
index 176da6b..f18c180 100755
--- a/src/index.js
+++ b/src/index.js
@@ -13,6 +13,11 @@ import {
normalizeDecimalNumber,
parseOrientation,
resetAndGetOrientation,
+ getEXIF,
+ insertEXIF,
+ base64ToArrayBuffer,
+ blobToBase64,
+ dataURItoBlob,
} from './utilities';
const { ArrayBuffer, FileReader } = WINDOW;
@@ -37,6 +42,7 @@ export default class Compressor {
...DEFAULTS,
...options,
};
+ this.exif = [];
this.aborted = false;
this.result = null;
this.init();
@@ -79,6 +85,9 @@ export default class Compressor {
const { result } = target;
const data = {};
+ if (options.retainExif) {
+ this.exif = getEXIF(result);
+ }
if (checkOrientation) {
// Reset the orientation value to its default value 1
// as some iOS browsers will render image with its orientation
@@ -279,14 +288,29 @@ export default class Compressor {
if (this.aborted) {
return;
}
-
+ const that = this;
const done = (result) => {
if (!this.aborted) {
- this.done({
- naturalWidth,
- naturalHeight,
- result,
- });
+ if (options.retainExif && result) {
+ blobToBase64(result, (base64) => {
+ that.done({
+ naturalWidth,
+ naturalHeight,
+ result: dataURItoBlob(
+ arrayBufferToDataURL(
+ insertEXIF(base64ToArrayBuffer(base64, options.mimeType), that.exif),
+ options.mimeType,
+ ),
+ ),
+ }, that);
+ });
+ } else {
+ this.done({
+ naturalWidth,
+ naturalHeight,
+ result,
+ });
+ }
}
};
@@ -301,8 +325,9 @@ export default class Compressor {
naturalWidth,
naturalHeight,
result,
- }) {
- const { file, image, options } = this;
+ }, _this = undefined) {
+ const that = _this || this;
+ const { file, image, options } = that;
if (URL && !options.checkOrientation) {
URL.revokeObjectURL(image.src);
diff --git a/src/utilities.js b/src/utilities.js
index 3aef019..72293d1 100755
--- a/src/utilities.js
+++ b/src/utilities.js
@@ -279,3 +279,82 @@ export function getAdjustedSizes(
height,
};
}
+
+export function getEXIF(arrayBuffer) {
+ let head = 0;
+ const segments = [];
+ let length;
+ let endPoint;
+ let seg;
+ const arr = [].slice.call(new Uint8Array(arrayBuffer), 0);
+
+ while (true) {
+ // SOS(Start of Scan)
+ if (arr[head] === 0xff && arr[head + 1] === 0xda) { break; }
+ // SOI(Start of Image)
+ if (arr[head] === 0xff && arr[head + 1] === 0xd8) {
+ head += 2;
+ } else {
+ length = arr[head + 2] * 256 + arr[head + 3];
+ endPoint = head + length + 2;
+ seg = arr.slice(head, endPoint);
+ head = endPoint;
+ segments.push(seg);
+ }
+ if (head > arr.length) {
+ break;
+ }
+ }
+ if (!segments.length) { return []; }
+ let res = [];
+ for (let x = 0; x < segments.length; x += 1) {
+ const s = segments[x];
+ if (s[0] === 0xff && s[1] === 0xe1) {
+ res = res.concat(s);
+ }
+ }
+ return res;
+}
+
+export function insertEXIF(resizedImg, exifArr) {
+ const arr = [].slice.call(new Uint8Array(resizedImg), 0);
+ if (arr[2] !== 0xff || arr[3] !== 0xe0) {
+ return resizedImg;
+ }
+ const app0Length = arr[4] * 256 + arr[5];
+
+ const newImg = [0xff, 0xd8].concat(exifArr, arr.slice(4 + app0Length));
+ return new Uint8Array(newImg);
+}
+
+export function dataURItoBlob(dataURI, mimeType = null) {
+ const mimeString = mimeType || dataURI.split(',')[0].split(':')[1].split(';')[0];
+ const byteString = atob(dataURI.split(',')[1]);
+ const arrayBuffer = new ArrayBuffer(byteString.length);
+ const intArray = new Uint8Array(arrayBuffer);
+
+ for (let i = 0; i < byteString.length; i += 1) {
+ intArray[i] = byteString.charCodeAt(i);
+ }
+ return new Blob([intArray], { type: mimeString });
+}
+
+export function base64ToArrayBuffer(base64) {
+ base64 = base64.replace(/^data:([^;]+);base64,/gim, '');
+ const binary = atob(base64);
+ const len = binary.length;
+ const buffer = new ArrayBuffer(len);
+ const view = new Uint8Array(buffer);
+ for (let i = 0; i < len; i += 1) {
+ view[i] = binary.charCodeAt(i);
+ }
+ return buffer;
+}
+
+export function blobToBase64(blob, callback) {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ callback(reader.result);
+ };
+ reader.readAsDataURL(blob);
+}
diff --git a/test/specs/Compressor.spec.js b/test/specs/Compressor.spec.js
index a7bc6fc..9a90da4 100644
--- a/test/specs/Compressor.spec.js
+++ b/test/specs/Compressor.spec.js
@@ -1,3 +1,5 @@
+import { getEXIF, blobToBase64, base64ToArrayBuffer } from '../../src/utilities';
+
describe('Compressor', () => {
it('should be a class (function)', () => {
expect(Compressor).to.be.a('function');
@@ -81,4 +83,22 @@ describe('Compressor', () => {
});
});
});
+
+ it('should same between the original image`s exif and the compressed image`s exif', (done) => {
+ window.loadImageAsBlob('/base/docs/images/picture.jpg', (image) => {
+ blobToBase64(image, (oBase64) => {
+ const exif = getEXIF(base64ToArrayBuffer(oBase64));
+
+ new Compressor(image, {
+ retainExif: true,
+ success(result) {
+ blobToBase64(result, (cBase64) => {
+ expect(getEXIF(base64ToArrayBuffer(cBase64)).sort()).to.deep.equal(exif.sort());
+ done();
+ });
+ },
+ });
+ });
+ });
+ });
});
diff --git a/types/index.d.ts b/types/index.d.ts
index c058f1a..34d036a 100755
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -10,6 +10,7 @@ declare namespace Compressor {
height?: number;
resize?: 'contain' | 'cover' | 'none';
quality?: number;
+ retainExif?: boolean;
mimeType?: string;
convertTypes?: string | string[];
convertSize?: number;