import type { APIRoute } from 'astro'; import { renderCell, type CropRel } from '../../../lib/printrender'; import { loadSource, sheetFilename, type PrintSource } from '../../../lib/printsource'; import { parseSizeMm, mmToPx, labelMm } from '../../../lib/paper'; import { dpiCheck } from '../../../lib/printlayout'; export const prerender = false; const json = (b: unknown, s = 200) => new Response(JSON.stringify(b), { status: s, headers: { 'Content-Type': 'application/json' } }); /** * Ein einzelnes Bild exakt auf ein Maß bringen — ohne KI, ohne Bogen. * Antwort: die fertige Bilddatei (PNG/JPG) mit dpi-Metadaten. */ export const POST: APIRoute = async ({ request, locals }) => { if (!locals.user) return new Response('Unauthorized', { status: 401 }); let body: any; try { body = await request.json(); } catch { return json({ error: 'Ungültige Anfrage.' }, 400); } try { const src: PrintSource = body?.src; const crop: CropRel | null = body?.crop ?? null; let size = (Number(body?.wMm) > 0 && Number(body?.hMm) > 0) ? { w: Number(body.wMm), h: Number(body.hMm) } : parseSizeMm(String(body?.size || '')); if (!size) return json({ error: 'Maß fehlt oder ist ungültig.' }, 400); if (body?.landscape) size = { w: size.h, h: size.w }; if (size.w < 5 || size.h < 5 || size.w > 2000 || size.h > 2000) return json({ error: 'Maß außerhalb des zulässigen Bereichs.' }, 400); const dpi = Math.min(1200, Math.max(72, Number(body?.dpi) || 300)); const ext: 'jpg' | 'png' = body?.ext === 'png' ? 'png' : 'jpg'; const buf = await loadSource(src, locals.user as any); const out = await renderCell(buf, crop, size.w, size.h, dpi, { ext, bleedMm: Number(body?.bleedMm) || 0 }); // Hinweis, falls die Quelle für echte 300 dpi zu klein ist. const cropW = (crop?.w ?? 1) * out.srcPx[0]; const check = dpiCheck(cropW, size.w, dpi); return new Response(out.buffer, { headers: { 'Content-Type': out.ext === 'png' ? 'image/png' : 'image/jpeg', 'Content-Disposition': `attachment; filename="${sheetFilename(body?.name || labelMm(size.w, size.h).replace(/[^0-9x×]/g, ''), out.ext)}"`, 'X-Klarbild-Px': `${out.width}x${out.height}`, 'X-Klarbild-Real-Dpi': String(check.dpi), 'X-Klarbild-Dpi-Ok': check.ok ? '1' : '0', }, }); } catch (e: any) { return json({ error: e?.message || 'Bild konnte nicht erzeugt werden.' }, 400); } }; /** Kleine Hilfe für Skripte/MCP: Maß → Pixel bei dpi. */ export const GET: APIRoute = async ({ url, locals }) => { if (!locals.user) return new Response('Unauthorized', { status: 401 }); const size = parseSizeMm(url.searchParams.get('size') || ''); const dpi = Math.min(1200, Math.max(72, Number(url.searchParams.get('dpi')) || 300)); if (!size) return json({ error: 'Parameter size fehlt (z. B. 12x15 oder 35x45mm).' }, 400); return json({ mm: size, dpi, px: { w: mmToPx(size.w, dpi), h: mmToPx(size.h, dpi) }, label: labelMm(size.w, size.h) }); };