Home Reference Source

src/utils/scaleImg.js

import {decode} from 'base64-arraybuffer';
import EXIF from 'exif-js';

const REGULAR_EXPRESSION_BASE64 = /^data:([A-Za-z-+/]+);base64,(.+)$/;

function isBase64(url) {
	return !!REGULAR_EXPRESSION_BASE64.test(url);
}

/**
 * Crops an image to get a square picture
 * @param {String} imageUrl Uri of an image
 * @param {Integer} widthScalated Width of the image
 * @param {Integer} heightScalated Height of the image
 * @return {String} Base64 uri of a transformed image
 * @private
 */
function cropImage(imageUrl, widthScalated, heightScalated) {
	return new Promise((resolve) => {
		if (!window) {
			resolve(imageUrl);
		}
		let image = new Image();
		image.onload = function() {
			let canvas = document.createElement('canvas');
			canvas.width = Math.min(this.width, widthScalated);
			canvas.height = Math.min(this.height, heightScalated);
			let sx = 0, sy = 0, dx = 0, dy = 0;
			let sWidth = this.width;
			let sHeight = this.width;
			const dWidth = Math.min(this.width, widthScalated);
			const dHeight = Math.min(this.height, heightScalated);
			if (this.height > this.width) {
				sy = (this.height - this.width) / 2;
			} else if (this.height < this.width) {
				sx = (this.width - this.height) / 2;
				sWidth = sHeight = this.height;
			}
			canvas.getContext('2d').drawImage(this,
				sx, sy,
				sWidth,
				sHeight,
				dx, dy,
				dWidth, dHeight);
			const base64Url = canvas.toDataURL('image/png');
			image = null;
			canvas = null;
			resolve(base64Url);
		};
		image.src = imageUrl;
	});
}

/**
 * Reset the orientation of an image and return the image in base64
 * Reference: https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images/31273162#31273162
 * @param {String} srcBase64 Base64 uri of an image
 * @param {Integer} srcOrientation Identify the orientation of an image
 * @return {String} Base64 uri of an transformed image
 */
function resetOrientation(srcBase64, srcOrientation) {
	let img = new Image();
	return new Promise((resolve) => {
		img.onload = function() {
			let canvas = document.createElement('canvas'),
				ctx = canvas.getContext('2d');

			// set proper canvas dimensions before transform & export
			if (srcOrientation > 4 && srcOrientation < 9) {
				canvas.width = this.height;
				canvas.height = this.width;
			} else {
				canvas.width = this.width;
				canvas.height = this.height;
			}

			// transform context before drawing image
			switch (srcOrientation) {
				case 2: ctx.transform(-1, 0, 0, 1, this.width, 0); break;
				case 3: ctx.transform(-1, 0, 0, -1, this.width, this.height); break;
				case 4: ctx.transform(1, 0, 0, -1, 0, this.height); break;
				case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
				case 6: ctx.transform(0, 1, -1, 0, this.height, 0); break;
				case 7: ctx.transform(0, -1, -1, 0, this.height, this.width); break;
				case 8: ctx.transform(0, -1, 1, 0, 0, this.width); break;
				default: break;
			}

			// draw image
			ctx.drawImage(img, 0, 0, this.width, this.height);
			const base64Url = ctx.canvas.toDataURL('image/png');
			img = null;
			canvas = null;
			resolve(base64Url);
		};
		img.src = srcBase64;
	});
}

/**
 * Scales an image to the size passed by parameters and returns a URI
 * with the new image encoded in base64
 * @param {string} url Base64 URI of an image
 * @param {number} width Desired width of the image
 * @param {number} height Desired height of the image
 * @return {Promise<string>} Base64 URI of a transformed image
 * @private
 */
export function scaleImg(url, width = 128, height = 128) {
	let type, data;
	if (!window) {
		return url;
	}
	return new Promise((resolve) => {
		if (!isBase64(url)) {
			return resolve();
		}
		[, type, data] = url.match(REGULAR_EXPRESSION_BASE64);
		const exifdata = EXIF.readFromBinaryFile(decode(data));
		if (exifdata) {
			return resolve(exifdata.Orientation);
		}
		return resolve();
	}).then((orientation) => {
		if (orientation) {
			return resetOrientation(url, orientation, type);
		}
		return url;
	}).then(imageUrl => cropImage(imageUrl, width, height));
}