// Papier- und Bildformate für den Druckbogen (Modul „Druck", ohne KI). // Alle Maße in Millimetern — mm ist die Leitwährung dieses Moduls, weil ein // Druckbogen physisch ist. Pixel entstehen erst beim Rendern (mm → dpi). export interface PaperSize { id: string; label: string; w: number; h: number; group: string } /** Bogenformate (Hochformat-Konvention: kurze Seite × lange Seite). */ export const PAPERS: PaperSize[] = [ // DIN { id: 'A6', label: 'DIN A6', w: 105, h: 148, group: 'DIN' }, { id: 'A5', label: 'DIN A5', w: 148, h: 210, group: 'DIN' }, { id: 'A4', label: 'DIN A4', w: 210, h: 297, group: 'DIN' }, { id: 'A3', label: 'DIN A3', w: 297, h: 420, group: 'DIN' }, { id: 'A3plus', label: 'DIN A3+ (Super A3)', w: 329, h: 483, group: 'DIN' }, { id: 'A2', label: 'DIN A2', w: 420, h: 594, group: 'DIN' }, // Fotopapier (Zuschnitte, wie sie im Fachhandel liegen) { id: 'F9x13', label: 'Fotopapier 9 × 13 cm', w: 90, h: 130, group: 'Fotopapier' }, { id: 'F10x15', label: 'Fotopapier 10 × 15 cm', w: 100, h: 150, group: 'Fotopapier' }, { id: 'F13x18', label: 'Fotopapier 13 × 18 cm', w: 130, h: 180, group: 'Fotopapier' }, { id: 'F15x20', label: 'Fotopapier 15 × 20 cm', w: 150, h: 200, group: 'Fotopapier' }, { id: 'F20x30', label: 'Fotopapier 20 × 30 cm', w: 200, h: 300, group: 'Fotopapier' }, // US { id: 'Letter', label: 'US Letter', w: 215.9, h: 279.4, group: 'US' }, { id: 'Legal', label: 'US Legal', w: 215.9, h: 355.6, group: 'US' }, ]; /** Bildformate (Endformat eines einzelnen Bildes auf dem Bogen). */ export interface PhotoSize { id: string; label: string; w: number; h: number; group: string } export const PHOTO_SIZES: PhotoSize[] = [ // Passbild / Ausweis { id: 'P35x45', label: 'Passbild 35 × 45 mm (biometrisch)', w: 35, h: 45, group: 'Passbild' }, { id: 'P50x50', label: 'Visum USA 51 × 51 mm (2 × 2 in)', w: 50.8, h: 50.8, group: 'Passbild' }, // Kleinformate (Kita, Schule, Portemonnaie) { id: 'K20x30', label: '2 × 3 cm', w: 20, h: 30, group: 'Klein' }, { id: 'K30x40', label: '3 × 4 cm', w: 30, h: 40, group: 'Klein' }, { id: 'K40x50', label: '4 × 5 cm', w: 40, h: 50, group: 'Klein' }, { id: 'K45x60', label: '4,5 × 6 cm', w: 45, h: 60, group: 'Klein' }, { id: 'K60x80', label: '6 × 8 cm', w: 60, h: 80, group: 'Klein' }, { id: 'K60x90', label: '6 × 9 cm', w: 60, h: 90, group: 'Klein' }, // Klassische Fotoformate — deckungsgleich mit den KI-Formaten aus src/lib/format.ts { id: 'S9x13', label: '9 × 13 cm', w: 90, h: 130, group: 'Foto' }, { id: 'S10x15', label: '10 × 15 cm', w: 100, h: 150, group: 'Foto' }, { id: 'S11x15', label: '11 × 15 cm', w: 110, h: 150, group: 'Foto' }, { id: 'S12x15', label: '12 × 15 cm', w: 120, h: 150, group: 'Foto' }, { id: 'S13x18', label: '13 × 18 cm', w: 130, h: 180, group: 'Foto' }, { id: 'S15x20', label: '15 × 20 cm', w: 150, h: 200, group: 'Foto' }, { id: 'S18x24', label: '18 × 24 cm', w: 180, h: 240, group: 'Foto' }, { id: 'S20x25', label: '20 × 25 cm', w: 200, h: 250, group: 'Foto' }, { id: 'S20x30', label: '20 × 30 cm', w: 200, h: 300, group: 'Foto' }, { id: 'S24x30', label: '24 × 30 cm', w: 240, h: 300, group: 'Foto' }, // Poster- und Rahmenformate (Bilderrahmen im Handel) { id: 'R30x40', label: '30 × 40 cm', w: 300, h: 400, group: 'Poster & Rahmen' }, { id: 'R30x45', label: '30 × 45 cm', w: 300, h: 450, group: 'Poster & Rahmen' }, { id: 'R40x50', label: '40 × 50 cm', w: 400, h: 500, group: 'Poster & Rahmen' }, { id: 'R40x60', label: '40 × 60 cm', w: 400, h: 600, group: 'Poster & Rahmen' }, { id: 'R50x70', label: '50 × 70 cm', w: 500, h: 700, group: 'Poster & Rahmen' }, { id: 'R60x80', label: '60 × 80 cm', w: 600, h: 800, group: 'Poster & Rahmen' }, { id: 'R60x90', label: '60 × 90 cm', w: 600, h: 900, group: 'Poster & Rahmen' }, { id: 'R70x100', label: '70 × 100 cm', w: 700, h: 1000, group: 'Poster & Rahmen' }, // Quadrate { id: 'Q10x10', label: '10 × 10 cm', w: 100, h: 100, group: 'Quadrat' }, { id: 'Q13x13', label: '13 × 13 cm', w: 130, h: 130, group: 'Quadrat' }, { id: 'Q20x20', label: '20 × 20 cm', w: 200, h: 200, group: 'Quadrat' }, { id: 'Q30x30', label: '30 × 30 cm', w: 300, h: 300, group: 'Quadrat' }, // DIN als Bildformat { id: 'DA6', label: 'DIN A6 (10,5 × 14,8 cm)', w: 105, h: 148, group: 'DIN' }, { id: 'DA5', label: 'DIN A5 (14,8 × 21 cm)', w: 148, h: 210, group: 'DIN' }, { id: 'DA4', label: 'DIN A4 (21 × 29,7 cm)', w: 210, h: 297, group: 'DIN' }, { id: 'DA3', label: 'DIN A3 (29,7 × 42 cm)', w: 297, h: 420, group: 'DIN' }, { id: 'DA2', label: 'DIN A2 (42 × 59,4 cm)', w: 420, h: 594, group: 'DIN' }, // Bildschirm-Seitenverhältnisse als Druckmaß (The Frame & Co.) { id: 'W16x9', label: '16:9 „The Frame" (30 cm breit)', w: 300, h: 168.75, group: 'Seitenverhältnis' }, { id: 'W9x16', label: '9:16 Hochformat (30 cm hoch)', w: 168.75, h: 300, group: 'Seitenverhältnis' }, { id: 'W3x2', label: '3:2 (auf 30 cm)', w: 300, h: 200, group: 'Seitenverhältnis' }, { id: 'W4x3', label: '4:3 (auf 30 cm)', w: 300, h: 225, group: 'Seitenverhältnis' }, { id: 'W5x4', label: '5:4 (auf 30 cm)', w: 300, h: 240, group: 'Seitenverhältnis' }, ]; export const paperById = (id: string) => PAPERS.find((p) => p.id === id) || null; export const photoById = (id: string) => PHOTO_SIZES.find((p) => p.id === id) || null; /** * Freitext-Maß → mm. Bewusst großzügig, weil Till Maße so tippt, wie er sie sagt: * „12x15" → 120 × 150 mm (cm ist die Standardeinheit) * „12 × 15 cm" → 120 × 150 mm * „35x45mm" → 35 × 45 mm * „5" → 50 × 50 mm (quadratisch) * „4:3 / 15" → Seitenverhältnis 4:3, längere Seite 15 cm → 150 × 112,5 mm */ export function parseSizeMm(input: string): { w: number; h: number } | null { const t = (input || '').trim().toLowerCase().replace(/,/g, '.'); if (!t) return null; // Seitenverhältnis mit Zielkante: „4:3 / 15" oder „4:3 15cm" const ratio = /^(\d+(?:\.\d+)?)\s*[:/]\s*(\d+(?:\.\d+)?)\s*(?:[/@ ]\s*(\d+(?:\.\d+)?)\s*(mm|cm)?)?$/.exec(t); if (ratio && ratio[3]) { // a:b wird als Breite:Höhe gelesen — „3:4/15" ist also hochkant, „4:3/15" quer. const a = parseFloat(ratio[1]), b = parseFloat(ratio[2]); const long = ratio[4] === 'mm' ? parseFloat(ratio[3]) : parseFloat(ratio[3]) * 10; if (!a || !b || !long) return null; return a >= b ? norm(long, long * (b / a)) : norm(long * (a / b), long); } const unit = /mm\s*$/.test(t) ? 1 : 10; // ohne Einheit: cm const body = t.replace(/\s*(mm|cm)\s*$/, '').trim(); const wh = /^(\d+(?:\.\d+)?)\s*[x×*]\s*(\d+(?:\.\d+)?)$/.exec(body); if (wh) return norm(parseFloat(wh[1]) * unit, parseFloat(wh[2]) * unit); const sq = /^(\d+(?:\.\d+)?)$/.exec(body); if (sq) { const n = parseFloat(sq[1]) * unit; return norm(n, n); } return null; } function norm(w: number, h: number): { w: number; h: number } | null { const r = (n: number) => Math.round(n * 100) / 100; if (!(w > 0) || !(h > 0) || w > 2000 || h > 2000) return null; return { w: r(w), h: r(h) }; } export const mmToPx = (mm: number, dpi: number) => Math.round((mm / 25.4) * dpi); export const mmToPt = (mm: number) => (mm / 25.4) * 72; /** Hübsche Beschriftung eines Maßes — ohne die echten Nachkommastellen zu verlieren. */ export function labelMm(w: number, h: number): string { const mm = (n: number) => de(Math.round(n * 10) / 10); const cm = (n: number) => de(Math.round(n * 100) / 1000); return w < 100 && h < 100 ? `${mm(w)} × ${mm(h)} mm` : `${cm(w)} × ${cm(h)} cm`; } const de = (n: number) => String(n).replace('.', ',');