n8n-nodes-doc-fill-handwritten-finalyy
Version:
Custom n8n node to fill PDF forms with handwritten.ttf font
238 lines • 10.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocFill = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const DocFillUtils_1 = require("./DocFillUtils");
const pdf_lib_1 = require("pdf-lib");
const fontkit_1 = __importDefault(require("@pdf-lib/fontkit"));
const promises_1 = require("fs/promises");
const path_1 = require("path");
function wrapText(text, fonts, fontSize, maxWidth, getRandomFont) {
const chars = text.split('');
const lines = [];
const fontsPerChar = [];
let currentLine = '';
let currentLineFonts = [];
let currentWidth = 0;
for (const char of chars) {
const font = getRandomFont();
const charWidth = font.widthOfTextAtSize(char, fontSize);
if (currentWidth + charWidth <= maxWidth) {
currentLine += char;
currentLineFonts.push(font);
currentWidth += charWidth;
}
else {
if (currentLine) {
lines.push(currentLine);
fontsPerChar.push(currentLineFonts);
}
currentLine = char;
currentLineFonts = [font];
currentWidth = charWidth;
}
}
if (currentLine) {
lines.push(currentLine);
fontsPerChar.push(currentLineFonts);
}
return { lines, fontsPerChar };
}
const nodeOperationOptions = [
{
displayName: 'Property Name',
name: 'dataPropertyName',
type: 'string',
default: 'data',
description: 'Name of the binary property which holds the document to be used',
},
{
displayName: 'Property Name Out',
name: 'dataPropertyNameOut',
type: 'string',
default: 'data',
description: 'Name of the binary property for the output',
},
{
displayName: 'Configuration JSON',
name: 'configurationJson',
type: 'json',
default: '',
description: 'JSON used to map the keys in the PDF form to the corresponding values',
},
];
class DocFill {
constructor() {
this.description = {
displayName: 'Doc Fill HAND',
name: 'docFill',
group: ['transform'],
version: 1,
description: 'Node made for filling a pdf form.',
defaults: {
name: 'Doc Fill HAND',
},
inputs: ['main'],
inputNames: ['Document'],
outputs: ['main'],
properties: [...nodeOperationOptions],
};
}
async execute() {
const items = this.getInputData();
let itemBinaryData;
let dataPropertyName;
let dataPropertyNameOut;
let jsonString;
let docFillConfigs;
let docBinaryData;
let docBuffer;
let pdfDoc;
let pdfForm;
const returnData = [];
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex, '');
dataPropertyNameOut = this.getNodeParameter('dataPropertyNameOut', itemIndex, '');
jsonString = this.getNodeParameter('configurationJson', itemIndex, '');
try {
itemBinaryData = items[itemIndex].binary;
docBinaryData = itemBinaryData[dataPropertyName];
if (!(0, DocFillUtils_1.isPDFDocument)(docBinaryData)) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input (on binary property "${dataPropertyName}") should be a PDF file, was ${docBinaryData.mimeType} instead`, { itemIndex });
}
docBuffer = await this.helpers.getBinaryDataBuffer(itemIndex, dataPropertyName);
pdfDoc = await pdf_lib_1.PDFDocument.load(docBuffer);
pdfDoc.registerFontkit(fontkit_1.default);
const fontFiles = [
'handwritten1.ttf',
'handwritten2.ttf',
'handwritten3.ttf',
'handwritten4.ttf',
'handwritten5.ttf',
'handwritten6.ttf',
'handwritten7.ttf',
'handwritten8.ttf',
'handwritten9.ttf',
];
const customFonts = [];
for (const fontFile of fontFiles) {
try {
const fontPath = (0, path_1.resolve)(__dirname, '..', '..', '..', 'fonts', fontFile);
console.log('Attempting to load font from:', fontPath);
const fontBytes = await (0, promises_1.readFile)(fontPath);
const font = await pdfDoc.embedFont(fontBytes);
customFonts.push(font);
console.log(`Font ${fontFile} loaded successfully`);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load font ${fontFile}: ${error.message}`, { itemIndex });
}
}
const getRandomFont = () => {
const randomIndex = Math.floor(Math.random() * customFonts.length);
return customFonts[randomIndex];
};
const getRandomSpacing = () => {
return Math.random() * 0.7 - 0.5;
};
pdfForm = pdfDoc.getForm();
docFillConfigs = JSON.parse(jsonString);
docFillConfigs.forEach((el) => {
var _a, _b;
if (el.type === 'textfield') {
const pdfTextField = pdfForm.getTextField(el.key);
const widget = pdfTextField.acroField.getWidgets()[0];
if (widget) {
const page = pdfDoc.getPages()[0];
const { x, y, width, height } = widget.getRectangle();
pdfTextField.setText('');
const xOffset = (_a = el.xOffset) !== null && _a !== void 0 ? _a : 0;
const yOffset = (_b = el.yOffset) !== null && _b !== void 0 ? _b : 0;
if (el.key === 'BT' || el.key === 'U') {
const { lines, fontsPerChar } = wrapText(el.value, customFonts, 12, width, getRandomFont);
let currentY = y + height - 12;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineFonts = fontsPerChar[i];
if (currentY < y)
break;
let currentX = x + xOffset;
for (let j = 0; j < line.length; j++) {
const char = line[j];
const font = lineFonts[j];
const charWidth = font.widthOfTextAtSize(char, 12);
page.drawText(char, {
x: currentX,
y: currentY + yOffset,
font: font,
size: 12,
color: (0, pdf_lib_1.rgb)(0, 0, 0),
});
currentX += charWidth + getRandomSpacing();
}
currentY -= 14;
}
}
else {
const { lines, fontsPerChar } = wrapText(el.value, customFonts, 12, width, getRandomFont);
if (lines.length > 0) {
const line = lines[0];
const lineFonts = fontsPerChar[0];
let currentX = x + xOffset;
for (let j = 0; j < line.length; j++) {
const char = line[j];
const font = lineFonts[j];
const charWidth = font.widthOfTextAtSize(char, 12);
page.drawText(char, {
x: currentX,
y: y + yOffset,
font: font,
size: 12,
color: (0, pdf_lib_1.rgb)(0, 0, 0),
});
currentX += charWidth + getRandomSpacing();
}
}
}
}
}
else {
(0, DocFillUtils_1.fillForm)(pdfForm, el, customFonts[0]);
}
});
let savedDoc = await pdfDoc.save();
const result = {
json: {
...items[itemIndex].json,
},
binary: {
...items[itemIndex].binary,
[dataPropertyNameOut]: await this.helpers.prepareBinaryData(Buffer.from(savedDoc), docBinaryData.fileName ? docBinaryData.fileName : dataPropertyNameOut + '.pdf', 'application/pdf'),
},
pairedItem: [items[itemIndex].pairedItem],
};
returnData.push(result);
}
catch (error) {
if (this.continueOnFail()) {
items.push({ json: this.getInputData(itemIndex)[0].json, error, pairedItem: itemIndex });
}
else {
if (error.context) {
error.context.itemIndex = itemIndex;
throw error;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, {
itemIndex,
});
}
}
}
return this.prepareOutputData(returnData);
}
}
exports.DocFill = DocFill;
//# sourceMappingURL=DocFill.node.js.map