fix: match customer draft layout and correctly use PNG assets for QR Code and Logos
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 53s
Build & Deploy / 🏗️ Build (push) Successful in 1m59s
Build & Deploy / 🚀 Deploy (push) Successful in 14s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 53s
Build & Deploy / 🏗️ Build (push) Successful in 1m59s
Build & Deploy / 🚀 Deploy (push) Successful in 14s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
This commit is contained in:
@@ -1,154 +1,201 @@
|
||||
import * as React from 'react';
|
||||
import { Document, Page, View, Text, Image, StyleSheet, Font } from '@react-pdf/renderer';
|
||||
import * as path from 'path';
|
||||
|
||||
// Wir nutzen Standard-Helvetica, da das Projekt Helvetica für sauberen Tech-Look vorsieht.
|
||||
const styles = StyleSheet.create({
|
||||
// Size incl. 3mm bleed on all sides
|
||||
// 85x55 -> 91x61 -> 257.95 x 172.91 pt
|
||||
pageFront: {
|
||||
backgroundColor: '#001a4d', // KLZ Navy
|
||||
width: 257.95,
|
||||
height: 172.91,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
fontFamily: 'Helvetica',
|
||||
},
|
||||
pageBack: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
width: 257.95,
|
||||
height: 172.91,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
padding: 24,
|
||||
position: 'relative',
|
||||
fontFamily: 'Helvetica',
|
||||
},
|
||||
logo: {
|
||||
width: 100, // Logo breite
|
||||
},
|
||||
accentLine: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: 8,
|
||||
backgroundColor: '#82ed20', // KLZ Green
|
||||
},
|
||||
accentLineTop: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: 8,
|
||||
backgroundColor: '#82ed20', // KLZ Green
|
||||
},
|
||||
personInfo: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
name: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#000d26',
|
||||
letterSpacing: -0.5,
|
||||
marginBottom: 2,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
position: {
|
||||
fontSize: 8,
|
||||
color: '#82ed20', // Highlight im Text
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 1,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
contactGrid: {
|
||||
marginTop: 10,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
},
|
||||
contactRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
contactLabel: {
|
||||
width: 40,
|
||||
fontSize: 7,
|
||||
color: '#4b5563',
|
||||
fontWeight: 'bold',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
contactValue: {
|
||||
fontSize: 8,
|
||||
color: '#111827',
|
||||
},
|
||||
logoTextWhite: {
|
||||
fontSize: 32,
|
||||
fontWeight: 700,
|
||||
color: '#FFFFFF',
|
||||
letterSpacing: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
logoTextBlue: {
|
||||
position: 'absolute',
|
||||
bottom: 24,
|
||||
right: 24,
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: '#001a4d',
|
||||
letterSpacing: 2,
|
||||
opacity: 0.3,
|
||||
},
|
||||
});
|
||||
import React from 'react';
|
||||
import { Document, Page, Text, View, StyleSheet, Font, Image } from '@react-pdf/renderer';
|
||||
|
||||
export interface PersonData {
|
||||
name: string;
|
||||
position: string;
|
||||
role: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
website: string;
|
||||
}
|
||||
|
||||
interface PDFBusinessCardProps {
|
||||
person: PersonData;
|
||||
logoWhitePath: string;
|
||||
logoBluePath: string;
|
||||
logoFullPath: string;
|
||||
truckImagePath: string;
|
||||
qrCodeDataUrl: string;
|
||||
}
|
||||
|
||||
export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({ person }) => {
|
||||
const styles = StyleSheet.create({
|
||||
page: {
|
||||
width: '85mm',
|
||||
height: '55mm',
|
||||
backgroundColor: '#ffffff',
|
||||
position: 'relative',
|
||||
fontFamily: 'Helvetica',
|
||||
},
|
||||
pageBleed: {
|
||||
// 85x55mm + 3mm bleed on each side = 91x61mm
|
||||
width: '91mm',
|
||||
height: '61mm',
|
||||
backgroundColor: '#ffffff',
|
||||
position: 'relative',
|
||||
fontFamily: 'Helvetica',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
// FRONT PAGE
|
||||
frontContainer: {
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
padding: '8mm',
|
||||
},
|
||||
frontLogo: {
|
||||
position: 'absolute',
|
||||
top: '5mm',
|
||||
right: '5mm',
|
||||
width: '25mm',
|
||||
},
|
||||
truckImage: {
|
||||
position: 'absolute',
|
||||
bottom: '-2mm',
|
||||
left: '-5mm',
|
||||
width: '100mm', // cover bleed width
|
||||
height: '50mm',
|
||||
objectFit: 'contain',
|
||||
},
|
||||
// BACK PAGE
|
||||
backContainer: {
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
padding: '6mm 6mm',
|
||||
},
|
||||
backLogo: {
|
||||
width: '20mm',
|
||||
marginBottom: '8mm',
|
||||
},
|
||||
backMiddle: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '8mm',
|
||||
},
|
||||
nameSection: {
|
||||
flexDirection: 'column',
|
||||
width: '35%',
|
||||
},
|
||||
nameText: {
|
||||
fontSize: 16,
|
||||
fontWeight: 700,
|
||||
color: '#000000',
|
||||
marginBottom: 2,
|
||||
},
|
||||
roleText: {
|
||||
fontSize: 8,
|
||||
fontWeight: 400,
|
||||
color: '#333333',
|
||||
},
|
||||
contactSection: {
|
||||
flexDirection: 'column',
|
||||
width: '45%',
|
||||
paddingLeft: '2mm',
|
||||
},
|
||||
contactRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 3,
|
||||
},
|
||||
iconCircle: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: 6,
|
||||
backgroundColor: '#82ed20',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 4,
|
||||
},
|
||||
contactText: {
|
||||
fontSize: 6.5,
|
||||
fontWeight: 500,
|
||||
color: '#000000',
|
||||
},
|
||||
qrSection: {
|
||||
width: '20%',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
qrCode: {
|
||||
width: '18mm',
|
||||
height: '18mm',
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: '5mm',
|
||||
left: '6mm',
|
||||
right: '6mm',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
footerText: {
|
||||
fontSize: 6.5,
|
||||
fontWeight: 500,
|
||||
color: '#000000',
|
||||
},
|
||||
pipe: {
|
||||
fontSize: 6.5,
|
||||
fontWeight: 500,
|
||||
color: '#82ed20',
|
||||
marginHorizontal: 3,
|
||||
},
|
||||
});
|
||||
|
||||
export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
||||
person,
|
||||
logoFullPath,
|
||||
truckImagePath,
|
||||
qrCodeDataUrl,
|
||||
}) => {
|
||||
return (
|
||||
<Document>
|
||||
{/* Front: Logo and Navy Background */}
|
||||
<Page size={[257.95, 172.91]} style={styles.pageFront}>
|
||||
<Text style={styles.logoTextWhite}>KLZ</Text>
|
||||
<View style={styles.accentLine} />
|
||||
<Document
|
||||
title={`Business Card - ${person.name}`}
|
||||
author="KLZ Cables"
|
||||
creator="KLZ React-PDF Engine"
|
||||
>
|
||||
{/* FRONT - 91x61mm with Bleed */}
|
||||
<Page size={[258, 173]} style={styles.pageBleed}>
|
||||
<Image src={truckImagePath} style={styles.truckImage} />
|
||||
<Image src={logoFullPath} style={styles.frontLogo} />
|
||||
</Page>
|
||||
|
||||
{/* Back: Contact Details */}
|
||||
<Page size={[257.95, 172.91]} style={styles.pageBack}>
|
||||
<View style={styles.accentLineTop} />
|
||||
{/* BACK - 91x61mm with Bleed */}
|
||||
<Page size={[258, 173]} style={styles.pageBleed}>
|
||||
<View style={styles.backContainer}>
|
||||
<Image src={logoFullPath} style={styles.backLogo} />
|
||||
|
||||
<View style={styles.personInfo}>
|
||||
<Text style={styles.name}>{person.name}</Text>
|
||||
<Text style={styles.position}>{person.position}</Text>
|
||||
</View>
|
||||
<View style={styles.backMiddle}>
|
||||
<View style={styles.nameSection}>
|
||||
<Text style={styles.nameText}>{person.name}</Text>
|
||||
<Text style={styles.roleText}>{person.role}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.contactGrid}>
|
||||
<View style={styles.contactRow}>
|
||||
<Text style={styles.contactLabel}>Phone</Text>
|
||||
<Text style={styles.contactValue}>{person.phone}</Text>
|
||||
<View style={styles.contactSection}>
|
||||
<View style={styles.contactRow}>
|
||||
<View style={styles.iconCircle} />
|
||||
<Text style={styles.contactText}>{person.phone}</Text>
|
||||
</View>
|
||||
<View style={styles.contactRow}>
|
||||
<View style={styles.iconCircle} />
|
||||
<Text style={styles.contactText}>{person.email}</Text>
|
||||
</View>
|
||||
<View style={styles.contactRow}>
|
||||
<View style={styles.iconCircle} />
|
||||
<Text style={styles.contactText}>klz-cables.com</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.qrSection}>
|
||||
<Image src={qrCodeDataUrl} style={styles.qrCode} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.contactRow}>
|
||||
<Text style={styles.contactLabel}>Email</Text>
|
||||
<Text style={styles.contactValue}>{person.email}</Text>
|
||||
</View>
|
||||
<View style={styles.contactRow}>
|
||||
<Text style={styles.contactLabel}>Web</Text>
|
||||
<Text style={styles.contactValue}>{person.website}</Text>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.footerText}>KLZ Vertriebs GmbH</Text>
|
||||
<Text style={styles.pipe}>|</Text>
|
||||
<Text style={styles.footerText}>Raiffeisenstraße 22</Text>
|
||||
<Text style={styles.pipe}>|</Text>
|
||||
<Text style={styles.footerText}>73630 Remshalden</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={styles.logoTextBlue}>KLZ</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pino": "^10.3.0",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-email": "^5.2.5",
|
||||
@@ -56,6 +57,7 @@
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^22.19.3",
|
||||
"@types/nodemailer": "^7.0.5",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/sharp": "^0.31.1",
|
||||
|
||||
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
@@ -84,6 +84,9 @@ importers:
|
||||
pino-pretty:
|
||||
specifier: ^13.1.3
|
||||
version: 13.1.3
|
||||
qrcode:
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
react:
|
||||
specifier: ^19.2.4
|
||||
version: 19.2.4
|
||||
@@ -163,6 +166,9 @@ importers:
|
||||
'@types/nodemailer':
|
||||
specifier: ^7.0.5
|
||||
version: 7.0.11
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.6
|
||||
version: 1.5.6
|
||||
'@types/react':
|
||||
specifier: ^19.2.7
|
||||
version: 19.2.14
|
||||
@@ -2801,6 +2807,9 @@ packages:
|
||||
'@types/pg@8.15.6':
|
||||
resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
|
||||
|
||||
'@types/react-dom@19.2.3':
|
||||
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
|
||||
peerDependencies:
|
||||
@@ -3990,6 +3999,9 @@ packages:
|
||||
dfa@1.2.0:
|
||||
resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==}
|
||||
|
||||
dijkstrajs@1.0.3:
|
||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||
|
||||
doctrine@2.1.0:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -6167,6 +6179,10 @@ packages:
|
||||
png-js@1.0.0:
|
||||
resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==}
|
||||
|
||||
pngjs@5.0.0:
|
||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
po-parser@2.1.1:
|
||||
resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==}
|
||||
|
||||
@@ -6286,6 +6302,11 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
qrcode@1.5.4:
|
||||
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
qs@6.14.2:
|
||||
resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -6964,8 +6985,8 @@ packages:
|
||||
third-party-web@0.26.7:
|
||||
resolution: {integrity: sha512-buUzX4sXC4efFX6xg2bw6/eZsCUh8qQwSavC4D9HpONMFlRbcHhD8Je5qwYdCpViR6q0qla2wPP+t91a2vgolg==}
|
||||
|
||||
third-party-web@0.29.0:
|
||||
resolution: {integrity: sha512-nBDSJw5B7Sl1YfsATG2XkW5qgUPODbJhXw++BKygi9w6O/NKS98/uY/nR/DxDq2axEjL6halHW1v+jhm/j1DBQ==}
|
||||
third-party-web@0.29.2:
|
||||
resolution: {integrity: sha512-fegtha91tq2DHphyoiBXVHjVi2YG9zFaRnboT9C28tO1en9Y3wJsfspuy40F+u5wl3hHVbw7cnd1b67kEGHb8g==}
|
||||
|
||||
thread-stream@4.0.0:
|
||||
resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==}
|
||||
@@ -9121,7 +9142,7 @@ snapshots:
|
||||
'@paulirish/trace_engine@0.0.53':
|
||||
dependencies:
|
||||
legacy-javascript: 0.0.1
|
||||
third-party-web: 0.29.0
|
||||
third-party-web: 0.29.2
|
||||
|
||||
'@pdf-lib/standard-fonts@1.0.0':
|
||||
dependencies:
|
||||
@@ -10152,6 +10173,10 @@ snapshots:
|
||||
pg-protocol: 1.13.0
|
||||
pg-types: 2.2.0
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
|
||||
'@types/react-dom@19.2.3(@types/react@19.2.14)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.14
|
||||
@@ -11442,6 +11467,8 @@ snapshots:
|
||||
|
||||
dfa@1.2.0: {}
|
||||
|
||||
dijkstrajs@1.0.3: {}
|
||||
|
||||
doctrine@2.1.0:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
@@ -14186,6 +14213,8 @@ snapshots:
|
||||
|
||||
png-js@1.0.0: {}
|
||||
|
||||
pngjs@5.0.0: {}
|
||||
|
||||
po-parser@2.1.1: {}
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
@@ -14317,6 +14346,12 @@ snapshots:
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
qrcode@1.5.4:
|
||||
dependencies:
|
||||
dijkstrajs: 1.0.3
|
||||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
qs@6.14.2:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
@@ -15196,7 +15231,7 @@ snapshots:
|
||||
|
||||
third-party-web@0.26.7: {}
|
||||
|
||||
third-party-web@0.29.0: {}
|
||||
third-party-web@0.29.2: {}
|
||||
|
||||
thread-stream@4.0.0:
|
||||
dependencies:
|
||||
|
||||
BIN
public/logo-blue.png
Normal file
BIN
public/logo-blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
BIN
public/logo-white.png
Normal file
BIN
public/logo-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/truck.png
Normal file
BIN
public/truck.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
@@ -9,45 +9,34 @@ import * as React from 'react';
|
||||
import { renderToBuffer } from '@react-pdf/renderer';
|
||||
import { execSync } from 'child_process';
|
||||
import { PDFBusinessCard, PersonData } from '../lib/pdf-business-card';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
const CONFIG = {
|
||||
outputDir: path.join(process.cwd(), 'public/downloads/business-cards'),
|
||||
logoWhite: path.join(process.cwd(), 'public/logo-white.png'),
|
||||
logoBlue: path.join(process.cwd(), 'public/logo-blue.png'),
|
||||
logoFull: path.join(process.cwd(), 'public/logo-full.png'),
|
||||
truckImage: path.join(process.cwd(), 'public/truck.png'),
|
||||
};
|
||||
|
||||
const PEOPLE: PersonData[] = [
|
||||
{
|
||||
name: 'Michael Bodemer',
|
||||
position: 'Owner & Managing Director',
|
||||
phone: '+49 151 462 467 98',
|
||||
role: 'Managing Director',
|
||||
phone: '+49 171 785 2420',
|
||||
email: 'michael.bodemer@klz-cables.com',
|
||||
website: 'www.klz-cables.com',
|
||||
},
|
||||
{
|
||||
name: 'Klaus Mintel',
|
||||
position: 'Managing Director',
|
||||
role: 'Managing Director',
|
||||
phone: '+49 151 1775 2873',
|
||||
email: 'klaus.mintel@klz-cables.com',
|
||||
website: 'www.klz-cables.com',
|
||||
},
|
||||
];
|
||||
|
||||
async function ensureOutputDir() {
|
||||
if (!fs.existsSync(CONFIG.outputDir)) {
|
||||
fs.mkdirSync(CONFIG.outputDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert ein RGB PDF zu CMYK via Ghostscript.
|
||||
*/
|
||||
function convertToCMYK(inputPath: string, outputPath: string) {
|
||||
console.log(`- Converting to CMYK via Ghostscript...`);
|
||||
try {
|
||||
// Basic CMYK conversion via Ghostscript.
|
||||
// -dSAFER -dBATCH -dNOPAUSE
|
||||
// -sDEVICE=pdfwrite
|
||||
// -sColorConversionStrategy=CMYK
|
||||
// -dProcessColorModel=/DeviceCMYK
|
||||
const gsCommand = `gs -dSAFER -dBATCH -dNOPAUSE -dNOCACHE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode -sOutputFile="${outputPath}" "${inputPath}"`;
|
||||
|
||||
execSync(gsCommand, { stdio: 'pipe' });
|
||||
@@ -59,11 +48,33 @@ function convertToCMYK(inputPath: string, outputPath: string) {
|
||||
}
|
||||
|
||||
async function generateBusinessCards() {
|
||||
await ensureOutputDir();
|
||||
if (!fs.existsSync(CONFIG.outputDir)) {
|
||||
fs.mkdirSync(CONFIG.outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (const person of PEOPLE) {
|
||||
console.log(`\nGenerating Business Card for: ${person.name}`);
|
||||
|
||||
// Generate VCard QR Code
|
||||
const lastName = person.name.split(' ').slice(1).join(' ');
|
||||
const firstName = person.name.split(' ')[0];
|
||||
const vcard = `BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:${lastName};${firstName};;;
|
||||
FN:${person.name}
|
||||
ORG:KLZ Vertriebs GmbH
|
||||
TITLE:${person.role}
|
||||
TEL;TYPE=WORK,VOICE:${person.phone}
|
||||
EMAIL;TYPE=PREF,INTERNET:${person.email}
|
||||
URL:https://klz-cables.com
|
||||
END:VCARD`;
|
||||
|
||||
const qrCodeDataUrl = await QRCode.toDataURL(vcard, {
|
||||
width: 400,
|
||||
margin: 0,
|
||||
color: { dark: '#000000', light: '#ffffff' },
|
||||
});
|
||||
|
||||
const safeName = person.name.replace(/\s+/g, '_');
|
||||
const rgbFileName = `KLZ_BusinessCard_${safeName}_RGB.pdf`;
|
||||
const cmykFileName = `KLZ_BusinessCard_${safeName}_CMYK_Print.pdf`;
|
||||
@@ -75,6 +86,11 @@ async function generateBusinessCards() {
|
||||
const buffer = await renderToBuffer(
|
||||
React.createElement(PDFBusinessCard, {
|
||||
person,
|
||||
logoWhitePath: CONFIG.logoWhite,
|
||||
logoBluePath: CONFIG.logoBlue,
|
||||
logoFullPath: CONFIG.logoFull,
|
||||
truckImagePath: CONFIG.truckImage,
|
||||
qrCodeDataUrl,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -93,7 +109,7 @@ async function main() {
|
||||
await generateBusinessCards();
|
||||
console.log(`\n✅ Finished in ${((Date.now() - start) / 1000).toFixed(2)}s`);
|
||||
} catch (error) {
|
||||
console.error('\\n❌ Fatal error:', error);
|
||||
console.error('\n❌ Fatal error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user