import { BadgeTemplate, BadgeLayer } from '@/types/newsletter'; // Vymazané všetky preddefinované šablóny — v systéme sú výhradne vlastné cenovky vytvorené používateľom export const DEFAULT_BADGE_TEMPLATES: BadgeTemplate[] = []; export interface BadgeProductData { priceMain: string; priceSub: string; discountText?: string; unitPrice?: string; } /** * Vygeneruje 100% čistý vektorový SVG reťazec cenovky bez akýchkoľvek * editovacích rámčekov, tooltippov či ovládacích prvkov. * Centy sú naformátované ako horný index (tspan dy="-0.45em"). * Cena je predvolene bez znaku €. */ export function renderCleanBadgeSvg( template: BadgeTemplate, product: BadgeProductData, overrideWidth?: number ): string { const pMain = product.priceMain || '0'; const pSub = product.priceSub || '00'; const discount = (product.discountText || '').replace('-', ''); const unitPrice = product.unitPrice || ''; // Získanie viewBox alebo predvolených rozmerov let vb = '0 0 140 95'; let vbWidth = 140; let vbHeight = 95; if (template.svgRaw) { const vbMatch = template.svgRaw.match(/viewBox=["']([^"']+)["']/i); if (vbMatch) { vb = vbMatch[1]; const parts = vb.trim().split(/\s+/).map(Number); if (parts.length === 4 && parts[2] > 0 && parts[3] > 0) { vbWidth = parts[2]; vbHeight = parts[3]; } } else { const wMatch = template.svgRaw.match(/width=["']([^"']+)["']/i); const hMatch = template.svgRaw.match(/height=["']([^"']+)["']/i); if (wMatch && hMatch) { const w = parseFloat(wMatch[1]); const h = parseFloat(hMatch[1]); if (w > 0 && h > 0) { vbWidth = w; vbHeight = h; vb = `0 0 ${w} ${h}`; } } } } // Extrahovanie vnútorného obsahu SVG (medzi a ) let innerBg = ''; if (template.svgRaw) { const inside = template.svgRaw.replace(/<\/?svg[^>]*>/gi, '').trim(); innerBg = inside; } else { innerBg = ``; } // Šírka výsledného SVG const displayWidth = overrideWidth || template.width || 110; const displayHeight = Math.round((displayWidth / vbWidth) * vbHeight); // Zostavenie textových vrstiev const textLayersSvg = (template.layersConfig || []).map((layer) => { const rawText = layer.text || ''; const fontSize = layer.fontSize || 16; const subFontSize = layer.indexFontSize || Math.max(10, Math.round(fontSize * 0.55)); const subDy = layer.indexOffsetY !== undefined ? `${layer.indexOffsetY}px` : '-0.45em'; const subDx = layer.indexOffsetX !== undefined ? `${layer.indexOffsetX}px` : '1px'; const revertDy = layer.indexOffsetY !== undefined ? `${-layer.indexOffsetY}px` : '0.45em'; const fontWeight = layer.fontWeight || '700'; const fill = layer.fill || '#ffffff'; const fontFamily = layer.fontFamily ? `'${layer.fontFamily}', -apple-system, BlinkMacSystemFont, Arial, sans-serif` : `'Arial Narrow', Arial, sans-serif`; let anchor = 'middle'; if (layer.align === 'left') anchor = 'start'; if (layer.align === 'right') anchor = 'end'; // Formátovanie textu: ak obsahuje {cena} alebo {centy}, centy vložíme do ako horný index let contentSvg = ''; if (rawText.includes('{cena}')) { const parts = rawText.split('{cena}'); const before = escapeXml(parts[0] || ''); const after = escapeXml(parts[1] || ''); contentSvg = `${before}${escapeXml(pMain)}${escapeXml(pSub)}${after ? `${after}` : ''}`; } else if (rawText.includes('{centy}')) { const parts = rawText.split('{centy}'); const before = escapeXml(parts[0] || ''); const after = escapeXml(parts[1] || ''); contentSvg = `${before}${escapeXml(pSub)}${after ? `${after}` : ''}`; } else { let replaced = rawText .replace(/{cela_suma}/g, pMain) .replace(/{zlava}/g, discount) .replace(/{jednotkova_cena}/g, unitPrice) .replace(/{mena}/g, '€'); contentSvg = escapeXml(replaced); } return `${contentSvg}`; }).join('\n '); return ` ${innerBg} ${textLayersSvg} `; } /** * Skonvertuje SVG reťazec do čistého PNG Data URL v prehliadači * (bez akýchkoľvek DOM prvkov alebo modrých drag & drop rámčekov). */ export async function svgToCleanPng( svgString: string, width: number, height: number ): Promise { if (typeof window === 'undefined') { return `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`; } return new Promise((resolve) => { try { const img = new Image(); const svgBlob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' }); const url = URL.createObjectURL(svgBlob); img.onload = () => { try { const canvas = document.createElement('canvas'); const scale = 2; // Vysoké rozlíšenie Retina canvas.width = width * scale; canvas.height = height * scale; const ctx = canvas.getContext('2d'); if (ctx) { ctx.scale(scale, scale); ctx.drawImage(img, 0, 0, width, height); URL.revokeObjectURL(url); resolve(canvas.toDataURL('image/png')); return; } } catch (e) { console.warn('Canvas export fallback to SVG data URL', e); } URL.revokeObjectURL(url); resolve(`data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`); }; img.onerror = () => { URL.revokeObjectURL(url); resolve(`data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`); }; img.src = url; } catch (err) { resolve(`data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`); } }); } function escapeXml(unsafe: string): string { if (!unsafe) return ''; return unsafe .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }