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
94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
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);
|