/** اندازهٔ استاندارد کاور فرم (نسبت ۳:۱) */ export const COVER_WIDTH = 1200; export const COVER_HEIGHT = 400; const MAX_INPUT_BYTES = 15_000_000; /** تصاویر عریض‌تر از این نسبت بنر حساب می‌شوند و برش می‌خورند؛ بقیه لوگو هستند و کامل جا می‌گیرند */ const BANNER_MIN_RATIO = 2; function loadImage(file: File): Promise { return new Promise((resolve, reject) => { const url = URL.createObjectURL(file); const img = new Image(); img.onload = () => { URL.revokeObjectURL(url); resolve(img); }; img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('تصویر قابل خواندن نیست.')); }; img.src = url; }); } function toBlob(canvas: HTMLCanvasElement, type: string, quality?: number): Promise { return new Promise((resolve, reject) => { canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('پردازش تصویر ناموفق بود.'))), type, quality); }); } /** * تصویر را به کاور ۱۲۰۰×۴۰۰ تبدیل می‌کند: * بنرهای عریض از وسط برش می‌خورند (JPEG)، * لوگوها و تصاویر مربعی/عمودی بدون برش وسط کادر با پس‌زمینهٔ شفاف قرار می‌گیرند (PNG) تا رنگ فرم پشتشان دیده شود. */ export async function prepareFormCover(file: File): Promise { if (!file.type.startsWith('image/')) throw new Error('فقط فایل تصویری (PNG، JPG، WEBP، SVG) مجاز است.'); if (file.size > MAX_INPUT_BYTES) throw new Error('حجم تصویر بیش از ۱۵ مگابایت است.'); const img = await loadImage(file); const w = img.naturalWidth || img.width; const h = img.naturalHeight || img.height; if (!w || !h) throw new Error('ابعاد تصویر نامعتبر است.'); const canvas = document.createElement('canvas'); canvas.width = COVER_WIDTH; canvas.height = COVER_HEIGHT; const ctx = canvas.getContext('2d'); if (!ctx) throw new Error('مرورگر از پردازش تصویر پشتیبانی نمی‌کند.'); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; const ratio = w / h; if (ratio >= BANNER_MIN_RATIO) { const target = COVER_WIDTH / COVER_HEIGHT; let sw = w; let sh = h; if (ratio > target) sw = h * target; else sh = w / target; ctx.drawImage(img, (w - sw) / 2, (h - sh) / 2, sw, sh, 0, 0, COVER_WIDTH, COVER_HEIGHT); const blob = await toBlob(canvas, 'image/jpeg', 0.88); return new File([blob], 'cover.jpg', { type: 'image/jpeg' }); } const boxW = COVER_WIDTH * 0.6; const boxH = COVER_HEIGHT * 0.7; const scale = Math.min(boxW / w, boxH / h, 3); const dw = w * scale; const dh = h * scale; ctx.drawImage(img, (COVER_WIDTH - dw) / 2, (COVER_HEIGHT - dh) / 2, dw, dh); const blob = await toBlob(canvas, 'image/png'); return new File([blob], 'cover.png', { type: 'image/png' }); }