File size: 3,102 Bytes
f500658 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | import { convertImageBytesToPng } from "./image-convert.ts";
import { formatDimensionNote, type ImageResizeOptions, resizeImage } from "./image-resize.ts";
export interface ProcessImageOptions {
/** Whether to resize images to inline provider limits. Default: true */
autoResizeImages?: boolean;
/** Optional resize overrides. Uses resizeImage defaults when omitted. */
resizeOptions?: ImageResizeOptions;
}
export type ProcessImageResult =
| {
ok: true;
data: string;
mimeType: string;
hints: string[];
}
| {
ok: false;
message: string;
};
interface NormalizedImage {
bytes: Uint8Array;
mimeType: string;
convertedFrom?: string;
}
function baseMimeType(mimeType: string): string {
return mimeType.split(";")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();
}
function normalizeSupportedImageMimeType(mimeType: string): string | null {
switch (baseMimeType(mimeType)) {
case "image/png":
return "image/png";
case "image/jpeg":
case "image/jpg":
return "image/jpeg";
case "image/gif":
return "image/gif";
case "image/webp":
return "image/webp";
default:
return null;
}
}
async function normalizeImage(bytes: Uint8Array, mimeType: string): Promise<NormalizedImage | null> {
const normalizedMimeType = normalizeSupportedImageMimeType(mimeType);
if (normalizedMimeType) {
return { bytes, mimeType: normalizedMimeType };
}
const pngBytes = await convertImageBytesToPng(bytes);
if (!pngBytes) {
return null;
}
return {
bytes: pngBytes,
mimeType: "image/png",
convertedFrom: baseMimeType(mimeType),
};
}
function conversionHint(from: string | undefined, to: string): string | undefined {
if (!from || from === to) return undefined;
return `[Image converted from ${from} to ${to}.]`;
}
export async function processImage(
bytes: Uint8Array,
mimeType: string,
options?: ProcessImageOptions,
): Promise<ProcessImageResult> {
const autoResizeImages = options?.autoResizeImages ?? true;
const normalized = await normalizeImage(bytes, mimeType);
if (!normalized) {
return {
ok: false,
message: "[Image omitted: could not be converted to a supported inline image format.]",
};
}
if (autoResizeImages) {
const resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);
if (!resized) {
return {
ok: false,
message: "[Image omitted: could not be resized below the inline image size limit.]",
};
}
const hints: string[] = [];
const convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);
if (convertedHint) hints.push(convertedHint);
const dimensionNote = formatDimensionNote(resized);
if (dimensionNote) hints.push(dimensionNote);
return {
ok: true,
data: resized.data,
mimeType: resized.mimeType,
hints,
};
}
const hints: string[] = [];
const convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);
if (convertedHint) hints.push(convertedHint);
return {
ok: true,
data: Buffer.from(normalized.bytes).toString("base64"),
mimeType: normalized.mimeType,
hints,
};
}
|