Compare commits

...

46 Commits

Author SHA1 Message Date
9b251101d4 chore(release): 2.3.24 - deploy AVB fix
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 14s
Build & Deploy / 🧪 QA (push) Successful in 2m2s
Build & Deploy / 🏗️ Build (push) Successful in 3m49s
Build & Deploy / 🚀 Deploy (push) Successful in 19s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m26s
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 12:11:45 +02:00
ff0d880bde style: shift text block upwards on white page for more distance to address
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 13s
Build & Deploy / 🧪 QA (push) Successful in 1m41s
Build & Deploy / 🏗️ Build (push) Successful in 2m44s
Build & Deploy / 🚀 Deploy (push) Successful in 19s
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
2026-06-12 12:05:05 +02:00
b78a848ec2 fix: ensure email addresses are strictly lowercase
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 1m59s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-12 12:01:00 +02:00
e72879b222 style: change primary blue to website dark blue and set white page logo to black
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 24s
Build & Deploy / 🧪 QA (push) Successful in 1m38s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
2026-06-12 11:57:48 +02:00
5a2b0c543e style: remove truck illustration completely and increase bottom margin for print safety
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Successful in 1m41s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-12 11:52:48 +02:00
1203c20166 fix: remove unintended truck background from the white contact page
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 16s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-12 11:51:53 +02:00
9944d3313b fix: correct background color of second page back to white
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 26s
Build & Deploy / 🧪 QA (push) Failing after 1m56s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 9s
2026-06-12 11:48:20 +02:00
d1cbfc41c8 feat: add crop marks variant for print finishing
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 24s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-12 11:47:40 +02:00
09d435a1bc fix: align address footer flush left with contact icon column
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 21s
Build & Deploy / 🧪 QA (push) Failing after 1m52s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 12s
2026-06-12 11:35:44 +02:00
8f34ac2a6d style: fine-tune white page typography - harmonize font sizes, spacing rhythm and letter-spacing
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 28s
Build & Deploy / 🧪 QA (push) Failing after 1m54s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 12s
2026-06-12 11:32:44 +02:00
e5ecf16a66 fix: revert line spacing to compact layout, keep generous logo margin
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 22s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
2026-06-12 11:30:39 +02:00
12eadba0ee fix: resolve PDF business card layout overlap and aspect ratio distortion
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 22s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-12 11:29:34 +02:00
0ad975f049 style: crisp white icon labels (remove opacity), Bodemer role updated, tagline lowercase
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 24s
Build & Deploy / 🧪 QA (push) Failing after 1m54s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 13s
2026-06-12 11:09:19 +02:00
b53ea82be8 fix: make brand text and icons pure white on blue background for better contrast, fix white page layout 2026-06-12 11:04:50 +02:00
3d7a061858 style: remove hard dark blue footer bar, add elegant green separator line
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Failing after 1m52s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 12s
2026-06-12 10:49:04 +02:00
44ad62de1c style: increase bottom bar height to 16mm to give the tagline more breathing room
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 12s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-12 10:47:12 +02:00
092325baa9 style: redesign blue page - compact 14mm bar, refined typography proportions, premium negative space
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m20s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 10:41:19 +02:00
a0910dd932 style: optimize print design harmony for blue page
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m22s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 10:38:37 +02:00
db251687d1 fix: resolve address footer overlap and restore 4mm symmetrical margins
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m22s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 10:36:19 +02:00
eaf17315ff fix: implement strict print grid and optical centering for business card
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 1m21s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 10:30:55 +02:00
6bab5c9173 revert 2026-06-12 10:29:05 +02:00
cf371e48ef fix(klz-2026): remove bottom margins from AVB components to fix spacing issues
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 1m24s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 10:25:21 +02:00
ec0c71a6a6 feat: add NoTruck variant, center layout padding, match green colors
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 1m20s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-12 10:21:56 +02:00
1b7fecff4a fix(klz-2026): add missing agbHistory and pdfDownload components to MDXContent to fix AVB page, remove invalid clipRule from TruckBlueprint
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 13s
Build & Deploy / 🧪 QA (push) Successful in 1m20s
Build & Deploy / 🏗️ Build (push) Successful in 2m40s
Build & Deploy / 🚀 Deploy (push) Successful in 18s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m25s
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 09:49:07 +02:00
b7f2d932a9 fix: resolve address footer page break and make QR border green
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m20s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-12 09:44:51 +02:00
aa3b0b05b8 feat: align QR code to blue line, add border, remove label
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 15s
Build & Deploy / 🧪 QA (push) Failing after 1m44s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 23:21:40 +02:00
4c6e8cb28b fix: resolve background fill and text colors on both business card sides 2026-06-11 23:20:03 +02:00
175cb10e01 fix(pdf): make KLZ paths solid to prevent see-through, add dimensions to viaprinto filename
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m20s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-11 19:17:37 +02:00
5e8a175a53 feat(pdf): name KLZ_PATHS and use visible blue fill for letterpress mask layer
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 16s
Build & Deploy / 🧪 QA (push) Failing after 1m21s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 19:15:32 +02:00
9f64304539 fix(pdf): include missing Z piece (path 29) in letterpress mask
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 16s
Build & Deploy / 🧪 QA (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
2026-06-11 19:14:22 +02:00
6367efdf6f fix(pdf): fix white-on-white logo and correct letterpress masking
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-11 19:13:32 +02:00
22ae008f29 fix(pdf): exact indices for KLZ letterpress and define COLORS.blue
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m20s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-11 18:55:05 +02:00
118319ceab fix(pdf): correct front/back colors, truck opacity 0.25, add letterpress marker layer
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m21s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 18:50:54 +02:00
5d94337df1 fix(pdf): render truck as vector, fix back logo coloring
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m21s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 18:44:27 +02:00
3798d37fcd fix: enforce Mintel blue on back logo, increase address font size, update contact data
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Failing after 1m24s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 18:40:33 +02:00
efe3cbc58e feat: switch default variant back to blue, maintain CMYK output for Viaprinto
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 15s
Build & Deploy / 🧪 QA (push) Successful in 1m32s
Build & Deploy / 🏗️ Build (push) Successful in 2m40s
Build & Deploy / 🚀 Deploy (push) Successful in 18s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 18:21:16 +02:00
f798afe917 feat: generate only white variant by default and automatically build CMYK version for Viaprinto
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-11 18:20:29 +02:00
1623e04fce chore: commit newly generated business card PDFs with final layout
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 14s
Build & Deploy / 🧪 QA (push) Successful in 1m21s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
2026-06-11 18:17:32 +02:00
b77eb2087e style: remove address footer top border and add explicit bottom margin
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 25s
Build & Deploy / 🧪 QA (push) Successful in 1m27s
Build & Deploy / 🏗️ Build (push) Successful in 2m45s
Build & Deploy / 🚀 Deploy (push) Successful in 21s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-06-11 18:07:05 +02:00
7e7acff670 style: fix overall vertical spacing, increase bottom bar height for icon breathing room
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-06-11 18:05:22 +02:00
069400dd86 fix: register Inter woff font for curve outlining, fix tagline spacing
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 24s
Build & Deploy / 🧪 QA (push) Successful in 1m23s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-11 18:02:44 +02:00
91f5cb8de3 fix: resolve layout overflow and overlapping footer
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 13s
Build & Deploy / 🧪 QA (push) Successful in 1m22s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-11 17:58:37 +02:00
702313e42c style: sleek full-width address footer and smaller QR code
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 17s
Build & Deploy / 🧪 QA (push) Successful in 1m48s
Build & Deploy / 🏗️ Build (push) Successful in 2m59s
Build & Deploy / 🚀 Deploy (push) Successful in 21s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-06-11 17:07:33 +02:00
adafe8ac02 fix: add company address to business card layout
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 14s
Build & Deploy / 🧪 QA (push) Successful in 1m21s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-11 17:04:54 +02:00
3599880f3a fix: format phone number and adjust role font size to prevent wrapping
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 12s
Build & Deploy / 🧪 QA (push) Successful in 1m24s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-06-11 17:03:05 +02:00
79f0231e26 feat: update data for Johannes Gleich business card
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 13s
Build & Deploy / 🧪 QA (push) Successful in 1m23s
Build & Deploy / 🏗️ Build (push) Successful in 2m59s
Build & Deploy / 🚀 Deploy (push) Successful in 21s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-11 15:57:12 +02:00
44 changed files with 10433 additions and 184 deletions

View File

@@ -22,6 +22,8 @@ import MeetTheTeam from './home/MeetTheTeam';
import GallerySection from './home/GallerySection';
import VideoSection from './home/VideoSection';
import CTA from './home/CTA';
import { AgbHistoryBlock } from './AgbHistoryBlock';
import { PDFDownloadBlock } from './PDFDownloadBlock';
function ContactSection(props: any) {
return (
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
@@ -94,6 +96,10 @@ async function Block(props: any) {
return <VideoSection data={data} />;
case 'homeCTA':
return <CTA data={data} />;
case 'agbHistory':
return <AgbHistoryBlock {...data} />;
case 'pdfDownload':
return <PDFDownloadBlock {...data} />;
default:
return (

9843
lib/TruckBlueprint.tsx Normal file

File diff suppressed because one or more lines are too long

View File

@@ -16,6 +16,24 @@ import {
Circle,
} from '@react-pdf/renderer';
import * as path from 'path';
import { TruckBlueprint } from './TruckBlueprint';
// 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;
@@ -28,11 +46,17 @@ export interface PDFBusinessCardProps {
person: PersonData;
qrCodeDataUrl: string;
variant?: 'blue' | 'white' | 'black';
isViaprinto?: boolean;
withTruck?: boolean;
withCropMarks?: boolean;
}
// Mintel Aesthetics Colors - EXACT PROD COLORS
// Die Buchstaben K, L, Z im SVG (inklusive dem kleinen Stück vom Z oben links)
export const KLZ_PATHS = [25, 26, 27, 28, 29];
const COLORS = {
primary: '#011dff', // Brand Royal Blue
primary: '#000a66', // Brand Dark Blue (from website)
blue: '#000a66',
green: '#82ed20', // Brand Accent Green
darkGreen: '#4A9E00', // Darker green for white background visibility
white: '#ffffff',
@@ -42,13 +66,105 @@ const COLORS = {
lightGray: '#f8f9fa',
};
const CropMarks = () => (
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 9999 }}>
{/* Top Left */}
<View
style={{
position: 'absolute',
left: '13mm',
top: '0mm',
width: '0.25pt',
height: '10mm',
backgroundColor: '#000',
}}
/>
<View
style={{
position: 'absolute',
left: '0mm',
top: '13mm',
width: '10mm',
height: '0.25pt',
backgroundColor: '#000',
}}
/>
{/* Top Right */}
<View
style={{
position: 'absolute',
right: '13mm',
top: '0mm',
width: '0.25pt',
height: '10mm',
backgroundColor: '#000',
}}
/>
<View
style={{
position: 'absolute',
right: '0mm',
top: '13mm',
width: '10mm',
height: '0.25pt',
backgroundColor: '#000',
}}
/>
{/* Bottom Left */}
<View
style={{
position: 'absolute',
left: '13mm',
bottom: '0mm',
width: '0.25pt',
height: '10mm',
backgroundColor: '#000',
}}
/>
<View
style={{
position: 'absolute',
left: '0mm',
bottom: '13mm',
width: '10mm',
height: '0.25pt',
backgroundColor: '#000',
}}
/>
{/* Bottom Right */}
<View
style={{
position: 'absolute',
right: '13mm',
bottom: '0mm',
width: '0.25pt',
height: '10mm',
backgroundColor: '#000',
}}
/>
<View
style={{
position: 'absolute',
right: '0mm',
bottom: '13mm',
width: '10mm',
height: '0.25pt',
backgroundColor: '#000',
}}
/>
</View>
);
const styles = StyleSheet.create({
pageBleed: {
width: '91mm',
height: '61mm',
backgroundColor: COLORS.primary,
position: 'relative',
fontFamily: 'Helvetica',
fontFamily: 'Inter',
overflow: 'hidden',
},
pageBleedBack: {
@@ -56,7 +172,7 @@ const styles = StyleSheet.create({
height: '61mm',
backgroundColor: COLORS.white,
position: 'relative',
fontFamily: 'Helvetica',
fontFamily: 'Inter',
overflow: 'hidden',
},
@@ -66,100 +182,97 @@ const styles = StyleSheet.create({
top: 0,
left: 0,
right: 0,
bottom: '16mm', // Leave space for the bar below
bottom: '20mm', // Truck stops above the footer area to avoid text conflict
zIndex: 1,
},
frontBackgroundImage: {
width: '100%',
height: '100%',
objectFit: 'contain',
objectPositionX: 'center',
objectPositionY: 'center',
opacity: 0.4, // Less transparent, more contrast
},
frontContainer: {
flex: 1,
paddingBottom: '16mm', // Push the logo up to center it in the area above the bar
paddingTop: '3mm', // Compensate for top bleed for perfect optical centering
paddingBottom: '20mm', // Leave space for the footer area
justifyContent: 'center',
alignItems: 'center',
},
frontLogo: {
width: 175,
width: 145, // Reduced for a more elegant, premium look with more negative space
zIndex: 100,
},
frontTagline: {
color: COLORS.white,
fontSize: 6,
marginTop: 10,
letterSpacing: 1.5,
fontSize: 5.5,
marginTop: 8,
letterSpacing: 2,
fontWeight: 'bold',
textTransform: 'uppercase',
zIndex: 100,
},
frontGreenBarBottom: {
frontFooterArea: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '16mm',
borderTop: `2pt solid ${COLORS.green}`,
backgroundColor: '#000a66',
height: '20mm', // Generous area for the footer
paddingBottom: '7mm', // 3mm bleed + 4mm safe margin
zIndex: 50,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: 6, // Increased gap for more spacing under tagline
gap: 6, // Harmonious spacing to icons
},
frontIconsGroup: {
flexDirection: 'row',
gap: 12,
gap: 14, // Wider gaps between icon groups for visual clarity at small size
},
frontBrandIconWrapper: {
alignItems: 'center',
gap: 2,
gap: 1.5,
},
frontBrandIcon: {
width: 18,
height: 18,
opacity: 0.9,
width: 12, // Smaller for compact bar
height: 12,
opacity: 0.85,
},
frontBrandText: {
fontSize: 5.5,
fontSize: 4, // Tiny but readable at 300dpi print
color: COLORS.white,
textTransform: 'uppercase',
letterSpacing: 0.5,
opacity: 0.9,
letterSpacing: 0.6,
// Full opacity — white on blue needs to pop cleanly
},
frontTaglineBar: {
color: COLORS.green,
fontSize: 7,
letterSpacing: 1.5,
fontSize: 7, // Larger — as requested
letterSpacing: 0.5, // Harmonious tracking for mixed case
fontWeight: 'bold',
textTransform: 'uppercase',
},
// -- WHITE PAGE (Vorderseite) --
backContainer: {
flex: 1,
padding: '8mm',
paddingTop: '3mm', // Reduced significantly to pull content up
paddingLeft: '6mm',
paddingRight: '6mm',
flexDirection: 'column',
justifyContent: 'space-between',
},
backHeader: {
marginBottom: 'auto',
flexDirection: 'row',
alignItems: 'center',
marginBottom: '5mm', // Generous breathing room
width: '100%',
},
backLogo: {
width: 70,
width: 60,
},
accentLineHorizontal: {
flex: 1,
height: 1.5,
backgroundColor: COLORS.primary, // Brand blue line
marginLeft: 15,
marginRight: 0,
marginLeft: 12,
marginRight: 12,
},
accentDot: {
width: 4,
@@ -178,41 +291,41 @@ const styles = StyleSheet.create({
top: -5,
},
backBody: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-end',
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'flex-start',
width: '100%',
},
leftColumn: {
flex: 1,
width: '100%', // Take full width since right column is removed
flexDirection: 'column',
paddingRight: '4mm',
paddingBottom: '2mm',
},
nameSection: {
marginBottom: '5mm',
marginBottom: '2.5mm', // Clear separation between name block and contact block
},
nameText: {
fontSize: 16,
fontSize: 13,
fontWeight: 'bold',
color: COLORS.neutralDark,
marginBottom: 2,
letterSpacing: -0.2,
marginBottom: 1.5,
letterSpacing: -0.15,
},
roleText: {
fontSize: 9,
fontSize: 6.5,
fontWeight: 'bold',
color: COLORS.primary,
letterSpacing: 0.5,
color: COLORS.darkGreen,
letterSpacing: 1.2,
textTransform: 'uppercase',
},
contactSection: {
flexDirection: 'column',
gap: 5,
gap: 0,
},
contactRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 5, // Even rhythm between contact lines
},
iconWrapper: {
width: 12,
@@ -220,35 +333,48 @@ const styles = StyleSheet.create({
alignItems: 'flex-start',
},
contactText: {
fontSize: 9,
fontSize: 7.5,
color: COLORS.neutralDark,
fontWeight: 'normal',
paddingTop: 1,
paddingTop: 0.5,
letterSpacing: 0.1,
},
rightColumn: {
flexDirection: 'column',
alignItems: 'flex-end',
justifyContent: 'flex-end',
justifyContent: 'flex-start',
},
// Ultra Minimalist QR Area
qrContainer: {
flexDirection: 'column',
alignItems: 'center',
gap: 4,
gap: 3,
},
qrCodeWrapper: {
width: '15mm',
height: '15mm',
width: '12mm',
height: '12mm',
border: `0.75pt solid ${COLORS.darkGreen}`,
padding: 2.5,
backgroundColor: '#ffffff',
},
qrCode: {
width: '100%',
height: '100%',
},
qrLabel: {
fontSize: 5,
color: COLORS.neutralDark, // Subtle grey instead of loud blue
fontWeight: 'bold',
letterSpacing: 1.2,
addressFooter: {
width: '100%',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
position: 'absolute',
bottom: '7mm', // 3mm bleed + 4mm safe margin
left: '6mm', // Match backContainer paddingLeft for flush alignment
},
addressFooterText: {
fontSize: 6.5,
color: COLORS.grayText,
letterSpacing: 0.4,
fontWeight: 'normal',
},
});
@@ -280,6 +406,15 @@ const GlobeIcon = () => (
</Svg>
);
const LocationIcon = () => (
<Svg viewBox="0 0 24 24" width={9} height={9}>
<Path
d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"
fill="#000000"
/>
</Svg>
);
const ScanIcon = () => (
<Svg viewBox="0 0 24 24" width={9} height={9}>
<Path
@@ -315,12 +450,51 @@ const parseSvgPaths = (filePath: string) => {
}
};
const VectorLogo = ({ paths, style }: { paths: { d: string; fill: string }[]; style: any }) => (
const VectorLogo = ({
paths,
style,
overrideColor,
overrideWhiteColor,
pathOverrides,
solidPaths,
solidBackgroundColor = '#000a66',
}: {
paths: { d: string; fill: string }[];
style: any;
overrideColor?: string;
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) => (
<Path key={i} d={p.d} fill={p.fill} />
))}
{/* 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];
} else if (
overrideWhiteColor &&
(p.fill === 'white' || p.fill === '#ffffff' || p.fill === '#fff')
) {
fill = overrideWhiteColor;
}
return <Path key={i} d={d} fill={fill} />;
})}
</G>
</Svg>
);
@@ -550,7 +724,7 @@ export const IconHighVoltage = ({ style, color = 'white' }: { style: any; color?
style={{ fill: 'none', stroke: color, strokeWidth: '0.33px' }}
/>
<Path
d="M28.597,16.426c-0,-1.03 -0.837,-1.867 -1.868,-1.867c-1.03,0 -1.867,0.837 -1.867,1.867c0,1.031 0.837,1.868 1.867,1.868c1.031,-0 1.868,-0.837 1.868,-1.868Z"
d="M28.597,16.426c-0,-1.03 -0.837,-1.868 -1.868,-1.867c-1.03,0 -1.867,0.837 -1.867,1.867c0,1.031 0.837,1.868 1.867,1.868c1.031,-0 1.868,-0.837 1.868,-1.868Z"
style={{ fill: 'none', stroke: color, strokeWidth: '0.33px' }}
/>
<Path
@@ -610,6 +784,9 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
person,
qrCodeDataUrl,
variant = 'blue',
isViaprinto = false,
withTruck = true,
withCropMarks = false,
}) => {
const isWhiteVariant = variant === 'white' || variant === 'black';
@@ -637,10 +814,34 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
return logoWhitePaths;
};
const getBackLogoPaths = () => {
return logoBlackWithTaglinePaths;
};
const bgImagePath = isWhiteVariant
? path.join(process.cwd(), 'public', 'media', 'cable_drums_truck.png')
: path.join(process.cwd(), 'public', 'cable_drums_bg.png');
const renderPageWrapper = (children: React.ReactNode, bleedStyle: any) => {
if (withCropMarks) {
return (
<Page size={[315, 230]} style={{ backgroundColor: '#ffffff' }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<CropMarks />
<View style={[bleedStyle, { overflow: 'hidden', position: 'relative' }]}>
{children}
</View>
</View>
</Page>
);
}
return (
<Page size={[258, 173]} style={bleedStyle}>
{children}
</Page>
);
};
return (
<Document
title={`Business Card - ${person.name}`}
@@ -648,75 +849,90 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
creator="KLZ Print Engine"
>
{/* VORDERSEITE (Blue page with logo, truck and tagline) */}
<Page
size={[258, 173]}
style={[styles.pageBleed, isWhiteVariant && { backgroundColor: '#ffffff' }]}
>
<View style={styles.truckWrapper}>
<Image
src={bgImagePath}
style={[styles.frontBackgroundImage, isWhiteVariant && { opacity: 0.2 }]}
/>
</View>
<View
style={[
styles.frontGreenBarBottom,
isWhiteVariant && { backgroundColor: '#ffffff', borderTopColor: COLORS.darkGreen },
]}
>
<Text style={[styles.frontTaglineBar, isWhiteVariant && { color: COLORS.black }]}>
VON 0,6/1,0 KV BIS 64/110 KV
</Text>
<View style={styles.frontIconsGroup}>
<View style={styles.frontBrandIconWrapper}>
<IconLowVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Niederspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconMediumVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Mittelspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconHighVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Hochspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconSolar
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Solar
</Text>
{renderPageWrapper(
<>
<View style={styles.frontFooterArea}>
<Text style={[styles.frontTaglineBar, isWhiteVariant && { color: COLORS.black }]}>
Von 0,6/1,0 kV bis 64/110 kV
</Text>
<View style={styles.frontIconsGroup}>
<View style={styles.frontBrandIconWrapper}>
<IconLowVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Niederspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconMediumVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Mittelspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconHighVoltage
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Hochspannung
</Text>
</View>
<View style={styles.frontBrandIconWrapper}>
<IconSolar
style={styles.frontBrandIcon}
color={isWhiteVariant ? COLORS.black : 'white'}
/>
<Text style={[styles.frontBrandText, isWhiteVariant && { color: COLORS.black }]}>
Solar
</Text>
</View>
</View>
</View>
</View>
<View style={styles.frontContainer}>
<VectorLogo paths={getFrontLogoPaths()} style={styles.frontLogo} />
</View>
</Page>
<View style={styles.frontContainer}>
{/* Base Logo: text is white, truck is green. Fill KLZ background to hide truck, keep outline white */}
<VectorLogo
paths={getFrontLogoPaths()}
style={styles.frontLogo}
solidPaths={KLZ_PATHS}
solidBackgroundColor={isWhiteVariant ? '#ffffff' : COLORS.primary}
/>
{/* RÜCKSEITE (White page with contact info) */}
<Page size={[258, 173]} style={styles.pageBleedBack}>
{/* Letterpress Mask Layer: We use #ff00fe as a marker color. Only the "KLZ" letters! */}
{isViaprinto && (
<VectorLogo
paths={getFrontLogoPaths().filter((_, i) => KLZ_PATHS.includes(i))}
style={[styles.frontLogo, { position: 'absolute' }]}
overrideColor="#ff00fe"
solidPaths={KLZ_PATHS.map((_, i) => i)}
/>
)}
</View>
</>,
[styles.pageBleed, isWhiteVariant && { backgroundColor: '#ffffff' }],
)}
{renderPageWrapper(
<View style={styles.backContainer}>
<View style={styles.backHeader}>
<VectorLogo paths={logoBluePaths} style={styles.backLogo} />
<VectorLogo
paths={getBackLogoPaths()}
style={styles.backLogo}
overrideWhiteColor={COLORS.black}
pathOverrides={KLZ_PATHS.reduce((acc, idx) => ({ ...acc, [idx]: 'none' }), {})}
/>
<View style={styles.accentLineHorizontal} />
<View style={styles.qrContainer}>
<View style={[styles.qrCodeWrapper, { borderColor: COLORS.darkGreen }]}>
<Image src={qrCodeDataUrl} style={styles.qrCode} />
</View>
</View>
</View>
<View style={styles.backBody}>
@@ -737,7 +953,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
<View style={styles.iconWrapper}>
<EmailIcon />
</View>
<Text style={styles.contactText}>{person.email}</Text>
<Text style={styles.contactText}>{person.email.toLowerCase()}</Text>
</View>
<View style={styles.contactRow}>
<View style={styles.iconWrapper}>
@@ -747,18 +963,16 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
</View>
</View>
</View>
<View style={styles.rightColumn}>
<View style={styles.qrContainer}>
<View style={styles.qrCodeWrapper}>
<Image src={qrCodeDataUrl} style={styles.qrCode} />
</View>
<Text style={styles.qrLabel}>ABSPEICHERN</Text>
</View>
</View>
</View>
</View>
</Page>
<View style={styles.addressFooter}>
<Text style={styles.addressFooterText}>
KLZ Vertriebs GmbH · Raiffeisenstraße 22 · 73630 Remshalden
</Text>
</View>
</View>,
styles.pageBleedBack,
)}
</Document>
);
};

View File

@@ -116,7 +116,7 @@
"prepare": "husky",
"preinstall": "npx only-allow pnpm"
},
"version": "2.3.23",
"version": "2.3.24",
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -19,13 +19,15 @@ 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[] = [
{
name: 'Michael Bodemer',
role: 'Managing Director',
phone: '+49 171 785 2420',
role: 'Owner & Managing Director',
phone: '+49 151 462 467 98',
email: 'michael.bodemer@klz-cables.com',
},
{
@@ -36,8 +38,8 @@ const PEOPLE: PersonData[] = [
},
{
name: 'Johannes Gleich',
role: 'Platzhalter Rolle',
phone: '+49 000 0000000',
role: 'Senior Key Account Manager',
phone: '+49 172 739 1109',
email: 'johannes.gleich@klz-cables.com',
},
];
@@ -71,69 +73,96 @@ 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)...`);
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',
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}`);
// Generate White Variant
const finalFileNameWhite = `KLZ_Visitenkarte_${safeName}_White.pdf`;
const rgbPathWhite = path.join(CONFIG.outputDir, `RGB_${finalFileNameWhite}`);
const finalPathWhite = path.join(CONFIG.outputDir, finalFileNameWhite);
console.log(`- Rendering White Variant...`);
const bufferWhite = await renderToBuffer(
// 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: 'white',
variant: 'blue',
isViaprinto: true,
}),
);
fs.writeFileSync(tempViaprintoPath, buffer);
fs.writeFileSync(rgbPathWhite, bufferWhite);
// Convert to CMYK for Viaprinto
const finalFileNameCMYK = `KLZ_Visitenkarte_${safeName}_Viaprinto_CMYK_91x61mm_3mmBleed.pdf`;
const finalPathCMYK = path.join(CONFIG.outputDir, finalFileNameCMYK);
const gsCommandWhite = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPathWhite}" "${rgbPathWhite}"`;
execSync(gsCommandWhite, { stdio: 'ignore' });
fs.unlinkSync(rgbPathWhite);
console.log(`✓ White Variant generated: ${finalFileNameWhite}`);
console.log(`- Converting to CMYK for Viaprinto...`);
const cmykCommand = `bash "${CONFIG.ghostscriptScript}" "${tempViaprintoPath}" "${finalPathCMYK}"`;
try {
execSync(cmykCommand, { stdio: 'inherit' });
fs.unlinkSync(tempViaprintoPath);
console.log(`✓ CMYK Print File generated: ${finalFileNameCMYK}`);
} catch (e) {
console.error(`❌ CMYK conversion failed! Is Ghostscript installed?`);
}
// Generate Black Variant
const finalFileNameBlack = `KLZ_Visitenkarte_${safeName}_Black.pdf`;
const rgbPathBlack = path.join(CONFIG.outputDir, `RGB_${finalFileNameBlack}`);
const finalPathBlack = path.join(CONFIG.outputDir, finalFileNameBlack);
// 3. NO TRUCK PDF (RGB)
console.log(`- Rendering No Truck Variant (RGB)...`);
const noTruckFileName = `KLZ_Visitenkarte_${safeName}_NoTruck.pdf`;
const tempNoTruckPath = path.join(CONFIG.outputDir, `temp_${noTruckFileName}`);
const finalNoTruckPath = path.join(CONFIG.outputDir, noTruckFileName);
console.log(`- Rendering Black Variant...`);
const bufferBlack = await renderToBuffer(
buffer = await renderToBuffer(
React.createElement(PDFBusinessCard, {
person,
qrCodeDataUrl,
variant: 'black',
variant: 'blue',
isViaprinto: false,
withTruck: false,
}),
);
fs.writeFileSync(tempNoTruckPath, buffer);
const gsNoTruckCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalNoTruckPath}" "${tempNoTruckPath}"`;
execSync(gsNoTruckCommand, { stdio: 'inherit' });
fs.unlinkSync(tempNoTruckPath);
// 4. CROP MARKS PDF (RGB)
console.log(`- Rendering Crop Marks Variant (RGB)...`);
const cropMarksFileName = `KLZ_Visitenkarte_${safeName}_CropMarks.pdf`;
const tempCropMarksPath = path.join(CONFIG.outputDir, `temp_${cropMarksFileName}`);
const finalCropMarksPath = path.join(CONFIG.outputDir, cropMarksFileName);
fs.writeFileSync(rgbPathBlack, bufferBlack);
const gsCommandBlack = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalPathBlack}" "${rgbPathBlack}"`;
execSync(gsCommandBlack, { stdio: 'ignore' });
fs.unlinkSync(rgbPathBlack);
console.log(`✓ Black Variant generated: ${finalFileNameBlack}`);
buffer = await renderToBuffer(
React.createElement(PDFBusinessCard, {
person,
qrCodeDataUrl,
variant: 'blue',
isViaprinto: false,
withCropMarks: true,
}),
);
fs.writeFileSync(tempCropMarksPath, buffer);
const gsCropMarksCommand = `gs -dNoOutputFonts -sDEVICE=pdfwrite -o "${finalCropMarksPath}" "${tempCropMarksPath}"`;
execSync(gsCropMarksCommand, { stdio: 'inherit' });
fs.unlinkSync(tempCropMarksPath);
console.log(`✓ Crop Marks Print File generated: ${cropMarksFileName}`);
}
}

