import React from 'react'; import * as fs from 'fs'; import { Document, Page, Text, View, StyleSheet, Font, Image, Svg, Path, G, Rect, ClipPath, Circle, } from '@react-pdf/renderer'; import * as path from 'path'; import { TruckBlueprint } from './TruckBlueprint'; // Register embedded fonts so Ghostscript -dNoOutputFonts can convert them to // outlines (curves). Built-in PDF fonts like Helvetica are NEVER embedded and // therefore cannot be outlined — always use a physical font file here. Font.register({ family: 'Inter', fonts: [ { src: path.join(process.cwd(), 'public', 'fonts', 'Inter-Regular.woff'), fontWeight: 'normal', }, { src: path.join(process.cwd(), 'public', 'fonts', 'Inter-Bold.woff'), fontWeight: 'bold', }, ], }); export interface PersonData { name: string; role: string; phone: string; email: string; } export interface PDFBusinessCardProps { person: PersonData; qrCodeDataUrl: string; variant?: 'blue' | 'white' | 'black'; } // Mintel Aesthetics Colors - EXACT PROD COLORS const COLORS = { primary: '#011dff', // Brand Royal Blue green: '#82ed20', // Brand Accent Green darkGreen: '#4A9E00', // Darker green for white background visibility white: '#ffffff', grayText: '#6c757d', black: '#0a0a0a', neutralDark: '#263336', lightGray: '#f8f9fa', }; const styles = StyleSheet.create({ pageBleed: { width: '91mm', height: '61mm', backgroundColor: COLORS.primary, position: 'relative', fontFamily: 'Inter', overflow: 'hidden', }, pageBleedBack: { width: '91mm', height: '61mm', backgroundColor: COLORS.white, position: 'relative', fontFamily: 'Inter', overflow: 'hidden', }, // -- BLUE PAGE (Rückseite) -- truckWrapper: { position: 'absolute', top: 0, left: 0, right: 0, bottom: '18mm', // Leave space for the bar below zIndex: 1, }, frontBackgroundImage: { width: '100%', height: '100%', opacity: 0.4, // Less transparent, more contrast }, frontContainer: { flex: 1, paddingBottom: '18mm', // Push the logo up to center it in the area above the bar justifyContent: 'center', alignItems: 'center', }, frontLogo: { width: 175, zIndex: 100, }, frontTagline: { color: COLORS.white, fontSize: 6, marginTop: 10, letterSpacing: 1.5, fontWeight: 'bold', textTransform: 'uppercase', zIndex: 100, }, frontGreenBarBottom: { position: 'absolute', bottom: 0, left: 0, right: 0, height: '18mm', borderTop: `2pt solid ${COLORS.green}`, backgroundColor: '#000a66', zIndex: 50, flexDirection: 'column', justifyContent: 'center', alignItems: 'center', gap: 5, }, frontIconsGroup: { flexDirection: 'row', gap: 12, }, frontBrandIconWrapper: { alignItems: 'center', gap: 2, }, frontBrandIcon: { width: 18, height: 18, opacity: 0.9, }, frontBrandText: { fontSize: 5.5, color: COLORS.white, textTransform: 'uppercase', letterSpacing: 0.5, opacity: 0.9, }, frontTaglineBar: { color: COLORS.green, fontSize: 7, letterSpacing: 1.5, fontWeight: 'bold', textTransform: 'uppercase', }, // -- WHITE PAGE (Vorderseite) -- backContainer: { flex: 1, paddingTop: '6mm', paddingBottom: '6mm', paddingLeft: '6mm', paddingRight: '6mm', flexDirection: 'column', }, backHeader: { marginBottom: '5mm', flexDirection: 'row', alignItems: 'center', width: '100%', }, backLogo: { width: 60, }, accentLineHorizontal: { flex: 1, height: 1.5, backgroundColor: COLORS.primary, // Brand blue line marginLeft: 15, marginRight: 0, }, accentDot: { width: 4, height: 4, borderRadius: 2, backgroundColor: COLORS.green, marginRight: 20, // Keep away from the edge! }, accentBoxEdge: { width: 3, height: 25, // Sleek vertical bar on the edge backgroundColor: COLORS.green, position: 'absolute', right: '-8mm', // Bleed edge top: -5, }, backBody: { flex: 1, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', width: '100%', }, leftColumn: { flex: 1, flexDirection: 'column', paddingRight: '4mm', }, nameSection: { marginBottom: '4mm', }, nameText: { fontSize: 14, fontWeight: 'bold', color: COLORS.neutralDark, marginBottom: 2, letterSpacing: -0.2, }, roleText: { fontSize: 7.5, fontWeight: 'bold', color: COLORS.primary, letterSpacing: 0.5, textTransform: 'uppercase', }, contactSection: { flexDirection: 'column', gap: 4, }, contactRow: { flexDirection: 'row', alignItems: 'center', }, iconWrapper: { width: 12, justifyContent: 'center', alignItems: 'flex-start', }, contactText: { fontSize: 8, color: COLORS.neutralDark, fontWeight: 'normal', paddingTop: 1, }, rightColumn: { flexDirection: 'column', alignItems: 'flex-end', justifyContent: 'flex-start', }, // Ultra Minimalist QR Area qrContainer: { flexDirection: 'column', alignItems: 'center', gap: 3, }, qrCodeWrapper: { width: '13mm', height: '13mm', }, qrCode: { width: '100%', height: '100%', }, qrLabel: { fontSize: 4.5, color: COLORS.neutralDark, // Subtle grey instead of loud blue fontWeight: 'bold', letterSpacing: 1.2, }, addressFooter: { width: '100%', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingTop: '2.5mm', marginTop: 'auto', marginBottom: '2mm', // Force extra space from the bottom edge }, addressFooterText: { fontSize: 7, // increased from 6 color: COLORS.grayText, letterSpacing: 0.3, fontWeight: 'normal', }, }); // SVG Icons const PhoneIcon = () => ( ); const EmailIcon = () => ( ); const GlobeIcon = () => ( ); const LocationIcon = () => ( ); const ScanIcon = () => ( ); const parseSvgPaths = (filePath: string) => { try { const raw = fs.readFileSync(filePath, 'utf8'); const paths = []; const pathRegex = /]+)>/g; let match; while ((match = pathRegex.exec(raw)) !== null) { const attrs = match[1]; const dMatch = attrs.match(/d="([^"]+)"/); if (!dMatch) continue; let fill = 'white'; const fillMatch = attrs.match(/fill(?:[:=])"?([^;"\s>]+)/); if (fillMatch) { fill = fillMatch[1].replace(/"$/, ''); if (fill === 'none') continue; } paths.push({ d: dMatch[1], fill }); } return paths; } catch (e) { console.error('Could not parse SVG:', filePath); return []; } }; const VectorLogo = ({ paths, style, overrideColor, overrideWhiteColor, }: { paths: { d: string; fill: string }[]; style: any; overrideColor?: string; overrideWhiteColor?: string; }) => ( {paths.map((p, i) => { let fill = overrideColor || p.fill; if ( overrideWhiteColor && (p.fill === 'white' || p.fill === '#ffffff' || p.fill === '#fff') ) { fill = overrideWhiteColor; } return ; })} ); export const IconSolar = ({ style, color = 'white' }: { style: any; color?: string }) => ( ); export const AbstractCable = ({ size = 10, color = COLORS.green, }: { size?: number; color?: string; }) => ( ); export const IconLowVoltage = ({ style, color = 'white' }: { style: any; color?: string }) => ( ); export const IconMediumVoltage = ({ style, color = 'white' }: { style: any; color?: string }) => ( ); export const IconHighVoltage = ({ style, color = 'white' }: { style: any; color?: string }) => ( ); export const PDFBusinessCard: React.FC = ({ person, qrCodeDataUrl, variant = 'blue', }) => { const isWhiteVariant = variant === 'white' || variant === 'black'; const logoWhitePath = path.join(process.cwd(), 'public', 'logo-green-text.svg'); const logoBluePath = path.join(process.cwd(), 'public', 'logo-green-text-blue.svg'); const logoBlueWithTaglinePath = path.join( process.cwd(), 'public', 'logo-green-text-blue-with-tagline.svg', ); const logoBlackWithTaglinePath = path.join( process.cwd(), 'public', 'logo-green-text-black-with-tagline.svg', ); const logoWhitePaths = parseSvgPaths(logoWhitePath); const logoBluePaths = parseSvgPaths(logoBluePath); const logoBlueWithTaglinePaths = parseSvgPaths(logoBlueWithTaglinePath); const logoBlackWithTaglinePaths = parseSvgPaths(logoBlackWithTaglinePath); const getFrontLogoPaths = () => { if (variant === 'black') return logoBlackWithTaglinePaths; if (variant === 'white') return logoBlueWithTaglinePaths; return logoWhitePaths; }; const bgImagePath = isWhiteVariant ? path.join(process.cwd(), 'public', 'media', 'cable_drums_truck.png') : path.join(process.cwd(), 'public', 'cable_drums_bg.png'); return ( {/* VORDERSEITE (Blue page with logo, truck and tagline) */} VON 0,6/1,0 KV BIS 64/110 KV Niederspannung Mittelspannung Hochspannung Solar {/* RÜCKSEITE (White page with contact info) */} {person.name} {person.role} {person.phone} {person.email} klz-cables.com ABSPEICHERN KLZ Vertriebs GmbH · Raiffeisenstraße 22 · 73630 Remshalden ); };