Fix mimetype change on conversions

This commit is contained in:
Enrico Ros
2024-10-02 21:04:28 -07:00
parent 74ed8c9e5e
commit b0809734aa
3 changed files with 82 additions and 23 deletions
+40 -1
View File
@@ -1,10 +1,49 @@
/**
* Converts a canvas to a data URL and extracts the MIME type and Base64 data
* @param canvas The canvas element to convert
* @param requestedMimeType The desired MIME type for the image
* @param imageQuality A number between 0 and 1 indicating image quality for lossy formats
* @param userLogLabel A label to use in console warnings
*/
export function canvasToDataURLAndMimeType(
canvas: HTMLCanvasElement,
requestedMimeType: string,
imageQuality: number | undefined,
userLogLabel: string,
): { mimeType: string; base64Data: string } {
// Extract the actual MIME type and Base64 data efficiently
const dataUrl = canvas.toDataURL(requestedMimeType, imageQuality);
const colonIndex = dataUrl.indexOf(':');
const semicolonIndex = dataUrl.indexOf(';', colonIndex);
if (colonIndex === -1 || semicolonIndex === -1)
throw new Error('canvasToDataURLAndMimeType: Invalid data URL format.');
const actualMimeType = dataUrl.slice(colonIndex + 1, semicolonIndex);
const commaIndex = dataUrl.indexOf(',');
if (commaIndex === -1)
throw new Error('canvasToDataURLAndMimeType: Invalid data URL comma.');
const base64Data = dataUrl.slice(commaIndex + 1);
// Warn if the actual MIME type differs from the requested one
if (actualMimeType !== requestedMimeType)
console.warn(`${userLogLabel}: requested MIME type "${requestedMimeType}" was not used. Actual MIME type is "${actualMimeType}".`);
return { mimeType: actualMimeType, base64Data };
}
/**
* Creates a Blob object representing the image contained in the canvas
* @param canvas The canvas element to convert to a Blob.
* @param imageFormat Browsers are required to support image/png; many will support additional formats including image/jpeg and image/webp.
* @param imageQuality A Number between 0 and 1 indicating image quality if the requested type is image/jpeg or image/webp.
*/
export async function asyncCanvasToBlob(canvas: HTMLCanvasElement, imageFormat: 'image/png' | 'image/jpeg', imageQuality?: number): Promise<Blob | null> {
export async function asyncCanvasToBlob(
canvas: HTMLCanvasElement,
imageFormat: 'image/png' | 'image/jpeg',
imageQuality?: number
): Promise<Blob | null> {
return new Promise((resolve) => canvas.toBlob(resolve, imageFormat, imageQuality));
}
+25 -10
View File
@@ -5,6 +5,7 @@
* Also see videoUtils.ts for more image-related functions.
*/
import { canvasToDataURLAndMimeType } from './canvasUtils';
import { createBlobURLFromDataURL } from './urlUtils';
@@ -67,11 +68,18 @@ export async function convertBase64Image(base64DataUrl: string, destMimeType: st
return;
}
ctx.drawImage(image, 0, 0);
const dataUrl = canvas.toDataURL(destMimeType, destQuality);
resolve({
mimeType: destMimeType,
base64: dataUrl.split(',')[1],
});
// Convert canvas image to a DataURL string
try {
const { mimeType: actualMimeType, base64Data } = canvasToDataURLAndMimeType(canvas, destMimeType, destQuality, 'image-convert');
resolve({
mimeType: actualMimeType,
base64: base64Data,
});
} catch (error) {
console.warn(`imageUtils: failed to convert image to ${destMimeType}.`, { error });
reject(new Error(`Failed to convert image to '${destMimeType}'.`));
}
};
image.onerror = (error) => {
console.warn('Failed to load image for conversion.', error);
@@ -222,11 +230,18 @@ export async function resizeBase64ImageIfNeeded(inputMimeType: string, inputBase
canvas.width = newWidth;
canvas.height = newHeight;
ctx.drawImage(image, 0, 0, newWidth, newHeight);
const resizedDataUrl = canvas.toDataURL(destMimeType, destQuality);
resolve({
mimeType: destMimeType,
base64: resizedDataUrl.split(',')[1], // Return base64 part only
});
// Convert canvas image to a DataURL string
try {
const { mimeType: actualMimeType, base64Data } = canvasToDataURLAndMimeType(canvas, destMimeType, destQuality, 'image-resize');
resolve({
mimeType: actualMimeType,
base64: base64Data,
});
} catch (error) {
console.warn(`imageUtils: failed to resize image to '${resizeMode}' as ${destMimeType}.`, { error });
reject(new Error(`Failed to resize image to '${resizeMode}' as '${destMimeType}'.`));
}
};
image.onerror = (error) => {
+17 -12
View File
@@ -1,3 +1,5 @@
import { canvasToDataURLAndMimeType } from './canvasUtils';
// configuration
const SKIP_LOADING_IN_DEV = false;
@@ -97,26 +99,29 @@ export async function pdfToImageDataURLs(pdfBuffer: ArrayBuffer, imageMimeType:
const viewport = page.getViewport({ scale });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const context = canvas.getContext('2d')!;
canvas.height = viewport.height;
canvas.width = viewport.width;
onProgress((i * 3 + 1) / (pdf.numPages * 3));
await page.render({
canvasContext: context!,
canvasContext: context,
viewport,
}).promise;
const base64DataUrl = canvas.toDataURL(imageMimeType, imageQuality);
const base64Data = base64DataUrl.slice(`data:${imageMimeType};base64,`.length);
images.push({
mimeType: imageMimeType,
base64Data,
scale,
width: viewport.width,
height: viewport.height,
});
// Convert canvas image to a DataURL string
try {
const { mimeType: actualMimeType, base64Data } = canvasToDataURLAndMimeType(canvas, imageMimeType, imageQuality, 'pdf-to-image');
images.push({
mimeType: actualMimeType,
base64Data,
scale,
width: viewport.width,
height: viewport.height,
});
} catch (error) {
console.warn(`pdfToImageDataURLs: failed to convert image to ${imageMimeType}.`, { error });
}
onProgress((i * 3 + 2) / (pdf.numPages * 3));
}