UNPKG

n8n-nodes-gotenberg-pdf

Version:

n8n community node for Gotenberg PDF conversion API

875 lines 41 kB
"use strict"; 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 &lt;span class="pageNumber"&gt;&lt;/span&gt; and &lt;span class="totalPages"&gt;&lt;/span&gt; for page numbers.', placeholder: '&lt;html&gt;&lt;head&gt;&lt;style&gt;body{font-size:12px;margin:auto 20px;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Page &lt;span class="pageNumber"&gt;&lt;/span&gt; of &lt;span class="totalPages"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;', }, { displayName: 'Footer HTML', name: 'footerHtml', type: 'string', typeOptions: { editor: 'htmlEditor', rows: 4, }, default: '', description: 'HTML content for page footer. Use &lt;span class="pageNumber"&gt;&lt;/span&gt; and &lt;span class="totalPages"&gt;&lt;/span&gt; for page numbers.', placeholder: '&lt;html&gt;&lt;head&gt;&lt;style&gt;body{font-size:12px;margin:auto 20px;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Page &lt;span class="pageNumber"&gt;&lt;/span&gt; of &lt;span class="totalPages"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;', }, ], }, ], }; } 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