import React from 'react'; import { Document, Page, View, Text, StyleSheet, Link, Image } from '@react-pdf/renderer'; // Standard fonts like Helvetica are built-in to PDF and don't require registration // unless we want to use specific TTF files. Using built-in Helvetica for maximum stability. // ─── Brand Tokens (matching datasheet) ────────────────────────────────── const C = { navy: '#001a4d', navyDeep: '#000d26', accent: '#82ed20', white: '#FFFFFF', offWhite: '#f8f9fa', gray100: '#f3f4f6', gray200: '#e5e7eb', gray300: '#d1d5db', gray400: '#9ca3af', gray600: '#4b5563', gray900: '#111827', }; const MARGIN = 72; const styles = StyleSheet.create({ page: { color: C.gray900, lineHeight: 1.6, backgroundColor: C.white, paddingTop: 50, paddingBottom: 80, fontFamily: 'Helvetica', }, // Premium Header Layout hero: { backgroundColor: C.offWhite, paddingTop: 24, paddingBottom: 24, paddingHorizontal: MARGIN, marginBottom: 40, marginTop: -50, // Counters the page padding to achieve full-bleed top borderBottomWidth: 1, borderBottomColor: C.gray200, position: 'relative', }, headerTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 32, }, logo: { width: 140, // Increased to fit full logo height: 45, objectFit: 'contain', }, docType: { fontSize: 9, fontWeight: 700, color: C.navy, letterSpacing: 2, textTransform: 'uppercase', opacity: 0.6, }, headerTitleArea: { marginTop: 0, }, pageTitle: { fontSize: 16, fontWeight: 700, color: C.navyDeep, marginBottom: 4, textTransform: 'uppercase', letterSpacing: 0, }, accentBar: { width: 40, height: 4, backgroundColor: C.accent, marginTop: 12, borderRadius: 2, }, // Content Area content: { paddingHorizontal: MARGIN, }, // Lexical Elements with high-fidelity formatting paragraph: { fontSize: 10, color: C.gray600, lineHeight: 1.7, marginBottom: 14, textAlign: 'justify', }, heading1: { fontSize: 14, fontWeight: 700, color: C.navyDeep, textTransform: 'uppercase', letterSpacing: 0.8, marginTop: 0, marginBottom: 0, }, heading1Wrapper: { marginTop: 24, marginBottom: 12, borderLeftWidth: 3, borderLeftColor: C.accent, paddingLeft: 12, }, heading2: { fontSize: 11, fontWeight: 700, color: C.navyDeep, marginTop: 18, marginBottom: 10, textTransform: 'uppercase', letterSpacing: 0.5, }, heading3: { fontSize: 10, fontWeight: 700, color: C.navyDeep, marginTop: 14, marginBottom: 8, }, list: { marginBottom: 16, marginLeft: 4, }, listItem: { flexDirection: 'row', marginBottom: 6, }, listItemBullet: { width: 20, fontSize: 10, color: C.gray400, fontWeight: 700, }, listItemContent: { flex: 1, fontSize: 10, color: C.gray600, lineHeight: 1.7, textAlign: 'justify', }, link: { color: C.navy, textDecoration: 'underline', }, textBold: { fontWeight: 700, fontFamily: 'Helvetica-Bold', color: C.navyDeep, }, textItalic: { fontStyle: 'italic', }, // Industrial Footer footer: { position: 'absolute', bottom: 30, left: MARGIN, right: MARGIN, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-end', paddingTop: 16, borderTopWidth: 0.5, borderTopColor: C.gray200, }, footerInfo: { flexDirection: 'column', }, footerBrand: { fontSize: 10, fontWeight: 700, color: C.navyDeep, textTransform: 'uppercase', letterSpacing: 1, marginBottom: 4, }, footerText: { fontSize: 7, color: C.gray400, fontWeight: 500, textTransform: 'uppercase', letterSpacing: 0.5, }, pageNum: { fontSize: 8, color: C.gray400, }, }); // ─── Lexical to React-PDF Renderer ──────────────────────────────── const renderLexicalNode = (node: any, idx: number): React.ReactNode => { if (!node) return null; switch (node.type) { case 'text': { const format = node.format || 0; const isBold = (format & 1) !== 0; const isItalic = (format & 2) !== 0; let elementStyle: any = {}; if (isBold) elementStyle = { ...elementStyle, ...styles.textBold }; if (isItalic) elementStyle = { ...elementStyle, ...styles.textItalic }; return ( {node.text} ); } case 'paragraph': { if (!node.children || node.children.length === 0) return null; return ( {node.children?.map((child: any, i: number) => renderLexicalNode(child, i))} ); } case 'heading': { if (!node.children || node.children.length === 0) return null; let hStyle = styles.heading3; let isH1 = false; if (node.tag === 'h1') { hStyle = styles.heading1; isH1 = true; } if (node.tag === 'h2') hStyle = styles.heading2; const content = ( {node.children?.map((child: any, i: number) => renderLexicalNode(child, i))} ); if (isH1) { return ( {content} ); } return content; } case 'list': { if (!node.children || node.children.length === 0) return null; return ( {node.children?.map((child: any, i: number) => { if (child.type === 'listitem') { return ( {node.listType === 'number' ? `${i + 1}.` : '•'} {child.children?.map((c: any, ci: number) => renderLexicalNode(c, ci))} ); } return renderLexicalNode(child, i); })} ); } case 'link': { const href = node.fields?.url || node.url || '#'; return ( {node.children?.map((child: any, i: number) => renderLexicalNode(child, i))} ); } case 'linebreak': { return {'\n'}; } // Ignore payload blocks recursively to avoid crashing case 'block': return null; default: if (node.children) { return ( {node.children.map((child: any, i: number) => renderLexicalNode(child, i))} ); } return null; } }; interface PDFPageProps { page: any; locale?: string; } export const PDFPage: React.FC = ({ page, locale = 'de' }) => { const dateStr = new Date().toLocaleDateString(locale === 'en' ? 'en-US' : 'de-DE', { year: 'numeric', month: 'long', day: 'numeric', }); const logoPath = `${process.cwd()}/public/logo-full.png`; return ( {/* Improved Hero Header - No longer fixed so it doesn't repeat on all pages */} {page.title} {page.content?.root?.children?.map((node: any, i: number) => renderLexicalNode(node, i), )} {/* Industrial footer with page numbers */} KLZ VERTRIEBS GMBH RAIFFEISENSTRASSE 22 • 73630 REMSHALDEN • {dateStr} `${pageNumber} / ${totalPages}`} /> ); };