2021-10-27 17:54:16 +00:00
|
|
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
import { blobToArrayBuffer } from 'blob-util';
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
import { scaleImageToLevel } from './scaleImageToLevel';
|
2023-10-04 00:09:31 +00:00
|
|
|
|
import { dropNull } from './dropNull';
|
|
|
|
|
import type {
|
|
|
|
|
AttachmentType,
|
|
|
|
|
UploadedAttachmentType,
|
|
|
|
|
} from '../types/Attachment';
|
2022-10-24 20:46:36 +00:00
|
|
|
|
import { canBeTranscoded } from '../types/Attachment';
|
|
|
|
|
import type { LoggerType } from '../types/Logging';
|
|
|
|
|
import * as MIME from '../types/MIME';
|
2022-11-22 18:43:43 +00:00
|
|
|
|
import * as Errors from '../types/errors';
|
2023-10-04 00:09:31 +00:00
|
|
|
|
import * as Bytes from '../Bytes';
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// Upgrade steps
|
|
|
|
|
// NOTE: This step strips all EXIF metadata from JPEG images as
|
|
|
|
|
// part of re-encoding the image:
|
|
|
|
|
export async function autoOrientJPEG(
|
|
|
|
|
attachment: AttachmentType,
|
|
|
|
|
{ logger }: { logger: LoggerType },
|
|
|
|
|
{
|
|
|
|
|
sendHQImages = false,
|
|
|
|
|
isIncoming = false,
|
|
|
|
|
}: {
|
|
|
|
|
sendHQImages?: boolean;
|
|
|
|
|
isIncoming?: boolean;
|
|
|
|
|
} = {}
|
|
|
|
|
): Promise<AttachmentType> {
|
|
|
|
|
if (isIncoming && !MIME.isJPEG(attachment.contentType)) {
|
|
|
|
|
return attachment;
|
|
|
|
|
}
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
if (!canBeTranscoded(attachment)) {
|
|
|
|
|
return attachment;
|
|
|
|
|
}
|
2022-04-27 00:31:01 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// If we haven't downloaded the attachment yet, we won't have the data.
|
|
|
|
|
// All images go through handleImageAttachment before being sent and thus have
|
|
|
|
|
// already been scaled to level, oriented, stripped of exif data, and saved
|
|
|
|
|
// in high quality format. If we want to send the image in HQ we can return
|
|
|
|
|
// the attachment as-is. Otherwise we'll have to further scale it down.
|
2023-10-30 16:24:28 +00:00
|
|
|
|
const { data, path, size } = attachment;
|
|
|
|
|
|
|
|
|
|
if (sendHQImages) {
|
2022-10-24 20:46:36 +00:00
|
|
|
|
return attachment;
|
|
|
|
|
}
|
2023-10-30 16:24:28 +00:00
|
|
|
|
let scaleTarget: string | Blob;
|
|
|
|
|
if (path) {
|
|
|
|
|
scaleTarget = window.Signal.Migrations.getAbsoluteAttachmentPath(path);
|
|
|
|
|
} else {
|
|
|
|
|
if (!data) {
|
|
|
|
|
return attachment;
|
|
|
|
|
}
|
|
|
|
|
scaleTarget = new Blob([data], {
|
|
|
|
|
type: attachment.contentType,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
try {
|
|
|
|
|
const { blob: xcodedDataBlob } = await scaleImageToLevel(
|
2023-10-30 16:24:28 +00:00
|
|
|
|
scaleTarget,
|
2022-10-24 20:46:36 +00:00
|
|
|
|
attachment.contentType,
|
2023-10-30 16:24:28 +00:00
|
|
|
|
size,
|
2022-10-24 20:46:36 +00:00
|
|
|
|
isIncoming
|
|
|
|
|
);
|
|
|
|
|
const xcodedDataArrayBuffer = await blobToArrayBuffer(xcodedDataBlob);
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// IMPORTANT: We overwrite the existing `data` `Uint8Array` losing the original
|
|
|
|
|
// image data. Ideally, we’d preserve the original image data for users who want to
|
|
|
|
|
// retain it but due to reports of data loss, we don’t want to overburden IndexedDB
|
|
|
|
|
// by potentially doubling stored image data.
|
|
|
|
|
// See: https://github.com/signalapp/Signal-Desktop/issues/1589
|
|
|
|
|
const xcodedAttachment = {
|
2023-03-27 23:48:57 +00:00
|
|
|
|
...attachment,
|
2022-10-24 20:46:36 +00:00
|
|
|
|
data: new Uint8Array(xcodedDataArrayBuffer),
|
|
|
|
|
size: xcodedDataArrayBuffer.byteLength,
|
|
|
|
|
};
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
return xcodedAttachment;
|
|
|
|
|
} catch (error: unknown) {
|
2022-11-22 18:43:43 +00:00
|
|
|
|
const errorString = Errors.toLogFormat(error);
|
2022-10-24 20:46:36 +00:00
|
|
|
|
logger.error(
|
|
|
|
|
'autoOrientJPEG: Failed to rotate/scale attachment',
|
|
|
|
|
errorString
|
|
|
|
|
);
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
return attachment;
|
2021-10-27 17:54:16 +00:00
|
|
|
|
}
|
2022-10-24 20:46:36 +00:00
|
|
|
|
}
|
2023-10-04 00:09:31 +00:00
|
|
|
|
|
|
|
|
|
export type CdnFieldsType = Pick<
|
|
|
|
|
AttachmentType,
|
|
|
|
|
'cdnId' | 'cdnKey' | 'cdnNumber' | 'key' | 'digest'
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
|
|
export function copyCdnFields(
|
|
|
|
|
uploaded?: UploadedAttachmentType
|
|
|
|
|
): CdnFieldsType {
|
|
|
|
|
if (!uploaded) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
cdnId: dropNull(uploaded.cdnId)?.toString(),
|
|
|
|
|
cdnKey: uploaded.cdnKey,
|
|
|
|
|
cdnNumber: dropNull(uploaded.cdnNumber),
|
|
|
|
|
key: Bytes.toBase64(uploaded.key),
|
|
|
|
|
digest: Bytes.toBase64(uploaded.digest),
|
|
|
|
|
};
|
|
|
|
|
}
|