@things-factory/integration-headless 7.0.38 → 7.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/engine/task/headless-pdf-capture-board.d.ts +1 -0
- package/dist-server/engine/task/headless-pdf-capture-board.js +311 -0
- package/dist-server/engine/task/headless-pdf-capture-board.js.map +1 -0
- package/dist-server/engine/task/headless-pdf-capture.js +111 -72
- package/dist-server/engine/task/headless-pdf-capture.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-open.js +255 -25
- package/dist-server/engine/task/headless-pdf-open.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-save.js +17 -47
- package/dist-server/engine/task/headless-pdf-save.js.map +1 -1
- package/dist-server/engine/task/index.d.ts +1 -0
- package/dist-server/engine/task/index.js +1 -0
- package/dist-server/engine/task/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/server/engine/task/headless-pdf-capture-board.ts +363 -0
- package/server/engine/task/headless-pdf-capture.ts +123 -96
- package/server/engine/task/headless-pdf-open.ts +286 -25
- package/server/engine/task/headless-pdf-save.ts +20 -54
- package/server/engine/task/index.ts +1 -0
- package/translations/en.json +5 -2
- package/translations/ja.json +5 -2
- package/translations/ko.json +5 -2
- package/translations/ms.json +5 -2
- package/translations/zh.json +5 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const ejs = tslib_1.__importStar(require("ejs"));
|
|
5
|
+
const integration_base_1 = require("@things-factory/integration-base");
|
|
6
|
+
const integration_base_2 = require("@things-factory/integration-base");
|
|
7
|
+
const utils_1 = require("@things-factory/utils");
|
|
8
|
+
const board_service_1 = require("@things-factory/board-service");
|
|
9
|
+
const pdf_lib_1 = require("pdf-lib");
|
|
10
|
+
const PAGE_FORMATS = {
|
|
11
|
+
A4: { width: 595.28, height: 841.89 },
|
|
12
|
+
A3: { width: 841.89, height: 1190.55 },
|
|
13
|
+
Letter: { width: 612, height: 792 },
|
|
14
|
+
Legal: { width: 612, height: 1008 }
|
|
15
|
+
};
|
|
16
|
+
async function HeadlessPDFCaptureBoard(step, context) {
|
|
17
|
+
var { connection: connectionName, params: stepOptions } = step;
|
|
18
|
+
var { accessor, board, draft, format = 'A4', // Set default value to A4
|
|
19
|
+
width, height, marginTop = 0, marginBottom = 0, marginLeft = 0, marginRight = 0, scale, printBackground, landscape, preferCSSPageSize } = stepOptions || {};
|
|
20
|
+
var { domain, data, user, logger, __headless_pdf } = context;
|
|
21
|
+
if (!__headless_pdf) {
|
|
22
|
+
throw new Error('It requires headless-pdf-open to be performed first');
|
|
23
|
+
}
|
|
24
|
+
if (!board || !board.id) {
|
|
25
|
+
throw new Error('The board property must be set');
|
|
26
|
+
}
|
|
27
|
+
var { pdfDoc, header, footer, watermark } = __headless_pdf;
|
|
28
|
+
var headlessPool = integration_base_2.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
29
|
+
let browser;
|
|
30
|
+
try {
|
|
31
|
+
browser = await headlessPool.acquire();
|
|
32
|
+
const page = await browser.newPage();
|
|
33
|
+
const boardInput = (0, utils_1.access)(accessor, data);
|
|
34
|
+
var { model, base } = await board_service_1.BoardFunc.headlessModel({ domain, id: board.id, model }, draft);
|
|
35
|
+
const [fontsToUse, fontStyles] = await board_service_1.BoardFunc.fonts(domain);
|
|
36
|
+
model.fonts = fontsToUse;
|
|
37
|
+
model.fontStyles = fontStyles;
|
|
38
|
+
var { width: boardWidthPx, height: boardHeightPx } = model;
|
|
39
|
+
if (preferCSSPageSize) {
|
|
40
|
+
width = undefined;
|
|
41
|
+
height = undefined;
|
|
42
|
+
}
|
|
43
|
+
else if (PAGE_FORMATS[format]) {
|
|
44
|
+
const pageDimensions = PAGE_FORMATS[format];
|
|
45
|
+
width = pageDimensions.width;
|
|
46
|
+
height = pageDimensions.height;
|
|
47
|
+
}
|
|
48
|
+
if (landscape) {
|
|
49
|
+
;
|
|
50
|
+
[width, height] = [height, width];
|
|
51
|
+
}
|
|
52
|
+
const contentWidth = width - marginLeft - marginRight;
|
|
53
|
+
const contentHeight = height - marginTop - marginBottom;
|
|
54
|
+
const boardWidthPt = (boardWidthPx * 72) / 96;
|
|
55
|
+
const boardHeightPt = (boardHeightPx * 72) / 96;
|
|
56
|
+
const widthRatio = contentWidth / boardWidthPt;
|
|
57
|
+
const heightRatio = contentHeight / boardHeightPt;
|
|
58
|
+
const ratio = Math.min(widthRatio, heightRatio);
|
|
59
|
+
const scaledBoardWidthPt = boardWidthPt * ratio;
|
|
60
|
+
const scaledBoardHeightPt = boardHeightPt * ratio;
|
|
61
|
+
const offsetX = (contentWidth - scaledBoardWidthPt) / 2 + marginLeft;
|
|
62
|
+
const offsetY = (contentHeight - scaledBoardHeightPt) / 2 + marginBottom;
|
|
63
|
+
await page.setViewport({ width: Math.round(boardWidthPx * ratio), height: Math.round(boardHeightPx * ratio) });
|
|
64
|
+
await page.setRequestInterception(true);
|
|
65
|
+
await page.setDefaultTimeout(10000);
|
|
66
|
+
page.on('console', async (msg) => {
|
|
67
|
+
console.log(`[browser ${msg.type()}] ${msg.text()}`);
|
|
68
|
+
});
|
|
69
|
+
page.on('requestfailed', request => {
|
|
70
|
+
console.log('Request failed:', request.url());
|
|
71
|
+
});
|
|
72
|
+
const protocol = 'http';
|
|
73
|
+
const host = 'localhost';
|
|
74
|
+
const port = process.env.PORT ? `:${process.env.PORT}` : '';
|
|
75
|
+
const path = '/internal-board-service-view';
|
|
76
|
+
const url = `${protocol}://${host}${port}${path}`;
|
|
77
|
+
const token = await (user === null || user === void 0 ? void 0 : user.sign());
|
|
78
|
+
page.on('request', request => {
|
|
79
|
+
if (request.url() === url) {
|
|
80
|
+
request.continue({
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
'x-things-factory-domain': domain === null || domain === void 0 ? void 0 : domain.subdomain,
|
|
85
|
+
Authorization: 'Bearer ' + token
|
|
86
|
+
},
|
|
87
|
+
postData: JSON.stringify({
|
|
88
|
+
model,
|
|
89
|
+
base
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else if (request.url().startsWith(`${protocol}://${host}${port}`)) {
|
|
94
|
+
request.continue({
|
|
95
|
+
headers: Object.assign(Object.assign({}, request.headers()), { 'x-things-factory-domain': domain === null || domain === void 0 ? void 0 : domain.subdomain, Authorization: 'Bearer ' + token })
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
request.continue();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
await page.goto(url);
|
|
103
|
+
await page.evaluate(data => {
|
|
104
|
+
//@ts-ignore
|
|
105
|
+
s.data = data;
|
|
106
|
+
return new Promise(resolve => {
|
|
107
|
+
requestAnimationFrame(() => resolve(0));
|
|
108
|
+
});
|
|
109
|
+
}, boardInput);
|
|
110
|
+
const renderTemplateSafely = (template, data) => {
|
|
111
|
+
try {
|
|
112
|
+
return ejs.render(template, data);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger.warn(`Template rendering error: ${error.message}`);
|
|
116
|
+
return template;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
header = renderTemplateSafely(header, __headless_pdf);
|
|
120
|
+
footer = renderTemplateSafely(footer, __headless_pdf);
|
|
121
|
+
// Apply header, footer, and watermark using Puppeteer
|
|
122
|
+
// Apply header, footer, and watermark using Puppeteer
|
|
123
|
+
if (header || footer || watermark) {
|
|
124
|
+
await page.evaluate(({ header, footer, watermark, isLandscape }) => {
|
|
125
|
+
const setPositioning = (element, position) => {
|
|
126
|
+
element.style.position = 'fixed';
|
|
127
|
+
element.style.left = '0';
|
|
128
|
+
element.style.width = '100%';
|
|
129
|
+
element.style.textAlign = 'center';
|
|
130
|
+
element.style.fontSize = '12px';
|
|
131
|
+
element.style.margin = '0';
|
|
132
|
+
element.style.padding = '0';
|
|
133
|
+
if (position === 'header') {
|
|
134
|
+
element.style.top = '0';
|
|
135
|
+
}
|
|
136
|
+
else if (position === 'footer') {
|
|
137
|
+
element.style.bottom = '0';
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
if (header) {
|
|
141
|
+
const headerElement = document.createElement('div');
|
|
142
|
+
headerElement.innerHTML = header;
|
|
143
|
+
setPositioning(headerElement, 'header');
|
|
144
|
+
document.body.appendChild(headerElement);
|
|
145
|
+
}
|
|
146
|
+
if (footer) {
|
|
147
|
+
const footerElement = document.createElement('div');
|
|
148
|
+
footerElement.innerHTML = footer;
|
|
149
|
+
setPositioning(footerElement, 'footer');
|
|
150
|
+
document.body.appendChild(footerElement);
|
|
151
|
+
}
|
|
152
|
+
if (watermark) {
|
|
153
|
+
const watermarkElement = document.createElement('div');
|
|
154
|
+
watermarkElement.innerHTML = watermark;
|
|
155
|
+
watermarkElement.style.position = 'fixed';
|
|
156
|
+
watermarkElement.style.top = isLandscape ? '40%' : '50%';
|
|
157
|
+
watermarkElement.style.left = '50%';
|
|
158
|
+
watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)';
|
|
159
|
+
watermarkElement.style.opacity = '0.2';
|
|
160
|
+
watermarkElement.style.fontSize = '50px';
|
|
161
|
+
watermarkElement.style.color = 'red';
|
|
162
|
+
watermarkElement.style.pointerEvents = 'none';
|
|
163
|
+
watermarkElement.style.zIndex = '9999';
|
|
164
|
+
document.body.appendChild(watermarkElement);
|
|
165
|
+
}
|
|
166
|
+
}, { header, footer, watermark, isLandscape: landscape });
|
|
167
|
+
}
|
|
168
|
+
const pageOptions = {
|
|
169
|
+
format,
|
|
170
|
+
width: `${scaledBoardWidthPt}px`,
|
|
171
|
+
height: `${scaledBoardHeightPt}px`,
|
|
172
|
+
margin: {
|
|
173
|
+
top: `${offsetY}px`,
|
|
174
|
+
right: `${offsetX}px`,
|
|
175
|
+
bottom: `${offsetY}px`,
|
|
176
|
+
left: `${offsetX}px`
|
|
177
|
+
},
|
|
178
|
+
scale,
|
|
179
|
+
printBackground,
|
|
180
|
+
landscape,
|
|
181
|
+
displayHeaderFooter: false, // Handled via Puppeteer directly
|
|
182
|
+
preferCSSPageSize
|
|
183
|
+
};
|
|
184
|
+
const contentBuffer = await page.pdf(pageOptions);
|
|
185
|
+
const contentPdfDoc = await pdf_lib_1.PDFDocument.load(contentBuffer);
|
|
186
|
+
const copiedPages = await pdfDoc.copyPages(contentPdfDoc, contentPdfDoc.getPageIndices());
|
|
187
|
+
copiedPages.forEach(page => pdfDoc.addPage(page));
|
|
188
|
+
__headless_pdf.pageCount += copiedPages.length;
|
|
189
|
+
await page.close();
|
|
190
|
+
headlessPool.release(browser);
|
|
191
|
+
return {
|
|
192
|
+
data: __headless_pdf
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (browser) {
|
|
197
|
+
await browser.close();
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
HeadlessPDFCaptureBoard.parameterSpec = [
|
|
203
|
+
{
|
|
204
|
+
type: 'scenario-step-input',
|
|
205
|
+
name: 'accessor',
|
|
206
|
+
label: 'accessor'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: 'resource-object',
|
|
210
|
+
name: 'board',
|
|
211
|
+
label: 'board',
|
|
212
|
+
property: {
|
|
213
|
+
queryName: 'boards'
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'boolean',
|
|
218
|
+
name: 'draft',
|
|
219
|
+
label: 'draft',
|
|
220
|
+
defaultValue: false,
|
|
221
|
+
description: 'Set whether to get the current working version or the last released version'
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
type: 'select',
|
|
225
|
+
name: 'format',
|
|
226
|
+
label: 'page-format',
|
|
227
|
+
property: {
|
|
228
|
+
options: [
|
|
229
|
+
{ display: '', value: '' },
|
|
230
|
+
{ display: 'A4', value: 'A4' },
|
|
231
|
+
{ display: 'A3', value: 'A3' },
|
|
232
|
+
{ display: 'Letter', value: 'Letter' },
|
|
233
|
+
{ display: 'Legal', value: 'Legal' }
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
description: 'Select the paper format for the PDF'
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
type: 'string',
|
|
240
|
+
name: 'width',
|
|
241
|
+
label: 'page-width',
|
|
242
|
+
placeholder: '(e.g., "8.5in", "21cm", "600px")',
|
|
243
|
+
description: 'Specify the width of the page (e.g., "8.5in", "21cm", "600px")'
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
type: 'string',
|
|
247
|
+
name: 'height',
|
|
248
|
+
label: 'page-height',
|
|
249
|
+
placeholder: '(e.g., "11in", "29.7cm", "800px")',
|
|
250
|
+
description: 'Specify the height of the page (e.g., "11in", "29.7cm", "800px")'
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
type: 'string',
|
|
254
|
+
name: 'marginTop',
|
|
255
|
+
label: 'page-margin-top',
|
|
256
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
257
|
+
description: 'Set the top margin for the page'
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
type: 'string',
|
|
261
|
+
name: 'marginBottom',
|
|
262
|
+
label: 'page-margin-bottom',
|
|
263
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
264
|
+
description: 'Set the bottom margin for the page'
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
type: 'string',
|
|
268
|
+
name: 'marginLeft',
|
|
269
|
+
label: 'page-margin-left',
|
|
270
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
271
|
+
description: 'Set the left margin for the page'
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
type: 'string',
|
|
275
|
+
name: 'marginRight',
|
|
276
|
+
label: 'page-margin-right',
|
|
277
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
278
|
+
description: 'Set the right margin for the page'
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
type: 'number',
|
|
282
|
+
name: 'scale',
|
|
283
|
+
label: 'page-scale',
|
|
284
|
+
defaultValue: 1,
|
|
285
|
+
description: 'Set the scale of the page content (default is 1)'
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
type: 'boolean',
|
|
289
|
+
name: 'printBackground',
|
|
290
|
+
label: 'print-background',
|
|
291
|
+
defaultValue: true,
|
|
292
|
+
description: 'Include background graphics when printing the page'
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
type: 'boolean',
|
|
296
|
+
name: 'landscape',
|
|
297
|
+
label: 'landscape',
|
|
298
|
+
defaultValue: false,
|
|
299
|
+
description: 'Print the PDF in landscape orientation'
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
type: 'boolean',
|
|
303
|
+
name: 'preferCSSPageSize',
|
|
304
|
+
label: 'prefer-css-page-size',
|
|
305
|
+
defaultValue: false,
|
|
306
|
+
description: 'Whether to prefer the CSS-defined page size over the given width and height'
|
|
307
|
+
}
|
|
308
|
+
];
|
|
309
|
+
HeadlessPDFCaptureBoard.help = 'integration/task/headless-pdf-capture-board';
|
|
310
|
+
integration_base_1.TaskRegistry.registerTaskHandler('headless-pdf-capture-board', HeadlessPDFCaptureBoard);
|
|
311
|
+
//# sourceMappingURL=headless-pdf-capture-board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-pdf-capture-board.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-capture-board.ts"],"names":[],"mappings":"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","sourcesContent":["import * as ejs from 'ejs'\nimport { TaskRegistry } from '@things-factory/integration-base'\nimport { ConnectionManager } from '@things-factory/integration-base'\nimport { access } from '@things-factory/utils'\nimport { BoardFunc } from '@things-factory/board-service'\nimport { PDFDocument } from 'pdf-lib'\n\nconst PAGE_FORMATS = {\n A4: { width: 595.28, height: 841.89 },\n A3: { width: 841.89, height: 1190.55 },\n Letter: { width: 612, height: 792 },\n Legal: { width: 612, height: 1008 }\n}\n\nasync function HeadlessPDFCaptureBoard(step, context) {\n var { connection: connectionName, params: stepOptions } = step\n var {\n accessor,\n board,\n draft,\n format = 'A4', // Set default value to A4\n width,\n height,\n marginTop = 0,\n marginBottom = 0,\n marginLeft = 0,\n marginRight = 0,\n scale,\n printBackground,\n landscape,\n preferCSSPageSize\n } = stepOptions || {}\n var { domain, data, user, logger, __headless_pdf } = context\n\n if (!__headless_pdf) {\n throw new Error('It requires headless-pdf-open to be performed first')\n }\n\n if (!board || !board.id) {\n throw new Error('The board property must be set')\n }\n\n var { pdfDoc, header, footer, watermark } = __headless_pdf\n\n var headlessPool = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n let browser\n\n try {\n browser = await headlessPool.acquire()\n const page = await browser.newPage()\n\n const boardInput = access(accessor, data)\n\n var { model, base } = await BoardFunc.headlessModel({ domain, id: board.id, model }, draft)\n const [fontsToUse, fontStyles] = await BoardFunc.fonts(domain)\n\n model.fonts = fontsToUse\n model.fontStyles = fontStyles\n\n var { width: boardWidthPx, height: boardHeightPx } = model\n\n if (preferCSSPageSize) {\n width = undefined\n height = undefined\n } else if (PAGE_FORMATS[format]) {\n const pageDimensions = PAGE_FORMATS[format]\n width = pageDimensions.width\n height = pageDimensions.height\n }\n\n if (landscape) {\n ;[width, height] = [height, width]\n }\n\n const contentWidth = width - marginLeft - marginRight\n const contentHeight = height - marginTop - marginBottom\n\n const boardWidthPt = (boardWidthPx * 72) / 96\n const boardHeightPt = (boardHeightPx * 72) / 96\n\n const widthRatio = contentWidth / boardWidthPt\n const heightRatio = contentHeight / boardHeightPt\n const ratio = Math.min(widthRatio, heightRatio)\n\n const scaledBoardWidthPt = boardWidthPt * ratio\n const scaledBoardHeightPt = boardHeightPt * ratio\n\n const offsetX = (contentWidth - scaledBoardWidthPt) / 2 + marginLeft\n const offsetY = (contentHeight - scaledBoardHeightPt) / 2 + marginBottom\n\n await page.setViewport({ width: Math.round(boardWidthPx * ratio), height: Math.round(boardHeightPx * ratio) })\n await page.setRequestInterception(true)\n await page.setDefaultTimeout(10000)\n\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n const protocol = 'http'\n const host = 'localhost'\n const port = process.env.PORT ? `:${process.env.PORT}` : ''\n const path = '/internal-board-service-view'\n const url = `${protocol}://${host}${port}${path}`\n\n const token = await user?.sign()\n\n page.on('request', request => {\n if (request.url() === url) {\n request.continue({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n },\n postData: JSON.stringify({\n model,\n base\n })\n })\n } else if (request.url().startsWith(`${protocol}://${host}${port}`)) {\n request.continue({\n headers: {\n ...request.headers(),\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n }\n })\n } else {\n request.continue()\n }\n })\n\n await page.goto(url)\n\n await page.evaluate(data => {\n //@ts-ignore\n s.data = data\n return new Promise(resolve => {\n requestAnimationFrame(() => resolve(0))\n })\n }, boardInput)\n\n const renderTemplateSafely = (template, data) => {\n try {\n return ejs.render(template, data)\n } catch (error) {\n logger.warn(`Template rendering error: ${error.message}`)\n return template\n }\n }\n\n header = renderTemplateSafely(header, __headless_pdf)\n footer = renderTemplateSafely(footer, __headless_pdf)\n\n // Apply header, footer, and watermark using Puppeteer\n // Apply header, footer, and watermark using Puppeteer\n if (header || footer || watermark) {\n await page.evaluate(\n ({ header, footer, watermark, isLandscape }) => {\n const setPositioning = (element, position) => {\n element.style.position = 'fixed'\n element.style.left = '0'\n element.style.width = '100%'\n element.style.textAlign = 'center'\n element.style.fontSize = '12px'\n element.style.margin = '0'\n element.style.padding = '0'\n if (position === 'header') {\n element.style.top = '0'\n } else if (position === 'footer') {\n element.style.bottom = '0'\n }\n }\n\n if (header) {\n const headerElement = document.createElement('div')\n headerElement.innerHTML = header\n setPositioning(headerElement, 'header')\n document.body.appendChild(headerElement)\n }\n\n if (footer) {\n const footerElement = document.createElement('div')\n footerElement.innerHTML = footer\n setPositioning(footerElement, 'footer')\n document.body.appendChild(footerElement)\n }\n\n if (watermark) {\n const watermarkElement = document.createElement('div')\n watermarkElement.innerHTML = watermark\n watermarkElement.style.position = 'fixed'\n watermarkElement.style.top = isLandscape ? '40%' : '50%'\n watermarkElement.style.left = '50%'\n watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)'\n watermarkElement.style.opacity = '0.2'\n watermarkElement.style.fontSize = '50px'\n watermarkElement.style.color = 'red'\n watermarkElement.style.pointerEvents = 'none'\n watermarkElement.style.zIndex = '9999'\n document.body.appendChild(watermarkElement)\n }\n },\n { header, footer, watermark, isLandscape: landscape }\n )\n }\n\n const pageOptions = {\n format,\n width: `${scaledBoardWidthPt}px`,\n height: `${scaledBoardHeightPt}px`,\n margin: {\n top: `${offsetY}px`,\n right: `${offsetX}px`,\n bottom: `${offsetY}px`,\n left: `${offsetX}px`\n },\n scale,\n printBackground,\n landscape,\n displayHeaderFooter: false, // Handled via Puppeteer directly\n preferCSSPageSize\n }\n\n const contentBuffer = await page.pdf(pageOptions)\n const contentPdfDoc = await PDFDocument.load(contentBuffer)\n const copiedPages = await pdfDoc.copyPages(contentPdfDoc, contentPdfDoc.getPageIndices())\n\n copiedPages.forEach(page => pdfDoc.addPage(page))\n\n __headless_pdf.pageCount += copiedPages.length\n\n await page.close()\n headlessPool.release(browser)\n\n return {\n data: __headless_pdf\n }\n } catch (error) {\n if (browser) {\n await browser.close()\n }\n\n throw error\n }\n}\n\nHeadlessPDFCaptureBoard.parameterSpec = [\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n },\n {\n type: 'resource-object',\n name: 'board',\n label: 'board',\n property: {\n queryName: 'boards'\n }\n },\n {\n type: 'boolean',\n name: 'draft',\n label: 'draft',\n defaultValue: false,\n description: 'Set whether to get the current working version or the last released version'\n },\n {\n type: 'select',\n name: 'format',\n label: 'page-format',\n property: {\n options: [\n { display: '', value: '' },\n { display: 'A4', value: 'A4' },\n { display: 'A3', value: 'A3' },\n { display: 'Letter', value: 'Letter' },\n { display: 'Legal', value: 'Legal' }\n ]\n },\n description: 'Select the paper format for the PDF'\n },\n {\n type: 'string',\n name: 'width',\n label: 'page-width',\n placeholder: '(e.g., \"8.5in\", \"21cm\", \"600px\")',\n description: 'Specify the width of the page (e.g., \"8.5in\", \"21cm\", \"600px\")'\n },\n {\n type: 'string',\n name: 'height',\n label: 'page-height',\n placeholder: '(e.g., \"11in\", \"29.7cm\", \"800px\")',\n description: 'Specify the height of the page (e.g., \"11in\", \"29.7cm\", \"800px\")'\n },\n {\n type: 'string',\n name: 'marginTop',\n label: 'page-margin-top',\n placeholder: '(e.g., \"0.5in\", \"1cm\", \"100px\")',\n description: 'Set the top margin for the page'\n },\n {\n type: 'string',\n name: 'marginBottom',\n label: 'page-margin-bottom',\n placeholder: '(e.g., \"0.5in\", \"1cm\", \"100px\")',\n description: 'Set the bottom margin for the page'\n },\n {\n type: 'string',\n name: 'marginLeft',\n label: 'page-margin-left',\n placeholder: '(e.g., \"0.5in\", \"1cm\", \"100px\")',\n description: 'Set the left margin for the page'\n },\n {\n type: 'string',\n name: 'marginRight',\n label: 'page-margin-right',\n placeholder: '(e.g., \"0.5in\", \"1cm\", \"100px\")',\n description: 'Set the right margin for the page'\n },\n {\n type: 'number',\n name: 'scale',\n label: 'page-scale',\n defaultValue: 1,\n description: 'Set the scale of the page content (default is 1)'\n },\n {\n type: 'boolean',\n name: 'printBackground',\n label: 'print-background',\n defaultValue: true,\n description: 'Include background graphics when printing the page'\n },\n {\n type: 'boolean',\n name: 'landscape',\n label: 'landscape',\n defaultValue: false,\n description: 'Print the PDF in landscape orientation'\n },\n {\n type: 'boolean',\n name: 'preferCSSPageSize',\n label: 'prefer-css-page-size',\n defaultValue: false,\n description: 'Whether to prefer the CSS-defined page size over the given width and height'\n }\n]\n\nHeadlessPDFCaptureBoard.help = 'integration/task/headless-pdf-capture-board'\n\nTaskRegistry.registerTaskHandler('headless-pdf-capture-board', HeadlessPDFCaptureBoard)\n"]}
|
|
@@ -2,26 +2,83 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const ejs = tslib_1.__importStar(require("ejs"));
|
|
5
|
-
const pdf_lib_1 = require("pdf-lib");
|
|
6
5
|
const integration_base_1 = require("@things-factory/integration-base");
|
|
7
6
|
const integration_base_2 = require("@things-factory/integration-base");
|
|
7
|
+
const utils_1 = require("@things-factory/utils");
|
|
8
|
+
const pdf_lib_1 = require("pdf-lib");
|
|
8
9
|
async function HeadlessPDFCapture(step, context) {
|
|
9
10
|
var { connection: connectionName, params: stepOptions } = step;
|
|
10
|
-
var { htmlContent, format, width, height,
|
|
11
|
-
var { domain, data, __headless_pdf } = context;
|
|
11
|
+
var { accessor, htmlContent, format, width, height, marginTop, marginBottom, marginLeft, marginRight, scale, printBackground, landscape, preferCSSPageSize } = stepOptions || {};
|
|
12
|
+
var { domain, data, logger, __headless_pdf } = context;
|
|
12
13
|
if (!__headless_pdf) {
|
|
13
|
-
throw new Error('
|
|
14
|
+
throw new Error('It requires headless-pdf-open to be performed first');
|
|
14
15
|
}
|
|
15
|
-
var {
|
|
16
|
+
var { pdfDoc, header, footer, watermark } = __headless_pdf;
|
|
16
17
|
var headlessPool = integration_base_2.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
17
18
|
let browser;
|
|
18
19
|
try {
|
|
19
|
-
// Puppeteer를 사용하여 브라우저와 페이지 인스턴스를 생성
|
|
20
20
|
browser = await headlessPool.acquire();
|
|
21
21
|
const page = await browser.newPage();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const templateInput = (0, utils_1.access)(accessor, data);
|
|
23
|
+
const renderTemplateSafely = (template, data) => {
|
|
24
|
+
try {
|
|
25
|
+
return ejs.render(template, data);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.warn(`Template rendering error: ${error.message}`);
|
|
29
|
+
return template;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const renderedHtmlContent = renderTemplateSafely(htmlContent, templateInput);
|
|
33
|
+
await page.setContent(renderedHtmlContent);
|
|
34
|
+
header = renderTemplateSafely(header, __headless_pdf);
|
|
35
|
+
footer = renderTemplateSafely(footer, __headless_pdf);
|
|
36
|
+
// Apply header, footer, and watermark using Puppeteer
|
|
37
|
+
if (header || footer || watermark) {
|
|
38
|
+
await page.evaluate(({ header, footer, watermark, isLandscape }) => {
|
|
39
|
+
const setPositioning = (element, position) => {
|
|
40
|
+
element.style.position = 'fixed';
|
|
41
|
+
element.style.left = '0';
|
|
42
|
+
element.style.width = '100%';
|
|
43
|
+
element.style.textAlign = 'center';
|
|
44
|
+
element.style.fontSize = '12px';
|
|
45
|
+
element.style.margin = '0';
|
|
46
|
+
element.style.padding = '0';
|
|
47
|
+
if (position === 'header') {
|
|
48
|
+
element.style.top = '0';
|
|
49
|
+
}
|
|
50
|
+
else if (position === 'footer') {
|
|
51
|
+
element.style.bottom = '0';
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (header) {
|
|
55
|
+
const headerElement = document.createElement('div');
|
|
56
|
+
headerElement.innerHTML = header;
|
|
57
|
+
setPositioning(headerElement, 'header');
|
|
58
|
+
document.body.appendChild(headerElement);
|
|
59
|
+
}
|
|
60
|
+
if (footer) {
|
|
61
|
+
const footerElement = document.createElement('div');
|
|
62
|
+
footerElement.innerHTML = footer;
|
|
63
|
+
setPositioning(footerElement, 'footer');
|
|
64
|
+
document.body.appendChild(footerElement);
|
|
65
|
+
}
|
|
66
|
+
if (watermark) {
|
|
67
|
+
const watermarkElement = document.createElement('div');
|
|
68
|
+
watermarkElement.innerHTML = watermark;
|
|
69
|
+
watermarkElement.style.position = 'fixed';
|
|
70
|
+
watermarkElement.style.top = isLandscape ? '40%' : '50%';
|
|
71
|
+
watermarkElement.style.left = '50%';
|
|
72
|
+
watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)';
|
|
73
|
+
watermarkElement.style.opacity = '0.2';
|
|
74
|
+
watermarkElement.style.fontSize = '50px';
|
|
75
|
+
watermarkElement.style.color = 'red';
|
|
76
|
+
watermarkElement.style.pointerEvents = 'none';
|
|
77
|
+
watermarkElement.style.zIndex = '9999';
|
|
78
|
+
document.body.appendChild(watermarkElement);
|
|
79
|
+
}
|
|
80
|
+
}, { header, footer, watermark, isLandscape: landscape });
|
|
81
|
+
}
|
|
25
82
|
if (preferCSSPageSize) {
|
|
26
83
|
width = undefined;
|
|
27
84
|
height = undefined;
|
|
@@ -30,56 +87,23 @@ async function HeadlessPDFCapture(step, context) {
|
|
|
30
87
|
format,
|
|
31
88
|
width,
|
|
32
89
|
height,
|
|
33
|
-
margin
|
|
90
|
+
margin: {
|
|
91
|
+
top: marginTop,
|
|
92
|
+
right: marginRight,
|
|
93
|
+
bottom: marginBottom,
|
|
94
|
+
left: marginLeft
|
|
95
|
+
},
|
|
34
96
|
scale,
|
|
35
97
|
printBackground,
|
|
36
98
|
landscape,
|
|
37
|
-
displayHeaderFooter,
|
|
38
|
-
headerTemplate: displayHeaderFooter ? headerTemplate : undefined,
|
|
39
|
-
footerTemplate: displayHeaderFooter ? footerTemplate : undefined,
|
|
99
|
+
displayHeaderFooter: false, // Handled via Puppeteer directly
|
|
40
100
|
preferCSSPageSize
|
|
41
101
|
};
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
pages.forEach((newPage, index) => {
|
|
48
|
-
const currentPageNumber = pageCount + index + 1;
|
|
49
|
-
// 헤더와 푸터 렌더링 (페이지 번호 및 총 페이지 수 포함)
|
|
50
|
-
if (displayHeaderFooter && (headerTemplateFromContext || footerTemplateFromContext)) {
|
|
51
|
-
const renderedHeader = headerTemplateFromContext
|
|
52
|
-
? ejs.render(headerTemplateFromContext, Object.assign(Object.assign({}, data), { pageNumber: currentPageNumber, totalPages: pageCount + pages.length }))
|
|
53
|
-
: '';
|
|
54
|
-
const renderedFooter = footerTemplateFromContext
|
|
55
|
-
? ejs.render(footerTemplateFromContext, Object.assign(Object.assign({}, data), { pageNumber: currentPageNumber, totalPages: pageCount + pages.length }))
|
|
56
|
-
: '';
|
|
57
|
-
// Header/Footer 그리기
|
|
58
|
-
if (renderedHeader) {
|
|
59
|
-
newPage.drawText(renderedHeader, { x: 50, y: newPage.getHeight() - 30, size: 12, color: (0, pdf_lib_1.rgb)(0, 0, 0) });
|
|
60
|
-
}
|
|
61
|
-
if (renderedFooter) {
|
|
62
|
-
newPage.drawText(renderedFooter, { x: 50, y: 30, size: 12, color: (0, pdf_lib_1.rgb)(0, 0, 0) });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// 워터마크 추가
|
|
66
|
-
if (watermark) {
|
|
67
|
-
const fontSize = 50;
|
|
68
|
-
const pageWidth = newPage.getWidth();
|
|
69
|
-
const pageHeight = newPage.getHeight();
|
|
70
|
-
newPage.drawText(watermark, {
|
|
71
|
-
x: pageWidth / 2 - (fontSize * watermark.length) / 4,
|
|
72
|
-
y: pageHeight / 2 - fontSize / 2,
|
|
73
|
-
size: fontSize,
|
|
74
|
-
color: (0, pdf_lib_1.rgb)(0.75, 0.75, 0.75),
|
|
75
|
-
rotate: (0, pdf_lib_1.degrees)(-45),
|
|
76
|
-
opacity: 0.5
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
pdf.addPage(newPage);
|
|
80
|
-
});
|
|
81
|
-
// 페이지 카운트 업데이트
|
|
82
|
-
__headless_pdf.pageCount += pages.length;
|
|
102
|
+
const contentBuffer = await page.pdf(pageOptions);
|
|
103
|
+
const contentPdfDoc = await pdf_lib_1.PDFDocument.load(contentBuffer);
|
|
104
|
+
const copiedPages = await pdfDoc.copyPages(contentPdfDoc, contentPdfDoc.getPageIndices());
|
|
105
|
+
copiedPages.forEach(page => pdfDoc.addPage(page));
|
|
106
|
+
__headless_pdf.pageCount += copiedPages.length;
|
|
83
107
|
await page.close();
|
|
84
108
|
headlessPool.release(browser);
|
|
85
109
|
return {
|
|
@@ -94,6 +118,11 @@ async function HeadlessPDFCapture(step, context) {
|
|
|
94
118
|
}
|
|
95
119
|
}
|
|
96
120
|
HeadlessPDFCapture.parameterSpec = [
|
|
121
|
+
{
|
|
122
|
+
type: 'scenario-step-input',
|
|
123
|
+
name: 'accessor',
|
|
124
|
+
label: 'accessor'
|
|
125
|
+
},
|
|
97
126
|
{
|
|
98
127
|
type: 'textarea',
|
|
99
128
|
name: 'htmlContent',
|
|
@@ -105,6 +134,7 @@ HeadlessPDFCapture.parameterSpec = [
|
|
|
105
134
|
label: 'page-format',
|
|
106
135
|
property: {
|
|
107
136
|
options: [
|
|
137
|
+
{ display: '', value: '' },
|
|
108
138
|
{ display: 'A4', value: 'A4' },
|
|
109
139
|
{ display: 'A3', value: 'A3' },
|
|
110
140
|
{ display: 'Letter', value: 'Letter' },
|
|
@@ -128,21 +158,37 @@ HeadlessPDFCapture.parameterSpec = [
|
|
|
128
158
|
description: 'Specify the height of the page (e.g., "11in", "29.7cm", "800px")'
|
|
129
159
|
},
|
|
130
160
|
{
|
|
131
|
-
type: '
|
|
132
|
-
name: '
|
|
133
|
-
label: 'page-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
type: 'string',
|
|
162
|
+
name: 'marginTop',
|
|
163
|
+
label: 'page-margin-top',
|
|
164
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
165
|
+
description: 'Set the top margin for the page'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: 'string',
|
|
169
|
+
name: 'marginBottom',
|
|
170
|
+
label: 'page-margin-bottom',
|
|
171
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
172
|
+
description: 'Set the bottom margin for the page'
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: 'string',
|
|
176
|
+
name: 'marginLeft',
|
|
177
|
+
label: 'page-margin-left',
|
|
178
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
179
|
+
description: 'Set the left margin for the page'
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: 'string',
|
|
183
|
+
name: 'marginRight',
|
|
184
|
+
label: 'page-margin-right',
|
|
185
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
186
|
+
description: 'Set the right margin for the page'
|
|
141
187
|
},
|
|
142
188
|
{
|
|
143
189
|
type: 'number',
|
|
144
190
|
name: 'scale',
|
|
145
|
-
label: 'scale',
|
|
191
|
+
label: 'page-scale',
|
|
146
192
|
defaultValue: 1,
|
|
147
193
|
description: 'Set the scale of the page content (default is 1)'
|
|
148
194
|
},
|
|
@@ -160,13 +206,6 @@ HeadlessPDFCapture.parameterSpec = [
|
|
|
160
206
|
defaultValue: false,
|
|
161
207
|
description: 'Print the PDF in landscape orientation'
|
|
162
208
|
},
|
|
163
|
-
{
|
|
164
|
-
type: 'boolean',
|
|
165
|
-
name: 'displayHeaderFooter',
|
|
166
|
-
label: 'display-header/footer',
|
|
167
|
-
defaultValue: false,
|
|
168
|
-
description: 'Display header and footer in the PDF'
|
|
169
|
-
},
|
|
170
209
|
{
|
|
171
210
|
type: 'boolean',
|
|
172
211
|
name: 'preferCSSPageSize',
|