All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 15s
Build & Deploy / 🧪 QA (push) Successful in 1m50s
Build & Deploy / 🏗️ Build (push) Successful in 2m51s
Build & Deploy / 🚀 Deploy (push) Successful in 18s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m24s
Build & Deploy / 🔔 Notify (push) Successful in 2s
106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
import { PDFDocument, PDFName, decodePDFRawStream, PDFRawStream } from 'pdf-lib';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
async function run() {
|
|
const inputPath = process.argv[2];
|
|
const outputPath = process.argv[3] || inputPath;
|
|
|
|
if (!inputPath || !fs.existsSync(inputPath)) {
|
|
console.error(`[ERROR] Input file not found: ${inputPath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`[INFO] Injecting PANTONE 289 C into: ${path.basename(inputPath)}`);
|
|
const pdfBytes = fs.readFileSync(inputPath);
|
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
|
|
// CMYK tint transform for PANTONE 289 C.
|
|
// Approximation: 100C, 64M, 0Y, 60K (1.0 0.64 0.0 0.60)
|
|
// The function maps an input x (0 to 1) to CMYK: x*1.0, x*0.64, x*0.0, x*0.60
|
|
const tintTransform = pdfDoc.context.obj({
|
|
FunctionType: 2,
|
|
Domain: [0, 1],
|
|
C0: [0, 0, 0, 0],
|
|
C1: [1.0, 0.64, 0.0, 0.6],
|
|
N: 1,
|
|
});
|
|
|
|
// Create the /Separation color space array
|
|
const pantoneCS = pdfDoc.context.obj([
|
|
PDFName.of('Separation'),
|
|
PDFName.of('PANTONE#20289#20C'),
|
|
PDFName.of('DeviceCMYK'),
|
|
tintTransform,
|
|
]);
|
|
const csRef = pdfDoc.context.register(pantoneCS);
|
|
|
|
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);
|
|
}
|
|
|
|
// Ensure ColorSpace dict exists
|
|
let colorSpaces = resources.get(PDFName.of('ColorSpace')) as any;
|
|
if (!colorSpaces) {
|
|
colorSpaces = pdfDoc.context.obj({});
|
|
resources.set(PDFName.of('ColorSpace'), colorSpaces);
|
|
}
|
|
// Register the PANTONE color space as /CSPantone in the page's resources
|
|
colorSpaces.set(PDFName.of('CSPantone'), csRef);
|
|
|
|
// Read all content streams
|
|
const contents = page.node.Contents();
|
|
let streams: any[] = [];
|
|
if (contents) {
|
|
const resolvedContents = pdfDoc.context.lookup(contents);
|
|
if (resolvedContents instanceof PDFRawStream) {
|
|
streams = [resolvedContents];
|
|
} else if (resolvedContents.constructor.name === 'PDFArray') {
|
|
const arr = resolvedContents as any;
|
|
for (let j = 0; j < arr.size(); j++) {
|
|
streams.push(pdfDoc.context.lookup(arr.get(j)));
|
|
}
|
|
}
|
|
}
|
|
|
|
let newStr = '';
|
|
for (let i = 0; i < streams.length; i++) {
|
|
const stream = streams[i];
|
|
if (stream instanceof PDFRawStream) {
|
|
const decoded = decodePDFRawStream(stream).decode();
|
|
const str = Buffer.from(decoded).toString('utf-8');
|
|
newStr += str + '\n';
|
|
}
|
|
}
|
|
|
|
// The CMYK values for the KLZ Blue created by Ghostscript from #000a66
|
|
// is '1 0.98 0.224 0.298'. We use a slightly dynamic regex to catch small precision diffs.
|
|
const dynamicRegexK = /1 0\.98[0-9]* 0\.224[0-9]* 0\.298[0-9]* k/g;
|
|
const dynamicRegexStrokeK = /1 0\.98[0-9]* 0\.224[0-9]* 0\.298[0-9]* K/g;
|
|
|
|
let modified = false;
|
|
if (dynamicRegexK.test(newStr) || dynamicRegexStrokeK.test(newStr)) {
|
|
newStr = newStr.replace(dynamicRegexK, '/CSPantone cs 1 scn');
|
|
newStr = newStr.replace(dynamicRegexStrokeK, '/CSPantone CS 1 SCN');
|
|
modified = true;
|
|
}
|
|
|
|
if (modified) {
|
|
const newStream = pdfDoc.context.flateStream(newStr);
|
|
const newRef = pdfDoc.context.register(newStream);
|
|
page.node.set(PDFName.of('Contents'), newRef);
|
|
console.log(`[INFO] Spot color injected into page ${pages.indexOf(page) + 1}.`);
|
|
}
|
|
}
|
|
|
|
const finalPdfBytes = await pdfDoc.save();
|
|
fs.writeFileSync(outputPath, finalPdfBytes);
|
|
console.log(`[SUCCESS] Saved 5-color PDF to ${outputPath}`);
|
|
}
|
|
|
|
run().catch(console.error);
|