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

This commit is contained in:
2026-06-11 18:44:27 +02:00
parent 3798d37fcd
commit 5d94337df1
3 changed files with 9981 additions and 9 deletions

9898
lib/TruckBlueprint.tsx Normal file

File diff suppressed because one or more lines are too long

View File

@@ -16,6 +16,7 @@ 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
@@ -89,9 +90,6 @@ const styles = StyleSheet.create({
frontBackgroundImage: {
width: '100%',
height: '100%',
objectFit: 'contain',
objectPositionX: 'center',
objectPositionY: 'center',
opacity: 0.4, // Less transparent, more contrast
},
frontContainer: {
@@ -362,16 +360,25 @@ const VectorLogo = ({
paths,
style,
overrideColor,
overrideWhiteColor,
}: {
paths: { d: string; fill: string }[];
style: any;
overrideColor?: string;
overrideWhiteColor?: 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={overrideColor || p.fill} />
))}
{paths.map((p, i) => {
let fill = overrideColor || p.fill;
if (
overrideWhiteColor &&
(p.fill === 'white' || p.fill === '#ffffff' || p.fill === '#fff')
) {
fill = overrideWhiteColor;
}
return <Path key={i} d={p.d} fill={fill} />;
})}
</G>
</Svg>
);
@@ -704,8 +711,7 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
style={[styles.pageBleed, isWhiteVariant && { backgroundColor: '#ffffff' }]}
>
<View style={styles.truckWrapper}>
<Image
src={bgImagePath}
<TruckBlueprint
style={[styles.frontBackgroundImage, isWhiteVariant && { opacity: 0.2 }]}
/>
</View>
@@ -766,7 +772,11 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
<Page size={[258, 173]} style={styles.pageBleedBack}>
<View style={styles.backContainer}>
<View style={styles.backHeader}>
<VectorLogo paths={logoBluePaths} style={styles.backLogo} overrideColor={COLORS.blue} />
<VectorLogo
paths={logoWhitePaths}
style={styles.backLogo}
overrideWhiteColor={COLORS.blue}
/>
<View style={styles.accentLineHorizontal} />
</View>

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!');