export type ImageScaleMode = 'contain' | 'cover' | 'fill'; export function readImageScaleMode(cfg: Record | undefined | null): ImageScaleMode { const v = String(cfg?.scaleMode ?? cfg?.ScaleMode ?? 'contain') .trim() .toLowerCase(); if (v === 'cover' || v === 'fill') return v; return 'contain'; } /** Tailwind classes for <img> inside a fixed box */ export function imageScaleModeImgClassName(mode: ImageScaleMode): string { switch (mode) { case 'cover': return 'h-full w-full object-cover'; case 'fill': return 'h-full w-full object-fill'; default: return 'max-h-full max-w-full object-contain'; } } export function computeImageDrawRect( boxW: number, boxH: number, sourceW: number, sourceH: number, scaleMode: string, ): { dx: number; dy: number; dw: number; dh: number } { const mode = String(scaleMode ?? 'contain').trim().toLowerCase(); if (sourceW <= 0 || sourceH <= 0 || mode === 'fill') { return { dx: 0, dy: 0, dw: boxW, dh: boxH }; } const ratio = mode === 'cover' ? Math.max(boxW / sourceW, boxH / sourceH) : Math.min(boxW / sourceW, boxH / sourceH); const dw = Math.max(1, Math.round(sourceW * ratio)); const dh = Math.max(1, Math.round(sourceH * ratio)); return { dx: Math.round((boxW - dw) / 2), dy: Math.round((boxH - dh) / 2), dw, dh, }; }