diff --git a/lib/pdf-business-card.tsx b/lib/pdf-business-card.tsx new file mode 100644 index 00000000..8b837c55 --- /dev/null +++ b/lib/pdf-business-card.tsx @@ -0,0 +1,155 @@ +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, + }, +}); + +export interface PersonData { + name: string; + position: string; + phone: string; + email: string; + website: string; +} + +interface PDFBusinessCardProps { + person: PersonData; +} + +export const PDFBusinessCard: React.FC = ({ person }) => { + return ( + + {/* Front: Logo and Navy Background */} + + KLZ + + + + {/* Back: Contact Details */} + + + + + {person.name} + {person.position} + + + + + Phone + {person.phone} + + + Email + {person.email} + + + Web + {person.website} + + + + KLZ + + + ); +}; diff --git a/package.json b/package.json index 1ad99108..380b6985 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "check:spell": "cspell \"content/**/*.{md,mdx}\" \"app/**/*.tsx\" \"components/**/*.tsx\"", "pdf:datasheets": "tsx ./scripts/generate-pdf-datasheets.ts", "pdf:datasheets:legacy": "tsx ./scripts/generate-pdf-datasheets-pdf-lib.ts", + "pdf:business-cards": "tsx ./scripts/generate-business-cards.ts", "assets:push:testing": "bash ./scripts/assets-sync.sh local testing", "assets:push:staging": "bash ./scripts/assets-sync.sh local staging", "assets:push:prod": "bash ./scripts/assets-sync.sh local prod", diff --git a/scripts/generate-business-cards.ts b/scripts/generate-business-cards.ts new file mode 100644 index 00000000..927d47c6 --- /dev/null +++ b/scripts/generate-business-cards.ts @@ -0,0 +1,101 @@ +#!/usr/bin/env ts-node +/** + * Generate PDF Business Cards and convert to CMYK + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as React from 'react'; +import { renderToBuffer } from '@react-pdf/renderer'; +import { execSync } from 'child_process'; +import { PDFBusinessCard, PersonData } from '../lib/pdf-business-card'; + +const CONFIG = { + outputDir: path.join(process.cwd(), 'public/downloads/business-cards'), +}; + +const PEOPLE: PersonData[] = [ + { + name: 'Michael Bodemer', + position: 'Owner & Managing Director', + phone: '+49 151 462 467 98', + email: 'michael.bodemer@klz-cables.com', + website: 'www.klz-cables.com', + }, + { + name: 'Klaus Mintel', + position: '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' }); + console.log(`✓ CMYK Print File generated: ${path.basename(outputPath)}`); + } catch (err: any) { + console.error(`❌ Ghostscript CMYK conversion failed! Is ghostscript (gs) installed?`); + console.error(err.message); + } +} + +async function generateBusinessCards() { + await ensureOutputDir(); + + for (const person of PEOPLE) { + console.log(`\nGenerating Business Card for: ${person.name}`); + + const safeName = person.name.replace(/\s+/g, '_'); + const rgbFileName = `KLZ_BusinessCard_${safeName}_RGB.pdf`; + const cmykFileName = `KLZ_BusinessCard_${safeName}_CMYK_Print.pdf`; + + const rgbPath = path.join(CONFIG.outputDir, rgbFileName); + const cmykPath = path.join(CONFIG.outputDir, cmykFileName); + + console.log(`- Rendering React-PDF layout...`); + const buffer = await renderToBuffer( + React.createElement(PDFBusinessCard, { + person, + }), + ); + + fs.writeFileSync(rgbPath, buffer); + console.log(`✓ RGB Master generated: ${rgbFileName}`); + + convertToCMYK(rgbPath, cmykPath); + } +} + +async function main() { + console.log('--- KLZ Business Card Generator ---'); + const start = Date.now(); + + try { + await generateBusinessCards(); + console.log(`\n✅ Finished in ${((Date.now() - start) / 1000).toFixed(2)}s`); + } catch (error) { + console.error('\\n❌ Fatal error:', error); + process.exit(1); + } +} + +main().catch(console.error);