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
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:
@@ -712,7 +712,11 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
>
|
>
|
||||||
<View style={styles.truckWrapper}>
|
<View style={styles.truckWrapper}>
|
||||||
<TruckBlueprint
|
<TruckBlueprint
|
||||||
style={[styles.frontBackgroundImage, isWhiteVariant && { opacity: 0.2 }]}
|
style={[
|
||||||
|
styles.frontBackgroundImage,
|
||||||
|
isWhiteVariant && { opacity: 0.2 },
|
||||||
|
!isWhiteVariant && { opacity: 0.25 },
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
@@ -764,7 +768,19 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.frontContainer}>
|
<View style={styles.frontContainer}>
|
||||||
<VectorLogo paths={getFrontLogoPaths()} style={styles.frontLogo} />
|
{/* Base Logo: text is blue, truck is green */}
|
||||||
|
<VectorLogo
|
||||||
|
paths={getFrontLogoPaths()}
|
||||||
|
style={styles.frontLogo}
|
||||||
|
overrideWhiteColor={COLORS.blue}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Letterpress Mask Layer: We use #ff00fe as a marker color. Only the "KLZ" letters! */}
|
||||||
|
<VectorLogo
|
||||||
|
paths={getFrontLogoPaths().slice(1, 6)}
|
||||||
|
style={[styles.frontLogo, { position: 'absolute' }]}
|
||||||
|
overrideColor="#ff00fe"
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
|
|||||||
93
scripts/inject-letterpress.ts
Normal file
93
scripts/inject-letterpress.ts
Normal 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);
|
||||||
Reference in New Issue
Block a user