fix: resolve background fill and text colors on both business card sides

This commit is contained in:
2026-06-11 23:20:03 +02:00
parent 175cb10e01
commit 4c6e8cb28b
12 changed files with 9884 additions and 9825 deletions

File diff suppressed because one or more lines are too long

View File

@@ -46,6 +46,7 @@ export interface PDFBusinessCardProps {
person: PersonData;
qrCodeDataUrl: string;
variant?: 'blue' | 'white' | 'black';
isViaprinto?: boolean;
}
// Die Buchstaben K, L, Z im SVG (inklusive dem kleinen Stück vom Z oben links)
@@ -178,7 +179,7 @@ const styles = StyleSheet.create({
accentLineHorizontal: {
flex: 1,
height: 1.5,
backgroundColor: COLORS.primary, // Brand blue line
backgroundColor: '#011dff', // Brand blue line
marginLeft: 15,
marginRight: 0,
},
@@ -223,7 +224,7 @@ const styles = StyleSheet.create({
roleText: {
fontSize: 7.5,
fontWeight: 'bold',
color: COLORS.primary,
color: '#011dff',
letterSpacing: 0.5,
textTransform: 'uppercase',
},
@@ -258,15 +259,15 @@ const styles = StyleSheet.create({
gap: 3,
},
qrCodeWrapper: {
width: '13mm',
height: '13mm',
width: '11mm',
height: '11mm',
},
qrCode: {
width: '100%',
height: '100%',
},
qrLabel: {
fontSize: 4.5,
fontSize: 4,
color: COLORS.neutralDark, // Subtle grey instead of loud blue
fontWeight: 'bold',
letterSpacing: 1.2,
@@ -367,6 +368,7 @@ const VectorLogo = ({
overrideWhiteColor,
pathOverrides,
solidPaths,
solidBackgroundColor = '#000a66',
}: {
paths: { d: string; fill: string }[];
style: any;
@@ -374,20 +376,25 @@ const VectorLogo = ({
overrideWhiteColor?: string;
pathOverrides?: Record<number, string>;
solidPaths?: number[];
solidBackgroundColor?: string;
}) => (
<Svg viewBox="0 0 295 99" style={style}>
<G {...({ fillRule: 'evenodd', clipRule: 'evenodd' } as any)}>
{paths.map((p, i) => {
let fill = overrideColor || p.fill;
let d = p.d;
if (solidPaths && solidPaths.includes(i)) {
// Keep only the first subpath to make the shape solid (remove holes)
{/* Background layer: solid fills to prevent see-through */}
{solidPaths &&
solidBackgroundColor &&
paths.map((p, i) => {
if (!solidPaths.includes(i)) return null;
let d = p.d;
const zIndex = d.toUpperCase().indexOf('Z');
if (zIndex !== -1) {
d = d.substring(0, zIndex + 1);
}
}
return <Path key={`bg-${i}`} d={d} fill={solidBackgroundColor} />;
})}
{paths.map((p, i) => {
let fill = overrideColor || p.fill;
const d = p.d;
if (pathOverrides && pathOverrides[i]) {
fill = pathOverrides[i];
@@ -688,6 +695,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
person,
qrCodeDataUrl,
variant = 'blue',
isViaprinto = false,
}) => {
const isWhiteVariant = variant === 'white' || variant === 'black';
@@ -737,11 +745,9 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
>
<View style={styles.truckWrapper}>
<TruckBlueprint
style={[
styles.frontBackgroundImage,
isWhiteVariant && { opacity: 0.2 },
!isWhiteVariant && { opacity: 0.25 },
]}
style={styles.frontBackgroundImage}
opacity={isWhiteVariant ? 0.2 : 0.25}
backgroundColor={isWhiteVariant ? '#ffffff' : '#011dff'}
/>
</View>
<View
@@ -781,6 +787,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
Hochspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconSolar
style={styles.frontBrandIcon}
@@ -793,33 +800,35 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
</View>
</View>
<View style={styles.frontContainer}>
{/* Base Logo: text is white, truck is green. Make KLZ letters blend into background */}
{/* Base Logo: text is white, truck is green. Fill KLZ background to hide truck, keep outline white */}
<VectorLogo
paths={getFrontLogoPaths()}
style={styles.frontLogo}
pathOverrides={KLZ_PATHS.reduce((acc, idx) => ({ ...acc, [idx]: COLORS.blue }), {})}
solidPaths={KLZ_PATHS}
solidBackgroundColor={isWhiteVariant ? '#ffffff' : '#011dff'}
/>
{/* Letterpress Mask Layer: We use #ff00fe as a marker color. Only the "KLZ" letters! */}
<VectorLogo
paths={getFrontLogoPaths().filter((_, i) => KLZ_PATHS.includes(i))}
style={[styles.frontLogo, { position: 'absolute' }]}
overrideColor="#ff00fe"
solidPaths={KLZ_PATHS.map((_, i) => i)}
/>
{isViaprinto && (
<VectorLogo
paths={getFrontLogoPaths().filter((_, i) => KLZ_PATHS.includes(i))}
style={[styles.frontLogo, { position: 'absolute' }]}
overrideColor="#ff00fe"
solidPaths={KLZ_PATHS.map((_, i) => i)}
/>
)}
</View>
</Page>
{/* RÜCKSEITE (White page with contact info) */}
{/* VORDERSEITE (White page with contact info) */}
<Page size={[258, 173]} style={styles.pageBleedBack}>
<View style={styles.backContainer}>
<View style={styles.backHeader}>
<VectorLogo
paths={getBackLogoPaths()}
style={styles.backLogo}
overrideWhiteColor={COLORS.primary}
solidPaths={KLZ_PATHS}
overrideWhiteColor={'#011dff'}
pathOverrides={KLZ_PATHS.reduce((acc, idx) => ({ ...acc, [idx]: 'none' }), {})}
/>
<View style={styles.accentLineHorizontal} />
</View>

View File

@@ -19,6 +19,8 @@ const CONFIG = {
truckImage: path.join(process.cwd(), 'public/truck.png'),
fontRegular: path.join(process.cwd(), 'public/fonts/Inter-Regular.ttf'),
fontBold: path.join(process.cwd(), 'public/fonts/Inter-Bold.ttf'),
ghostscriptScript:
'/Users/marcmintel/.gemini/config/plugins/mintel-ui-standards-plugin/skills/mintel-print-workflow/scripts/convert-cmyk.sh',
};
const PEOPLE: PersonData[] = [
@@ -71,39 +73,52 @@ END:VCARD`;
});
const safeName = person.name.replace(/\s+/g, '_');
const finalFileName = `KLZ_Visitenkarte_${safeName}.pdf`;
const rgbPath = path.join(CONFIG.outputDir, `RGB_${finalFileName}`);
const finalPath = path.join(CONFIG.outputDir, finalFileName);
console.log(`- Rendering React-PDF layout (RGB ONLY, Blue Variant)...`);
const buffer = await renderToBuffer(
// 1. STANDARD DIGITAL PDF (RGB, no letterpress mask)
console.log(`- Rendering standard digital PDF (RGB ONLY, Blue Variant)...`);
const finalFileName = `KLZ_Visitenkarte_${safeName}.pdf`;
const tempFileName = `temp_${finalFileName}`;
const finalPath = path.join(CONFIG.outputDir, finalFileName);
const tempPath = path.join(CONFIG.outputDir, tempFileName);
let buffer = await renderToBuffer(
React.createElement(PDFBusinessCard, {
person,
qrCodeDataUrl,
variant: 'blue', // Explicitly use blue now
variant: 'blue',
isViaprinto: false,
}),
);
fs.writeFileSync(tempPath, buffer);
fs.writeFileSync(rgbPath, buffer);
console.log(`- Outlining fonts via Ghostscript (Keeping RGB)...`);
const gsCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPath}" "${rgbPath}"`;
execSync(gsCommand, { stdio: 'ignore' });
fs.unlinkSync(rgbPath);
// Convert fonts to outlines via ghostscript
const gsCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPath}" "${tempPath}"`;
execSync(gsCommand, { stdio: 'inherit' });
fs.unlinkSync(tempPath);
console.log(`✓ Final Print File generated (Fonts to Paths, RGB preserved): ${finalFileName}`);
// 2. VIAPRINTO PDF (Letterpress mask, CMYK)
console.log(`- Rendering Viaprinto PDF (Letterpress mask)...`);
const tempViaprintoPath = path.join(CONFIG.outputDir, `temp_viaprinto_${safeName}.pdf`);
buffer = await renderToBuffer(
React.createElement(PDFBusinessCard, {
person,
qrCodeDataUrl,
variant: 'blue',
isViaprinto: true,
}),
);
fs.writeFileSync(tempViaprintoPath, buffer);
// Convert to CMYK for Viaprinto
const finalFileNameCMYK = `KLZ_Visitenkarte_${safeName}_Viaprinto_CMYK_91x61mm_3mmBleed.pdf`;
const finalPathCMYK = path.join(CONFIG.outputDir, finalFileNameCMYK);
console.log(`- Converting to CMYK for Viaprinto...`);
const cmykScript =
'/Users/marcmintel/.gemini/config/plugins/mintel-ui-standards-plugin/skills/mintel-print-workflow/scripts/convert-cmyk.sh';
const cmykCommand = `bash "${cmykScript}" "${finalPath}" "${finalPathCMYK}"`;
const cmykCommand = `bash "${CONFIG.ghostscriptScript}" "${tempViaprintoPath}" "${finalPathCMYK}"`;
try {
execSync(cmykCommand, { stdio: 'ignore' });
execSync(cmykCommand, { stdio: 'inherit' });
fs.unlinkSync(tempViaprintoPath);
console.log(`✓ CMYK Print File generated: ${finalFileNameCMYK}`);
} catch (e) {
console.error(`❌ CMYK conversion failed! Is Ghostscript installed?`);