fix: resolve background fill and text colors on both business card sides
This commit is contained in:
19593
lib/TruckBlueprint.tsx
19593
lib/TruckBlueprint.tsx
File diff suppressed because one or more lines are too long
@@ -46,6 +46,7 @@ export interface PDFBusinessCardProps {
|
|||||||
person: PersonData;
|
person: PersonData;
|
||||||
qrCodeDataUrl: string;
|
qrCodeDataUrl: string;
|
||||||
variant?: 'blue' | 'white' | 'black';
|
variant?: 'blue' | 'white' | 'black';
|
||||||
|
isViaprinto?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Die Buchstaben K, L, Z im SVG (inklusive dem kleinen Stück vom Z oben links)
|
// 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: {
|
accentLineHorizontal: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
backgroundColor: COLORS.primary, // Brand blue line
|
backgroundColor: '#011dff', // Brand blue line
|
||||||
marginLeft: 15,
|
marginLeft: 15,
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
},
|
},
|
||||||
@@ -223,7 +224,7 @@ const styles = StyleSheet.create({
|
|||||||
roleText: {
|
roleText: {
|
||||||
fontSize: 7.5,
|
fontSize: 7.5,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: COLORS.primary,
|
color: '#011dff',
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
},
|
},
|
||||||
@@ -258,15 +259,15 @@ const styles = StyleSheet.create({
|
|||||||
gap: 3,
|
gap: 3,
|
||||||
},
|
},
|
||||||
qrCodeWrapper: {
|
qrCodeWrapper: {
|
||||||
width: '13mm',
|
width: '11mm',
|
||||||
height: '13mm',
|
height: '11mm',
|
||||||
},
|
},
|
||||||
qrCode: {
|
qrCode: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
qrLabel: {
|
qrLabel: {
|
||||||
fontSize: 4.5,
|
fontSize: 4,
|
||||||
color: COLORS.neutralDark, // Subtle grey instead of loud blue
|
color: COLORS.neutralDark, // Subtle grey instead of loud blue
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
letterSpacing: 1.2,
|
letterSpacing: 1.2,
|
||||||
@@ -367,6 +368,7 @@ const VectorLogo = ({
|
|||||||
overrideWhiteColor,
|
overrideWhiteColor,
|
||||||
pathOverrides,
|
pathOverrides,
|
||||||
solidPaths,
|
solidPaths,
|
||||||
|
solidBackgroundColor = '#000a66',
|
||||||
}: {
|
}: {
|
||||||
paths: { d: string; fill: string }[];
|
paths: { d: string; fill: string }[];
|
||||||
style: any;
|
style: any;
|
||||||
@@ -374,20 +376,25 @@ const VectorLogo = ({
|
|||||||
overrideWhiteColor?: string;
|
overrideWhiteColor?: string;
|
||||||
pathOverrides?: Record<number, string>;
|
pathOverrides?: Record<number, string>;
|
||||||
solidPaths?: number[];
|
solidPaths?: number[];
|
||||||
|
solidBackgroundColor?: string;
|
||||||
}) => (
|
}) => (
|
||||||
<Svg viewBox="0 0 295 99" style={style}>
|
<Svg viewBox="0 0 295 99" style={style}>
|
||||||
<G {...({ fillRule: 'evenodd', clipRule: 'evenodd' } as any)}>
|
<G {...({ fillRule: 'evenodd', clipRule: 'evenodd' } as any)}>
|
||||||
{paths.map((p, i) => {
|
{/* Background layer: solid fills to prevent see-through */}
|
||||||
let fill = overrideColor || p.fill;
|
{solidPaths &&
|
||||||
let d = p.d;
|
solidBackgroundColor &&
|
||||||
|
paths.map((p, i) => {
|
||||||
if (solidPaths && solidPaths.includes(i)) {
|
if (!solidPaths.includes(i)) return null;
|
||||||
// Keep only the first subpath to make the shape solid (remove holes)
|
let d = p.d;
|
||||||
const zIndex = d.toUpperCase().indexOf('Z');
|
const zIndex = d.toUpperCase().indexOf('Z');
|
||||||
if (zIndex !== -1) {
|
if (zIndex !== -1) {
|
||||||
d = d.substring(0, 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]) {
|
if (pathOverrides && pathOverrides[i]) {
|
||||||
fill = pathOverrides[i];
|
fill = pathOverrides[i];
|
||||||
@@ -688,6 +695,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
person,
|
person,
|
||||||
qrCodeDataUrl,
|
qrCodeDataUrl,
|
||||||
variant = 'blue',
|
variant = 'blue',
|
||||||
|
isViaprinto = false,
|
||||||
}) => {
|
}) => {
|
||||||
const isWhiteVariant = variant === 'white' || variant === 'black';
|
const isWhiteVariant = variant === 'white' || variant === 'black';
|
||||||
|
|
||||||
@@ -737,11 +745,9 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
>
|
>
|
||||||
<View style={styles.truckWrapper}>
|
<View style={styles.truckWrapper}>
|
||||||
<TruckBlueprint
|
<TruckBlueprint
|
||||||
style={[
|
style={styles.frontBackgroundImage}
|
||||||
styles.frontBackgroundImage,
|
opacity={isWhiteVariant ? 0.2 : 0.25}
|
||||||
isWhiteVariant && { opacity: 0.2 },
|
backgroundColor={isWhiteVariant ? '#ffffff' : '#011dff'}
|
||||||
!isWhiteVariant && { opacity: 0.25 },
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
@@ -781,6 +787,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
Hochspannung
|
Hochspannung
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.frontBrandIconWrapper}>
|
<View style={styles.frontBrandIconWrapper}>
|
||||||
<IconSolar
|
<IconSolar
|
||||||
style={styles.frontBrandIcon}
|
style={styles.frontBrandIcon}
|
||||||
@@ -793,33 +800,35 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.frontContainer}>
|
<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
|
<VectorLogo
|
||||||
paths={getFrontLogoPaths()}
|
paths={getFrontLogoPaths()}
|
||||||
style={styles.frontLogo}
|
style={styles.frontLogo}
|
||||||
pathOverrides={KLZ_PATHS.reduce((acc, idx) => ({ ...acc, [idx]: COLORS.blue }), {})}
|
|
||||||
solidPaths={KLZ_PATHS}
|
solidPaths={KLZ_PATHS}
|
||||||
|
solidBackgroundColor={isWhiteVariant ? '#ffffff' : '#011dff'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Letterpress Mask Layer: We use #ff00fe as a marker color. Only the "KLZ" letters! */}
|
{/* Letterpress Mask Layer: We use #ff00fe as a marker color. Only the "KLZ" letters! */}
|
||||||
<VectorLogo
|
{isViaprinto && (
|
||||||
paths={getFrontLogoPaths().filter((_, i) => KLZ_PATHS.includes(i))}
|
<VectorLogo
|
||||||
style={[styles.frontLogo, { position: 'absolute' }]}
|
paths={getFrontLogoPaths().filter((_, i) => KLZ_PATHS.includes(i))}
|
||||||
overrideColor="#ff00fe"
|
style={[styles.frontLogo, { position: 'absolute' }]}
|
||||||
solidPaths={KLZ_PATHS.map((_, i) => i)}
|
overrideColor="#ff00fe"
|
||||||
/>
|
solidPaths={KLZ_PATHS.map((_, i) => i)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
{/* RÜCKSEITE (White page with contact info) */}
|
{/* VORDERSEITE (White page with contact info) */}
|
||||||
<Page size={[258, 173]} style={styles.pageBleedBack}>
|
<Page size={[258, 173]} style={styles.pageBleedBack}>
|
||||||
<View style={styles.backContainer}>
|
<View style={styles.backContainer}>
|
||||||
<View style={styles.backHeader}>
|
<View style={styles.backHeader}>
|
||||||
<VectorLogo
|
<VectorLogo
|
||||||
paths={getBackLogoPaths()}
|
paths={getBackLogoPaths()}
|
||||||
style={styles.backLogo}
|
style={styles.backLogo}
|
||||||
overrideWhiteColor={COLORS.primary}
|
overrideWhiteColor={'#011dff'}
|
||||||
solidPaths={KLZ_PATHS}
|
pathOverrides={KLZ_PATHS.reduce((acc, idx) => ({ ...acc, [idx]: 'none' }), {})}
|
||||||
/>
|
/>
|
||||||
<View style={styles.accentLineHorizontal} />
|
<View style={styles.accentLineHorizontal} />
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,6 +19,8 @@ const CONFIG = {
|
|||||||
truckImage: path.join(process.cwd(), 'public/truck.png'),
|
truckImage: path.join(process.cwd(), 'public/truck.png'),
|
||||||
fontRegular: path.join(process.cwd(), 'public/fonts/Inter-Regular.ttf'),
|
fontRegular: path.join(process.cwd(), 'public/fonts/Inter-Regular.ttf'),
|
||||||
fontBold: path.join(process.cwd(), 'public/fonts/Inter-Bold.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[] = [
|
const PEOPLE: PersonData[] = [
|
||||||
@@ -71,39 +73,52 @@ END:VCARD`;
|
|||||||
});
|
});
|
||||||
|
|
||||||
const safeName = person.name.replace(/\s+/g, '_');
|
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)...`);
|
// 1. STANDARD DIGITAL PDF (RGB, no letterpress mask)
|
||||||
const buffer = await renderToBuffer(
|
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, {
|
React.createElement(PDFBusinessCard, {
|
||||||
person,
|
person,
|
||||||
qrCodeDataUrl,
|
qrCodeDataUrl,
|
||||||
variant: 'blue', // Explicitly use blue now
|
variant: 'blue',
|
||||||
|
isViaprinto: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
fs.writeFileSync(tempPath, buffer);
|
||||||
|
|
||||||
fs.writeFileSync(rgbPath, buffer);
|
// Convert fonts to outlines via ghostscript
|
||||||
|
const gsCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPath}" "${tempPath}"`;
|
||||||
console.log(`- Outlining fonts via Ghostscript (Keeping RGB)...`);
|
execSync(gsCommand, { stdio: 'inherit' });
|
||||||
const gsCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPath}" "${rgbPath}"`;
|
fs.unlinkSync(tempPath);
|
||||||
execSync(gsCommand, { stdio: 'ignore' });
|
|
||||||
|
|
||||||
fs.unlinkSync(rgbPath);
|
|
||||||
|
|
||||||
console.log(`✓ Final Print File generated (Fonts to Paths, RGB preserved): ${finalFileName}`);
|
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
|
// Convert to CMYK for Viaprinto
|
||||||
const finalFileNameCMYK = `KLZ_Visitenkarte_${safeName}_Viaprinto_CMYK_91x61mm_3mmBleed.pdf`;
|
const finalFileNameCMYK = `KLZ_Visitenkarte_${safeName}_Viaprinto_CMYK_91x61mm_3mmBleed.pdf`;
|
||||||
const finalPathCMYK = path.join(CONFIG.outputDir, finalFileNameCMYK);
|
const finalPathCMYK = path.join(CONFIG.outputDir, finalFileNameCMYK);
|
||||||
|
|
||||||
console.log(`- Converting to CMYK for Viaprinto...`);
|
console.log(`- Converting to CMYK for Viaprinto...`);
|
||||||
const cmykScript =
|
const cmykCommand = `bash "${CONFIG.ghostscriptScript}" "${tempViaprintoPath}" "${finalPathCMYK}"`;
|
||||||
'/Users/marcmintel/.gemini/config/plugins/mintel-ui-standards-plugin/skills/mintel-print-workflow/scripts/convert-cmyk.sh';
|
|
||||||
const cmykCommand = `bash "${cmykScript}" "${finalPath}" "${finalPathCMYK}"`;
|
|
||||||
try {
|
try {
|
||||||
execSync(cmykCommand, { stdio: 'ignore' });
|
execSync(cmykCommand, { stdio: 'inherit' });
|
||||||
|
fs.unlinkSync(tempViaprintoPath);
|
||||||
console.log(`✓ CMYK Print File generated: ${finalFileNameCMYK}`);
|
console.log(`✓ CMYK Print File generated: ${finalFileNameCMYK}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`❌ CMYK conversion failed! Is Ghostscript installed?`);
|
console.error(`❌ CMYK conversion failed! Is Ghostscript installed?`);
|
||||||
|
|||||||
Reference in New Issue
Block a user