n8n-nodes-gotenberg-pdf
Version:
n8n community node for Gotenberg PDF conversion API
875 lines • 41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Gotenberg = void 0;
const n8n_workflow_1 = require("n8n-workflow");
class Gotenberg {
constructor() {
this.description = {
displayName: 'Gotenberg',
name: 'gotenberg',
icon: { light: 'file:gotenberg.svg', dark: 'file:gotenberg.svg' },
group: ['transform'],
version: 1,
description: 'Convert HTML to PDF using Gotenberg API',
defaults: {
name: 'Gotenberg',
},
inputs: ["main"],
outputs: ["main"],
credentials: [
{
name: 'gotenbergApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'HTML to PDF',
value: 'htmlToPdf',
description: 'Convert HTML content to PDF',
action: 'Convert HTML to PDF',
},
{
name: 'URL to PDF',
value: 'urlToPdf',
description: 'Convert a web page to PDF',
action: 'Convert URL to PDF',
},
{
name: 'Merge PDFs',
value: 'mergePdfs',
description: 'Merge multiple PDF files into one',
action: 'Merge PDF files',
},
{
name: 'Split PDF',
value: 'splitPdf',
description: 'Split PDF file into multiple files',
action: 'Split PDF file',
},
],
default: 'htmlToPdf',
},
{
displayName: 'HTML Content',
name: 'htmlContent',
type: 'string',
displayOptions: {
show: {
operation: ['htmlToPdf'],
},
},
typeOptions: {
editor: 'htmlEditor',
rows: 10,
},
default: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My PDF</title>
</head>
<body>
<h1>Hello world!</h1>
<img src="image.png" alt="Example Image" />
</body>
</html>`,
description: 'The HTML content to convert to PDF. You can reference images and other assets by filename.',
required: true,
},
{
displayName: 'Additional Files',
name: 'additionalFiles',
type: 'collection',
displayOptions: {
show: {
operation: ['htmlToPdf'],
},
},
placeholder: 'Add Files',
default: {},
description: 'Additional files to include (images, CSS, fonts, etc.)',
options: [
{
displayName: 'Image Files',
name: 'imageFiles',
type: 'string',
default: 'data',
description: 'Binary field name containing image files (PNG, JPG, GIF, etc.)',
placeholder: 'data',
},
{
displayName: 'CSS Files',
name: 'cssFiles',
type: 'string',
default: 'data',
description: 'Binary field name containing CSS stylesheet files',
placeholder: 'data',
},
{
displayName: 'Font Files',
name: 'fontFiles',
type: 'string',
default: 'data',
description: 'Binary field name containing font files (WOFF, TTF, etc.)',
placeholder: 'data',
},
{
displayName: 'Other Files',
name: 'otherFiles',
type: 'string',
default: 'data',
description: 'Binary field name containing any other asset files',
placeholder: 'data',
},
],
},
{
displayName: 'URL',
name: 'url',
type: 'string',
displayOptions: {
show: {
operation: ['urlToPdf'],
},
},
default: '',
placeholder: 'https://example.com',
description: 'The URL of the web page to convert to PDF',
required: true,
},
{
displayName: 'PDF Files',
name: 'pdfFiles',
type: 'string',
displayOptions: {
show: {
operation: ['mergePdfs'],
},
},
default: 'data',
description: 'The binary field name containing PDF files to merge',
required: true,
},
{
displayName: 'PDF File',
name: 'pdfFile',
type: 'string',
displayOptions: {
show: {
operation: ['splitPdf'],
},
},
default: 'data',
description: 'The binary field name containing the PDF file to split',
required: true,
},
{
displayName: 'Split Mode',
name: 'splitMode',
type: 'options',
displayOptions: {
show: {
operation: ['splitPdf'],
},
},
options: [
{
name: 'By Intervals',
value: 'intervals',
description: 'Split into equal page intervals',
},
{
name: 'Specific Pages',
value: 'pages',
description: 'Split at specific page numbers',
},
],
default: 'intervals',
description: 'How to split the PDF',
},
{
displayName: 'Interval Size',
name: 'intervalSize',
type: 'number',
displayOptions: {
show: {
operation: ['splitPdf'],
splitMode: ['intervals'],
},
},
default: 10,
description: 'Number of pages per split file',
typeOptions: {
minValue: 1,
},
},
{
displayName: 'Page Numbers',
name: 'pageNumbers',
type: 'string',
displayOptions: {
show: {
operation: ['splitPdf'],
splitMode: ['pages'],
},
},
default: '5,10,15',
description: 'Comma-separated page numbers where to split (e.g., "5,10,15")',
placeholder: '5,10,15',
},
{
displayName: 'Paper Size',
name: 'paperSize',
type: 'options',
displayOptions: {
show: {
operation: ['htmlToPdf', 'urlToPdf'],
},
},
options: [
{
name: 'A3',
value: 'A3',
},
{
name: 'A4',
value: 'A4',
},
{
name: 'A5',
value: 'A5',
},
{
name: 'Legal',
value: 'Legal',
},
{
name: 'Letter',
value: 'Letter',
},
],
default: 'A4',
description: 'The paper size for the PDF',
},
{
displayName: 'Landscape',
name: 'landscape',
type: 'boolean',
displayOptions: {
show: {
operation: ['htmlToPdf', 'urlToPdf'],
},
},
default: false,
description: 'Whether to use landscape orientation',
},
{
displayName: 'Advanced Options',
name: 'advancedOptions',
type: 'collection',
displayOptions: {
show: {
operation: ['htmlToPdf', 'urlToPdf'],
},
},
placeholder: 'Add Option',
default: {},
description: 'Advanced PDF generation options',
options: [
{
displayName: 'Emulated Media Type',
name: 'emulatedMediaType',
type: 'options',
options: [
{
name: 'Print',
value: 'print',
},
{
name: 'Screen',
value: 'screen',
},
],
default: 'print',
description: 'Media type to emulate when rendering',
},
{
displayName: 'Flatten PDF',
name: 'flatten',
type: 'boolean',
default: false,
description: 'Whether to flatten the resulting PDF',
},
{
displayName: 'Generate Tagged PDF',
name: 'generateTaggedPdf',
type: 'boolean',
default: false,
description: 'Whether to generate tagged (accessible) PDF',
},
{
displayName: 'Margins',
name: 'margins',
type: 'fixedCollection',
description: 'Custom page margins in inches',
default: {},
options: [
{
displayName: 'Margin Values',
name: 'marginValues',
values: [
{
displayName: 'Top',
name: 'top',
type: 'number',
default: 0.39,
description: 'Top margin in inches',
typeOptions: {
numberPrecision: 2,
minValue: 0,
},
},
{
displayName: 'Bottom',
name: 'bottom',
type: 'number',
default: 0.39,
description: 'Bottom margin in inches',
typeOptions: {
numberPrecision: 2,
minValue: 0,
},
},
{
displayName: 'Left',
name: 'left',
type: 'number',
default: 0.39,
description: 'Left margin in inches',
typeOptions: {
numberPrecision: 2,
minValue: 0,
},
},
{
displayName: 'Right',
name: 'right',
type: 'number',
default: 0.39,
description: 'Right margin in inches',
typeOptions: {
numberPrecision: 2,
minValue: 0,
},
},
],
},
],
},
{
displayName: 'Metadata',
name: 'metadata',
type: 'json',
default: '{}',
description: 'PDF metadata as JSON object (e.g., {"Author": "John Doe", "Title": "My Document"})',
placeholder: '{"Author": "John Doe", "Title": "My Document"}',
},
{
displayName: 'Page Ranges',
name: 'pageRanges',
type: 'string',
default: '',
description: 'Page ranges to print (e.g., "1-5,8,11-13"). Leave empty for all pages.',
placeholder: '1-5,8,11-13',
},
{
displayName: 'PDF/A Format',
name: 'pdfa',
type: 'options',
options: [
{
name: 'None',
value: '',
},
{
name: 'PDF/A-1b',
value: 'PDF/A-1b',
},
{
name: 'PDF/A-2b',
value: 'PDF/A-2b',
},
{
name: 'PDF/A-3b',
value: 'PDF/A-3b',
},
],
default: '',
description: 'Convert to PDF/A format for long-term archival',
},
{
displayName: 'PDF/UA Accessibility',
name: 'pdfua',
type: 'boolean',
default: false,
description: 'Whether to enable PDF for Universal Access (PDF/UA) for optimal accessibility',
},
{
displayName: 'Print Background',
name: 'printBackground',
type: 'boolean',
default: false,
description: 'Whether to print background graphics',
},
{
displayName: 'Scale',
name: 'scale',
type: 'number',
default: 1.0,
description: 'The scale of the page rendering (0.1 to 2.0)',
typeOptions: {
numberPrecision: 2,
minValue: 0.1,
maxValue: 2.0,
},
},
{
displayName: 'Single Page',
name: 'singlePage',
type: 'boolean',
default: false,
description: 'Whether to print entire content in one single page',
},
{
displayName: 'Wait Delay',
name: 'waitDelay',
type: 'number',
default: 0,
description: 'Duration in seconds to wait before converting (useful for JavaScript-heavy pages)',
typeOptions: {
numberPrecision: 1,
minValue: 0,
maxValue: 30,
},
},
],
},
{
displayName: 'Header & Footer',
name: 'headerFooter',
type: 'collection',
displayOptions: {
show: {
operation: ['htmlToPdf', 'urlToPdf'],
},
},
placeholder: 'Add Header/Footer',
default: {},
description: 'Add custom header and footer to PDF pages',
options: [
{
displayName: 'Header HTML',
name: 'headerHtml',
type: 'string',
typeOptions: {
editor: 'htmlEditor',
rows: 4,
},
default: '',
description: 'HTML content for page header. Use <span class="pageNumber"></span> and <span class="totalPages"></span> for page numbers.',
placeholder: '<html><head><style>body{font-size:12px;margin:auto 20px;}</style></head><body><p>Page <span class="pageNumber"></span> of <span class="totalPages"></span></p></body></html>',
},
{
displayName: 'Footer HTML',
name: 'footerHtml',
type: 'string',
typeOptions: {
editor: 'htmlEditor',
rows: 4,
},
default: '',
description: 'HTML content for page footer. Use <span class="pageNumber"></span> and <span class="totalPages"></span> for page numbers.',
placeholder: '<html><head><style>body{font-size:12px;margin:auto 20px;}</style></head><body><p>Page <span class="pageNumber"></span> of <span class="totalPages"></span></p></body></html>',
},
],
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
try {
const operation = this.getNodeParameter('operation', i);
const credentials = await this.getCredentials('gotenbergApi');
const baseUrl = credentials.baseUrl;
let response;
let fileName = 'document.pdf';
let mimeType = 'application/pdf';
let formDataObj = {};
if (operation === 'htmlToPdf') {
const htmlContent = this.getNodeParameter('htmlContent', i);
const paperSize = this.getNodeParameter('paperSize', i);
const landscape = this.getNodeParameter('landscape', i);
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
const headerFooter = this.getNodeParameter('headerFooter', i, {});
const additionalFiles = this.getNodeParameter('additionalFiles', i, {});
formDataObj = {
files: {
value: htmlContent,
options: {
filename: 'index.html',
contentType: 'text/html',
},
},
};
let fileIndex = 0;
const fileTypes = [
{ field: additionalFiles.imageFiles, type: 'image' },
{ field: additionalFiles.cssFiles, type: 'css' },
{ field: additionalFiles.fontFiles, type: 'font' },
{ field: additionalFiles.otherFiles, type: 'other' }
];
for (const fileType of fileTypes) {
if (fileType.field && items[i].binary && items[i].binary[fileType.field]) {
const binaryData = items[i].binary[fileType.field];
if (Array.isArray(binaryData)) {
for (const file of binaryData) {
formDataObj[`file_${fileIndex}`] = {
value: file.data,
options: {
filename: file.fileName || `${fileType.type}_${fileIndex}.bin`,
contentType: file.mimeType || 'application/octet-stream',
},
};
fileIndex++;
}
}
else {
formDataObj[`file_${fileIndex}`] = {
value: binaryData.data,
options: {
filename: binaryData.fileName || `${fileType.type}_${fileIndex}.bin`,
contentType: binaryData.mimeType || 'application/octet-stream',
},
};
fileIndex++;
}
}
}
if (headerFooter.headerHtml) {
formDataObj[`file_${fileIndex}`] = {
value: headerFooter.headerHtml,
options: {
filename: 'header.html',
contentType: 'text/html',
},
};
fileIndex++;
}
if (headerFooter.footerHtml) {
formDataObj[`file_${fileIndex}`] = {
value: headerFooter.footerHtml,
options: {
filename: 'footer.html',
contentType: 'text/html',
},
};
fileIndex++;
}
if (paperSize === 'A4') {
formDataObj.paperWidth = '8.27in';
formDataObj.paperHeight = '11.7in';
}
else if (paperSize === 'Letter') {
formDataObj.paperWidth = '8.5in';
formDataObj.paperHeight = '11in';
}
else if (paperSize === 'Legal') {
formDataObj.paperWidth = '8.5in';
formDataObj.paperHeight = '14in';
}
else if (paperSize === 'A3') {
formDataObj.paperWidth = '11.7in';
formDataObj.paperHeight = '16.5in';
}
else if (paperSize === 'A5') {
formDataObj.paperWidth = '5.8in';
formDataObj.paperHeight = '8.3in';
}
if (landscape) {
formDataObj.landscape = 'true';
}
if (advancedOptions.margins && advancedOptions.margins.marginValues) {
const margins = advancedOptions.margins.marginValues;
if (margins.top !== undefined)
formDataObj.marginTop = `${margins.top}in`;
if (margins.bottom !== undefined)
formDataObj.marginBottom = `${margins.bottom}in`;
if (margins.left !== undefined)
formDataObj.marginLeft = `${margins.left}in`;
if (margins.right !== undefined)
formDataObj.marginRight = `${margins.right}in`;
}
if (advancedOptions.scale !== undefined && advancedOptions.scale !== 1.0) {
formDataObj.scale = advancedOptions.scale.toString();
}
if (advancedOptions.waitDelay !== undefined && advancedOptions.waitDelay > 0) {
formDataObj.waitDelay = `${advancedOptions.waitDelay}s`;
}
if (advancedOptions.pageRanges) {
formDataObj.nativePageRanges = advancedOptions.pageRanges;
}
if (advancedOptions.printBackground === true) {
formDataObj.printBackground = 'true';
}
if (advancedOptions.singlePage === true) {
formDataObj.singlePage = 'true';
}
if (advancedOptions.generateTaggedPdf === true) {
formDataObj.generateTaggedPdf = 'true';
}
if (advancedOptions.pdfa) {
formDataObj.pdfa = advancedOptions.pdfa;
}
if (advancedOptions.pdfua === true) {
formDataObj.pdfua = 'true';
}
if (advancedOptions.emulatedMediaType && advancedOptions.emulatedMediaType !== 'print') {
formDataObj.emulatedMediaType = advancedOptions.emulatedMediaType;
}
if (advancedOptions.flatten === true) {
formDataObj.flatten = 'true';
}
if (advancedOptions.metadata) {
try {
const metadata = typeof advancedOptions.metadata === 'string' ?
JSON.parse(advancedOptions.metadata) : advancedOptions.metadata;
formDataObj.metadata = JSON.stringify(metadata);
}
catch (error) {
}
}
response = await this.helpers.request({
method: 'POST',
url: `${baseUrl}/forms/chromium/convert/html`,
formData: formDataObj,
encoding: null,
});
}
else if (operation === 'urlToPdf') {
const url = this.getNodeParameter('url', i);
const paperSize = this.getNodeParameter('paperSize', i);
const landscape = this.getNodeParameter('landscape', i);
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
const headerFooter = this.getNodeParameter('headerFooter', i, {});
formDataObj = {
url: url,
};
if (headerFooter.headerHtml) {
formDataObj.header_files = {
value: headerFooter.headerHtml,
options: {
filename: 'header.html',
contentType: 'text/html',
},
};
}
if (headerFooter.footerHtml) {
formDataObj.footer_files = {
value: headerFooter.footerHtml,
options: {
filename: 'footer.html',
contentType: 'text/html',
},
};
}
if (paperSize === 'A4') {
formDataObj.paperWidth = '8.27in';
formDataObj.paperHeight = '11.7in';
}
else if (paperSize === 'Letter') {
formDataObj.paperWidth = '8.5in';
formDataObj.paperHeight = '11in';
}
else if (paperSize === 'Legal') {
formDataObj.paperWidth = '8.5in';
formDataObj.paperHeight = '14in';
}
else if (paperSize === 'A3') {
formDataObj.paperWidth = '11.7in';
formDataObj.paperHeight = '16.5in';
}
else if (paperSize === 'A5') {
formDataObj.paperWidth = '5.8in';
formDataObj.paperHeight = '8.3in';
}
if (landscape) {
formDataObj.landscape = 'true';
}
if (advancedOptions.margins && advancedOptions.margins.marginValues) {
const margins = advancedOptions.margins.marginValues;
if (margins.top !== undefined)
formDataObj.marginTop = `${margins.top}in`;
if (margins.bottom !== undefined)
formDataObj.marginBottom = `${margins.bottom}in`;
if (margins.left !== undefined)
formDataObj.marginLeft = `${margins.left}in`;
if (margins.right !== undefined)
formDataObj.marginRight = `${margins.right}in`;
}
if (advancedOptions.scale !== undefined && advancedOptions.scale !== 1.0) {
formDataObj.scale = advancedOptions.scale.toString();
}
if (advancedOptions.waitDelay !== undefined && advancedOptions.waitDelay > 0) {
formDataObj.waitDelay = `${advancedOptions.waitDelay}s`;
}
if (advancedOptions.pageRanges) {
formDataObj.nativePageRanges = advancedOptions.pageRanges;
}
if (advancedOptions.printBackground === true) {
formDataObj.printBackground = 'true';
}
if (advancedOptions.singlePage === true) {
formDataObj.singlePage = 'true';
}
if (advancedOptions.generateTaggedPdf === true) {
formDataObj.generateTaggedPdf = 'true';
}
if (advancedOptions.pdfa) {
formDataObj.pdfa = advancedOptions.pdfa;
}
if (advancedOptions.pdfua === true) {
formDataObj.pdfua = 'true';
}
if (advancedOptions.emulatedMediaType && advancedOptions.emulatedMediaType !== 'print') {
formDataObj.emulatedMediaType = advancedOptions.emulatedMediaType;
}
if (advancedOptions.flatten === true) {
formDataObj.flatten = 'true';
}
if (advancedOptions.metadata) {
try {
const metadata = typeof advancedOptions.metadata === 'string' ?
JSON.parse(advancedOptions.metadata) : advancedOptions.metadata;
formDataObj.metadata = JSON.stringify(metadata);
}
catch (error) {
}
}
response = await this.helpers.request({
method: 'POST',
url: `${baseUrl}/forms/chromium/convert/url`,
formData: formDataObj,
encoding: null,
});
}
else if (operation === 'mergePdfs') {
const pdfFiles = this.getNodeParameter('pdfFiles', i);
if (!items[i].binary || !items[i].binary[pdfFiles]) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found for field: ${pdfFiles}`);
}
const binaryData = items[i].binary[pdfFiles];
if (Array.isArray(binaryData)) {
for (let j = 0; j < binaryData.length; j++) {
formDataObj[`files_${j}`] = {
value: binaryData[j].data,
options: {
filename: binaryData[j].fileName || `document_${j}.pdf`,
contentType: binaryData[j].mimeType || 'application/pdf',
},
};
}
}
else {
formDataObj.files = {
value: binaryData.data,
options: {
filename: binaryData.fileName || 'document.pdf',
contentType: binaryData.mimeType || 'application/pdf',
},
};
}
response = await this.helpers.request({
method: 'POST',
url: `${baseUrl}/forms/pdfengines/merge`,
formData: formDataObj,
encoding: null,
});
}
else if (operation === 'splitPdf') {
const pdfFile = this.getNodeParameter('pdfFile', i);
const splitMode = this.getNodeParameter('splitMode', i);
if (!items[i].binary || !items[i].binary[pdfFile]) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found for field: ${pdfFile}`);
}
const binaryData = items[i].binary[pdfFile];
formDataObj = {
files: {
value: binaryData.data,
options: {
filename: binaryData.fileName || 'document.pdf',
contentType: binaryData.mimeType || 'application/pdf',
},
},
};
if (splitMode === 'intervals') {
const intervalSize = this.getNodeParameter('intervalSize', i);
formDataObj.splitMode = 'intervals';
formDataObj.splitSpan = intervalSize.toString();
}
else if (splitMode === 'pages') {
const pageNumbers = this.getNodeParameter('pageNumbers', i);
const pages = pageNumbers.split(',').map(p => p.trim()).join(',');
formDataObj.splitMode = 'pages';
formDataObj.splitSpan = pages;
}
response = await this.helpers.request({
method: 'POST',
url: `${baseUrl}/forms/pdfengines/split`,
formData: formDataObj,
encoding: null,
});
fileName = 'split-pages.zip';
mimeType = 'application/zip';
}
else {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`);
}
const newItem = {
json: items[i].json,
binary: {},
};
if (newItem.binary) {
newItem.binary.data = await this.helpers.prepareBinaryData(response, fileName, mimeType);
}
returnData.push(newItem);
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
pairedItem: {
item: i,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.Gotenberg = Gotenberg;
//# sourceMappingURL=Gotenberg.node.js.map