View File

@@ -0,0 +1,93 @@
import { PDFDocument, PDFName, PDFString, PDFNumber, PDFDict, PDFArray } from 'pdf-lib';
import fs from 'fs';
import path from 'path';
async function run() {
const filePaths = [
'KLZ_Visitenkarte_Michael_Bodemer_Viaprinto_CMYK.pdf',
'KLZ_Visitenkarte_Klaus_Mintel_Viaprinto_CMYK.pdf',
'KLZ_Visitenkarte_Johannes_Gleich_Viaprinto_CMYK.pdf',
];
const outputDir = path.join(process.cwd(), '.klz-print-engine-output');
for (const fileName of filePaths) {
const filePath = path.join(outputDir, fileName);
if (!fs.existsSync(filePath)) continue;
console.log(`Injecting Letterpress spot color into: ${fileName}`);
const pdfBytes = fs.readFileSync(filePath);
const pdfDoc = await PDFDocument.load(pdfBytes);
// Create the /Separation /letterpress /DeviceCMYK color space
// 0 1 0 0 in CMYK is Magenta.
const tintTransform = pdfDoc.context.obj({
FunctionType: 4,
Domain: [0, 1],
Range: [0, 1, 0, 1, 0, 1, 0, 1],
Length: 26,
});
// Simple postscript function: { 0 1 0 0 } - just output magenta for preview
const stream = pdfDoc.context.stream('{ 0 1 0 0 }', {
FunctionType: 4,
Domain: [0, 1],
Range: [0, 1, 0, 1, 0, 1, 0, 1],
});
const letterpressCS = pdfDoc.context.obj([
PDFName.of('Separation'),
PDFName.of('letterpress'),
PDFName.of('DeviceCMYK'),
stream,
]);
const csRef = pdfDoc.context.register(letterpressCS);
const extGState = pdfDoc.context.obj({
Type: 'ExtGState',
OP: true,
op: true,
OPM: 1,
});
const gsRef = pdfDoc.context.register(extGState);
// We inject the color space into all pages
const pages = pdfDoc.getPages();
for (const page of pages) {
let resources = page.node.Resources();
if (!resources) {
resources = pdfDoc.context.obj({});
page.node.set(PDFName.of('Resources'), resources);
}
// ColorSpace
let colorSpaces = resources.get(PDFName.of('ColorSpace')) as PDFDict;
if (!colorSpaces) {
colorSpaces = pdfDoc.context.obj({});
resources.set(PDFName.of('ColorSpace'), colorSpaces);
}
colorSpaces.set(PDFName.of('CSLetterpress'), csRef);
// ExtGState
let extGStates = resources.get(PDFName.of('ExtGState')) as PDFDict;
if (!extGStates) {
extGStates = pdfDoc.context.obj({});
resources.set(PDFName.of('ExtGState'), extGStates);
}
extGStates.set(PDFName.of('GSLetterpress'), gsRef);
}
// Now we need to search the Content Streams for #ff00fe.
// In CMYK without ICC profiling, Ghostscript maps #ff00fe (RGB 1 0 0.996)
// Actually, Ghostscript strips RGB completely. It maps it to something close to 0 1 0 0 k.
// Instead of messing with Ghostscript output, I'll just write a new file!
// Actually, I don't need to do stream hacking if I just generate a separate PDF
// from React-PDF that contains ONLY the letterpress elements,
// then use Ghostscript to merge them!
// Since stream manipulation is highly brittle, let's keep it simple.
}
}
run().catch(console.error);

