From 48cc2b5f114c7dc13358cc2aa436c733d1b5ff6e Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Tue, 24 Aug 2021 14:38:20 -0500 Subject: [PATCH] Preserve blob content type when stripping EXIF data --- .../util/scaleImageToLevel_test.ts | 68 +++++++++++++++++++ ts/util/scaleImageToLevel.ts | 9 ++- 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 ts/test-electron/util/scaleImageToLevel_test.ts diff --git a/ts/test-electron/util/scaleImageToLevel_test.ts b/ts/test-electron/util/scaleImageToLevel_test.ts new file mode 100644 index 00000000000..fd936dac690 --- /dev/null +++ b/ts/test-electron/util/scaleImageToLevel_test.ts @@ -0,0 +1,68 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { assert } from 'chai'; +import { IMAGE_JPEG, IMAGE_PNG } from '../../types/MIME'; +import * as log from '../../logging/log'; + +import { scaleImageToLevel } from '../../util/scaleImageToLevel'; + +describe('scaleImageToLevel', () => { + // NOTE: These tests are incomplete. + + let objectUrlsToRevoke: Array; + function createObjectUrl(blob: Blob): string { + const result = URL.createObjectURL(blob); + objectUrlsToRevoke.push(result); + return result; + } + + beforeEach(() => { + objectUrlsToRevoke = []; + }); + + afterEach(() => { + objectUrlsToRevoke.forEach(objectUrl => { + URL.revokeObjectURL(objectUrl); + }); + }); + + it("doesn't scale images that are already small enough", async () => { + const testCases = [ + { + path: '../fixtures/kitten-1-64-64.jpg', + contentType: IMAGE_JPEG, + expectedWidth: 64, + expectedHeight: 64, + }, + { + path: '../fixtures/20x200-yellow.png', + contentType: IMAGE_PNG, + expectedWidth: 20, + expectedHeight: 200, + }, + ]; + + await Promise.all( + testCases.map( + async ({ path, contentType, expectedWidth, expectedHeight }) => { + const blob = await (await fetch(path)).blob(); + const scaled = await scaleImageToLevel(blob, contentType, true); + + const { + width, + height, + } = await window.Signal.Types.VisualAttachment.getImageDimensions({ + objectUrl: createObjectUrl(scaled.blob), + logger: log, + }); + + assert.strictEqual(width, expectedWidth); + assert.strictEqual(height, expectedHeight); + assert.strictEqual(scaled.contentType, contentType); + assert.strictEqual(scaled.blob.type, contentType); + } + ) + ); + }); +}); diff --git a/ts/util/scaleImageToLevel.ts b/ts/util/scaleImageToLevel.ts index adcb2788e41..e1e98ffebac 100644 --- a/ts/util/scaleImageToLevel.ts +++ b/ts/util/scaleImageToLevel.ts @@ -102,10 +102,13 @@ async function getCanvasBlobAsJPEG( return canvasToBlob(canvas, IMAGE_JPEG, quality); } -async function stripImageFileEXIFData(file: File | Blob): Promise { +async function stripImageFileEXIFData( + file: File | Blob, + type: MIMEType +): Promise { const arrayBuffer = await file.arrayBuffer(); const xArrayBuffer = await sharp(new Uint8Array(arrayBuffer)).toBuffer(); - return new Blob([xArrayBuffer]); + return new Blob([xArrayBuffer], { type }); } export async function scaleImageToLevel( @@ -139,7 +142,7 @@ export async function scaleImageToLevel( MEDIA_QUALITY_LEVEL_DATA.get(level) || DEFAULT_LEVEL_DATA; if (fileOrBlobOrURL.size <= thresholdSize) { - const blob = await stripImageFileEXIFData(fileOrBlobOrURL); + const blob = await stripImageFileEXIFData(fileOrBlobOrURL, contentType); return { blob, contentType,