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';
// 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%',
objectFit: 'contain',
objectPositionX: 'center',
objectPositionY: 'center',
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: 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 }: { paths: { d: string; fill: string }[]; style: any }) => (
);
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
);
};