View File

@@ -0,0 +1,64 @@
import fs from 'fs';
const svgFile = process.argv[2];
const outFile = process.argv[3];
let content = fs.readFileSync(svgFile, 'utf8');
content = content.replace(/<\?xml.*\?>/g, '');
content = content.replace(/<!DOCTYPE.*>/g, '');
content = content.replace(/xmlns:xlink="[^"]+"/g, '');
content = content.replace(/xmlns:serif="[^"]+"/g, '');
content = content.replace(/xml:space="preserve"/g, '');
content = content.replace(/xmlns="[^"]+"/g, '');
content = content.replace(/version="[^"]+"/g, '');
// Convert attributes to camelCase
content = content.replace(/fill-rule/g, 'fillRule');
content = content.replace(/clip-rule/g, 'clipRule');
content = content.replace(/stroke-linejoin/g, 'strokeLinejoin');
content = content.replace(/stroke-miterlimit/g, 'strokeMiterlimit');
// Convert style="fill:#xxxx;fill-rule:nonzero;" to fill="#xxxx" fillRule="nonzero"
content = content.replace(/style="([^"]+)"/g, (match, styleStr) => {
const parts = styleStr.split(';').filter(Boolean);
let attrs = [];
for (const part of parts) {
const [key, val] = part.split(':');
if (key === 'fill') attrs.push(`fill="${val}"`);
if (key === 'fillRule' || key === 'fill-rule') attrs.push(`fillRule="${val}"`);
if (key === 'clipRule' || key === 'clip-rule') attrs.push(`clipRule="${val}"`);
if (key === 'strokeLinejoin' || key === 'stroke-linejoin')
attrs.push(`strokeLinejoin="${val}"`);
if (key === 'strokeMiterlimit' || key === 'stroke-miterlimit')
attrs.push(`strokeMiterlimit="${val}"`);
}
return attrs.join(' ');
});
// React components
content = content.replace(/<svg/g, '<Svg');
content = content.replace(/<\/svg>/g, '</Svg>');
content = content.replace(/<g/g, '<G');
content = content.replace(/<\/g>/g, '</G>');
content = content.replace(/<path/g, '<Path');
content = content.replace(/<\/path>/g, '</Path>');
// In React PDF, width/height on Svg can be removed and controlled via style.
content = content.replace(/width="100%" height="100%" /, '');
const final = `
import React from 'react';
import { Svg, G, Path } from '@react-pdf/renderer';
export const TruckBlueprint = ({ style, opacity = 1 }: { style?: any, opacity?: number }) => (
<Svg viewBox="0 0 1615 974" style={[style, { opacity }]}>
<G fillRule="evenodd" clipRule="evenodd" strokeLinejoin="round" strokeMiterlimit="2">
${content.match(/<G[\s\S]*<\/G>/)?.[0] || content}
</G>
</Svg>
);
`;
fs.writeFileSync(outFile, final);
console.log('Done!');