From b9c8dc55902177a46d36a4601999d40be0c11030 Mon Sep 17 00:00:00 2001 From: Tony Brobston Date: Mon, 27 Jul 2020 13:29:12 -0500 Subject: [PATCH] feat: add fixImageOrientation option --- src/services/canvasService.ts | 25 ++++++++++++++++------ src/services/optionService.ts | 1 + src/services/scaleService.ts | 2 +- src/types/InputOptions.ts | 1 + src/types/Options.ts | 1 + tests/services/canvasService.test.ts | 31 +++++++++++++++++++++++----- tests/services/optionService.test.ts | 4 ++++ tests/services/scaleService.test.ts | 2 +- 8 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/services/canvasService.ts b/src/services/canvasService.ts index 5ebad53..c69f5f3 100644 --- a/src/services/canvasService.ts +++ b/src/services/canvasService.ts @@ -1,8 +1,14 @@ +import {Options} from '../types/Options'; import exifService from './exifService'; -const setCanvasDimensions = - (canvas: HTMLCanvasElement, orientation: number, scaledHeight: number, scaledWidth: number): void => { - if (orientation > 4 && orientation < 9) { +const setCanvasDimensions = ( + canvas: HTMLCanvasElement, + orientation: number, + fixImageOrientation: boolean, + scaledHeight: number, + scaledWidth: number, +): void => { + if (orientation > 4 && orientation < 9 && fixImageOrientation) { canvas.width = scaledHeight; canvas.height = scaledWidth; } else { @@ -39,15 +45,22 @@ const correctExifRotation = (context: CanvasTransform, orientation: number, heig } }; -const create = async (file: File, image: HTMLImageElement, scale: number): Promise => { +const create = async ( + file: File, + image: HTMLImageElement, + scale: number, + {fixImageOrientation}: Options, +): Promise => { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); if (context) { const scaledHeight = image.height * scale; const scaledWidth = image.width * scale; const orientation = await exifService.determineOrientation(file); - setCanvasDimensions(canvas, orientation, scaledHeight, scaledWidth); - correctExifRotation(context, orientation, scaledHeight, scaledWidth); + setCanvasDimensions(canvas, orientation, fixImageOrientation, scaledHeight, scaledWidth); + if (fixImageOrientation) { + correctExifRotation(context, orientation, scaledHeight, scaledWidth); + } context.drawImage(image, 0, 0, scaledWidth, scaledHeight); return canvas; } else { diff --git a/src/services/optionService.ts b/src/services/optionService.ts index dbaf6f6..aa13db2 100644 --- a/src/services/optionService.ts +++ b/src/services/optionService.ts @@ -4,6 +4,7 @@ import {Options} from '../types/Options'; const override = (inputOptions: InputOptions): Options => { return { allowCrossOriginResourceSharing: false, + fixImageOrientation: true, quality: 0.5, returnOriginalIfCompressedFileIsLarger: false, returnOriginalOnFailure: true, diff --git a/src/services/scaleService.ts b/src/services/scaleService.ts index 8634474..8d2b4b2 100644 --- a/src/services/scaleService.ts +++ b/src/services/scaleService.ts @@ -19,7 +19,7 @@ const determineScale = ({height, width}: HTMLImageElement, {maxHeight, maxWidth, const toCanvas = async (file: File, options: Options): Promise => { const image = await imageService.create(file, options); const scale = determineScale(image, options); - return canvasService.create(file, image, scale); + return canvasService.create(file, image, scale, options); }; export default { diff --git a/src/types/InputOptions.ts b/src/types/InputOptions.ts index e4e59e0..a9e3374 100644 --- a/src/types/InputOptions.ts +++ b/src/types/InputOptions.ts @@ -1,5 +1,6 @@ export interface InputOptions { readonly allowCrossOriginResourceSharing?: boolean; + readonly fixImageOrientation?: boolean; readonly quality?: number; readonly returnOriginalOnFailure?: boolean; readonly returnOriginalIfCompressedFileIsLarger?: boolean; diff --git a/src/types/Options.ts b/src/types/Options.ts index 17540b3..7fe0c21 100644 --- a/src/types/Options.ts +++ b/src/types/Options.ts @@ -2,6 +2,7 @@ import {InputOptions} from './InputOptions'; export interface Options extends InputOptions { readonly allowCrossOriginResourceSharing: boolean; + readonly fixImageOrientation: boolean; readonly quality: number; readonly returnOriginalOnFailure: boolean; readonly returnOriginalIfCompressedFileIsLarger: boolean; diff --git a/tests/services/canvasService.test.ts b/tests/services/canvasService.test.ts index c7c379e..708e6dd 100644 --- a/tests/services/canvasService.test.ts +++ b/tests/services/canvasService.test.ts @@ -1,4 +1,5 @@ import {Chance} from 'chance'; +import {Options} from '../../src/types/Options'; import canvasService from '../../src/services/canvasService'; import exifService from '../../src/services/exifService'; @@ -19,6 +20,9 @@ describe('canvasService', () => { }); const scaledHeight = image.height * scale; const scaledWidth = image.width * scale; + const options = { + fixImageOrientation: true, + } as Options; const expectedOrientation = 1; @@ -28,7 +32,7 @@ describe('canvasService', () => { describe('create', () => { beforeAll(async () => { - actualCanvas = await canvasService.create(file, image, scale); + actualCanvas = await canvasService.create(file, image, scale, options); }); it('should determine orientation', () => { @@ -43,6 +47,10 @@ describe('canvasService', () => { }); describe('correctExifRotation', () => { + afterEach(() => { + transform.mockClear(); + }); + const transform = jest.fn(); const canvas = document.createElement('canvas'); canvas.getContext = jest.fn().mockReturnValue({ @@ -103,10 +111,9 @@ describe('canvasService', () => { width: number, }) => { it(`should correct orientation ${scenario.exifOrientation}`, async () => { - transform.mockClear(); exifService.determineOrientation = jest.fn(() => Promise.resolve(scenario.exifOrientation)); - actualCanvas = await canvasService.create(file, image, scale); + actualCanvas = await canvasService.create(file, image, scale, options); expect(actualCanvas.height).toBe(Math.floor(scenario.height)); expect(actualCanvas.width).toBe(Math.floor(scenario.width)); @@ -115,6 +122,20 @@ describe('canvasService', () => { expect(transform).toHaveBeenCalledWith(...scenario.parameters); }); }); + + it('should NOT correct orientation', async () => { + const exifOrientation = 6; + exifService.determineOrientation = jest.fn(() => Promise.resolve(exifOrientation)); + + actualCanvas = await canvasService.create(file, image, scale, { + fixImageOrientation: false, + } as Options); + + expect(actualCanvas.height).toBe(Math.floor(scaledHeight)); + expect(actualCanvas.width).toBe(Math.floor(scaledWidth)); + + expect(transform).not.toHaveBeenCalled(); + }); }); describe('cannot read context', () => { @@ -124,7 +145,7 @@ describe('canvasService', () => { document.createElement = jest.fn(() => canvas); try { - await canvasService.create(file, image, scale); + await canvasService.create(file, image, scale, options); } catch (error) { expect(error.message).toBe('Could not get CanvasRenderingContext2D from HTMLCanvasElement.'); } @@ -135,7 +156,7 @@ describe('canvasService', () => { canvas.getContext = jest.fn(() => null); document.createElement = jest.fn(() => canvas); - await expect(canvasService.create(file, image, scale)).rejects.toThrow(); + await expect(canvasService.create(file, image, scale, options)).rejects.toThrow(); }); }); }); diff --git a/tests/services/optionService.test.ts b/tests/services/optionService.test.ts index 2dd91ac..2e4e32b 100644 --- a/tests/services/optionService.test.ts +++ b/tests/services/optionService.test.ts @@ -7,6 +7,7 @@ describe('optionService', () => { { expectedOptions: { allowCrossOriginResourceSharing: false, + fixImageOrientation: true, quality: 0.5, returnOriginalIfCompressedFileIsLarger: false, returnOriginalOnFailure: true, @@ -18,6 +19,7 @@ describe('optionService', () => { { expectedOptions: { allowCrossOriginResourceSharing: true, + fixImageOrientation: false, maxHeight: 5, maxWidth: 4, quality: 0.75, @@ -27,6 +29,7 @@ describe('optionService', () => { } as Options, inputOptions: { allowCrossOriginResourceSharing: true, + fixImageOrientation: false, maxHeight: 5, maxWidth: 4, quality: 0.75, @@ -39,6 +42,7 @@ describe('optionService', () => { { expectedOptions: { allowCrossOriginResourceSharing: true, + fixImageOrientation: true, quality: 0.5, returnOriginalIfCompressedFileIsLarger: false, returnOriginalOnFailure: true, diff --git a/tests/services/scaleService.test.ts b/tests/services/scaleService.test.ts index e80d591..2fb4ae7 100644 --- a/tests/services/scaleService.test.ts +++ b/tests/services/scaleService.test.ts @@ -198,7 +198,7 @@ describe('scaleService', () => { it('should create a canvas', () => { expect(canvasService.create).toHaveBeenCalledTimes(1); expect(canvasService.create).toHaveBeenCalledWith( - file, image, scenario.scale); + file, image, scenario.scale, scenario.options); }); it('should return a scaled canvasService', () => {