@things-factory/integration-headless 7.0.49 → 7.0.54

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.
Files changed (28) hide show
  1. package/dist-server/engine/task/headless-pdf-capture-board.d.ts +59 -1
  2. package/dist-server/engine/task/headless-pdf-capture-board.js +29 -250
  3. package/dist-server/engine/task/headless-pdf-capture-board.js.map +1 -1
  4. package/dist-server/engine/task/headless-pdf-capture-markdown.d.ts +52 -0
  5. package/dist-server/engine/task/headless-pdf-capture-markdown.js +38 -0
  6. package/dist-server/engine/task/headless-pdf-capture-markdown.js.map +1 -0
  7. package/dist-server/engine/task/headless-pdf-capture.d.ts +52 -1
  8. package/dist-server/engine/task/headless-pdf-capture.js +13 -196
  9. package/dist-server/engine/task/headless-pdf-capture.js.map +1 -1
  10. package/dist-server/engine/task/headless-pdf-open.js +27 -257
  11. package/dist-server/engine/task/headless-pdf-open.js.map +1 -1
  12. package/dist-server/engine/task/headless-pdf-save.js +32 -20
  13. package/dist-server/engine/task/headless-pdf-save.js.map +1 -1
  14. package/dist-server/engine/task/index.d.ts +1 -0
  15. package/dist-server/engine/task/index.js +1 -0
  16. package/dist-server/engine/task/index.js.map +1 -1
  17. package/dist-server/engine/task/pdf-capture-util.d.ts +96 -0
  18. package/dist-server/engine/task/pdf-capture-util.js +252 -0
  19. package/dist-server/engine/task/pdf-capture-util.js.map +1 -0
  20. package/dist-server/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +2 -2
  22. package/server/engine/task/headless-pdf-capture-board.ts +41 -293
  23. package/server/engine/task/headless-pdf-capture-markdown.ts +41 -0
  24. package/server/engine/task/headless-pdf-capture.ts +12 -226
  25. package/server/engine/task/headless-pdf-open.ts +28 -292
  26. package/server/engine/task/headless-pdf-save.ts +34 -21
  27. package/server/engine/task/index.ts +1 -0
  28. package/server/engine/task/pdf-capture-util.ts +319 -0
@@ -1,250 +1,36 @@
1
- import * as ejs from 'ejs'
2
1
  import { TaskRegistry } from '@things-factory/integration-base'
3
- import { ConnectionManager } from '@things-factory/integration-base'
4
2
  import { access } from '@things-factory/utils'
5
- import { PDFDocument } from 'pdf-lib'
6
3
 
7
- async function HeadlessPDFCapture(step, context) {
8
- var { connection: connectionName, params: stepOptions } = step
9
- var {
10
- accessor,
11
- htmlContent,
12
- format,
13
- width,
14
- height,
15
- marginTop,
16
- marginBottom,
17
- marginLeft,
18
- marginRight,
19
- scale,
20
- printBackground,
21
- landscape,
22
- preferCSSPageSize
23
- } = stepOptions || {}
24
- var { domain, data, logger, __headless_pdf } = context
4
+ import { PDFCaptureUtil, getCommonParameterSpec } from './pdf-capture-util'
25
5
 
26
- if (!__headless_pdf) {
27
- throw new Error('It requires headless-pdf-open to be performed first')
28
- }
29
-
30
- var { pdfDoc, header, footer, watermark } = __headless_pdf
31
-
32
- var headlessPool = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
33
- let browser
6
+ export async function HeadlessPDFCapture(step, context) {
7
+ const pdfUtil = new PDFCaptureUtil(context)
8
+ await pdfUtil.initBrowser(step.connection)
34
9
 
35
10
  try {
36
- browser = await headlessPool.acquire()
37
- const page = await browser.newPage()
38
-
39
- const templateInput = access(accessor, data)
40
-
41
- const renderTemplateSafely = (template, data) => {
42
- try {
43
- return ejs.render(template, data)
44
- } catch (error) {
45
- logger.warn(`Template rendering error: ${error.message}`)
46
- return template
47
- }
48
- }
49
-
50
- const renderedHtmlContent = renderTemplateSafely(htmlContent, templateInput)
51
- await page.setContent(renderedHtmlContent)
52
-
53
- header = renderTemplateSafely(header, __headless_pdf)
54
- footer = renderTemplateSafely(footer, __headless_pdf)
55
-
56
- // Apply header, footer, and watermark using Puppeteer
57
- if (header || footer || watermark) {
58
- await page.evaluate(
59
- ({ header, footer, watermark, isLandscape }) => {
60
- const setPositioning = (element, position) => {
61
- element.style.position = 'fixed'
62
- element.style.left = '0'
63
- element.style.width = '100%'
64
- element.style.textAlign = 'center'
65
- element.style.fontSize = '12px'
66
- element.style.margin = '0'
67
- element.style.padding = '0'
68
- if (position === 'header') {
69
- element.style.top = '0'
70
- } else if (position === 'footer') {
71
- element.style.bottom = '0'
72
- }
73
- }
74
-
75
- if (header) {
76
- const headerElement = document.createElement('div')
77
- headerElement.innerHTML = header
78
- setPositioning(headerElement, 'header')
79
- document.body.appendChild(headerElement)
80
- }
81
-
82
- if (footer) {
83
- const footerElement = document.createElement('div')
84
- footerElement.innerHTML = footer
85
- setPositioning(footerElement, 'footer')
86
- document.body.appendChild(footerElement)
87
- }
88
-
89
- if (watermark) {
90
- const watermarkElement = document.createElement('div')
91
- watermarkElement.innerHTML = watermark
92
- watermarkElement.style.position = 'fixed'
93
- watermarkElement.style.top = isLandscape ? '40%' : '50%'
94
- watermarkElement.style.left = '50%'
95
- watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)'
96
- watermarkElement.style.opacity = '0.2'
97
- watermarkElement.style.fontSize = '50px'
98
- watermarkElement.style.color = 'red'
99
- watermarkElement.style.pointerEvents = 'none'
100
- watermarkElement.style.zIndex = '9999'
101
- document.body.appendChild(watermarkElement)
102
- }
103
- },
104
- { header, footer, watermark, isLandscape: landscape }
105
- )
106
- }
107
-
108
- if (preferCSSPageSize) {
109
- width = undefined
110
- height = undefined
111
- }
112
-
113
- const pageOptions = {
114
- format,
115
- width,
116
- height,
117
- margin: {
118
- top: marginTop,
119
- right: marginRight,
120
- bottom: marginBottom,
121
- left: marginLeft
122
- },
123
- scale,
124
- printBackground,
125
- landscape,
126
- displayHeaderFooter: false, // Handled via Puppeteer directly
127
- preferCSSPageSize
128
- }
11
+ const { accessor, htmlContent } = step.params
12
+ const templateInput = access(accessor, context.data)
129
13
 
130
- const contentBuffer = await page.pdf(pageOptions)
131
- const contentPdfDoc = await PDFDocument.load(contentBuffer)
132
- const copiedPages = await pdfDoc.copyPages(contentPdfDoc, contentPdfDoc.getPageIndices())
14
+ const renderedHtmlContent = pdfUtil.renderTemplate(htmlContent, templateInput)
133
15
 
134
- copiedPages.forEach(page => pdfDoc.addPage(page))
135
-
136
- __headless_pdf.pageCount += copiedPages.length
137
-
138
- await page.close()
139
- headlessPool.release(browser)
16
+ await pdfUtil.processPageAndGeneratePDF(step.params, renderedHtmlContent)
140
17
 
141
18
  return {
142
- data: __headless_pdf
19
+ data: context.__headless_pdf
143
20
  }
144
21
  } catch (error) {
145
- if (browser) {
146
- await browser.close()
147
- }
148
-
149
22
  throw error
23
+ } finally {
24
+ await pdfUtil.closeBrowser()
150
25
  }
151
26
  }
152
27
 
153
28
  HeadlessPDFCapture.parameterSpec = [
154
- {
155
- type: 'scenario-step-input',
156
- name: 'accessor',
157
- label: 'accessor'
158
- },
29
+ ...getCommonParameterSpec(),
159
30
  {
160
31
  type: 'textarea',
161
32
  name: 'htmlContent',
162
33
  label: 'html-content'
163
- },
164
- {
165
- type: 'select',
166
- name: 'format',
167
- label: 'page-format',
168
- property: {
169
- options: [
170
- { display: '', value: '' },
171
- { display: 'A4', value: 'A4' },
172
- { display: 'A3', value: 'A3' },
173
- { display: 'Letter', value: 'Letter' },
174
- { display: 'Legal', value: 'Legal' }
175
- ]
176
- },
177
- description: 'Select the paper format for the PDF'
178
- },
179
- {
180
- type: 'string',
181
- name: 'width',
182
- label: 'page-width',
183
- placeholder: '(e.g., "8.5in", "21cm", "600px")',
184
- description: 'Specify the width of the page (e.g., "8.5in", "21cm", "600px")'
185
- },
186
- {
187
- type: 'string',
188
- name: 'height',
189
- label: 'page-height',
190
- placeholder: '(e.g., "11in", "29.7cm", "800px")',
191
- description: 'Specify the height of the page (e.g., "11in", "29.7cm", "800px")'
192
- },
193
- {
194
- type: 'string',
195
- name: 'marginTop',
196
- label: 'page-margin-top',
197
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
198
- description: 'Set the top margin for the page'
199
- },
200
- {
201
- type: 'string',
202
- name: 'marginBottom',
203
- label: 'page-margin-bottom',
204
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
205
- description: 'Set the bottom margin for the page'
206
- },
207
- {
208
- type: 'string',
209
- name: 'marginLeft',
210
- label: 'page-margin-left',
211
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
212
- description: 'Set the left margin for the page'
213
- },
214
- {
215
- type: 'string',
216
- name: 'marginRight',
217
- label: 'page-margin-right',
218
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
219
- description: 'Set the right margin for the page'
220
- },
221
- {
222
- type: 'number',
223
- name: 'scale',
224
- label: 'page-scale',
225
- defaultValue: 1,
226
- description: 'Set the scale of the page content (default is 1)'
227
- },
228
- {
229
- type: 'boolean',
230
- name: 'printBackground',
231
- label: 'print-background',
232
- defaultValue: true,
233
- description: 'Include background graphics when printing the page'
234
- },
235
- {
236
- type: 'boolean',
237
- name: 'landscape',
238
- label: 'landscape',
239
- defaultValue: false,
240
- description: 'Print the PDF in landscape orientation'
241
- },
242
- {
243
- type: 'boolean',
244
- name: 'preferCSSPageSize',
245
- label: 'prefer-css-page-size',
246
- defaultValue: false,
247
- description: 'Whether to prefer the CSS-defined page size over the given width and height'
248
34
  }
249
35
  ]
250
36
 
@@ -1,243 +1,64 @@
1
- import * as ejs from 'ejs'
2
1
  import { PDFDocument } from 'pdf-lib'
3
2
  import { TaskRegistry } from '@things-factory/integration-base'
4
- import { ConnectionManager } from '@things-factory/integration-base'
5
3
  import { access } from '@things-factory/utils'
4
+ import { PDFCaptureUtil, getCommonParameterSpec } from './pdf-capture-util'
6
5
 
7
6
  async function HeadlessPDFOpen(step, context) {
8
- var { connection: connectionName, params: stepOptions } = step
9
- var {
10
- accessor,
11
- coverPage,
12
- lastPage,
7
+ const { connection: connectionName, params: stepOptions } = step
8
+ const { accessor, coverPage, lastPage, header, footer, watermark, fileName } = stepOptions || {}
9
+
10
+ const { data } = context
11
+
12
+ // Create a new PDF document using pdf-lib
13
+ const pdfDoc = await PDFDocument.create()
14
+
15
+ const pdfInfo = {
16
+ pdfDoc,
13
17
  header,
14
18
  footer,
15
19
  watermark,
16
20
  fileName,
17
- format,
18
- width,
19
- height,
20
- marginTop,
21
- marginBottom,
22
- marginLeft,
23
- marginRight,
24
- scale,
25
- printBackground,
26
- landscape,
27
- preferCSSPageSize
28
- } = stepOptions || {}
29
- const { data, logger } = context
21
+ pageCount: 0
22
+ } as any
30
23
 
31
- const headlessPool = ConnectionManager.getConnectionInstanceByName(context.domain, connectionName)
32
- let browser
24
+ context.__headless_pdf = pdfInfo
33
25
 
34
- // Create a new PDF document using pdf-lib
35
- const pdfDoc = await PDFDocument.create()
26
+ const pdfUtil = new PDFCaptureUtil(context)
27
+ await pdfUtil.initBrowser(connectionName)
36
28
 
37
29
  try {
38
- browser = await headlessPool.acquire()
39
- const page = await browser.newPage()
40
-
41
30
  const templateInput = access(accessor, data)
42
31
 
43
- const renderTemplateSafely = (template, data) => {
44
- try {
45
- return ejs.render(template, data)
46
- } catch (error) {
47
- logger.warn(`Template rendering error: ${error.message}`)
48
- return template
49
- }
50
- }
51
-
52
32
  // Convert Cover Page to PDF and add it to the document (if provided)
53
33
  if (coverPage) {
54
- const renderedCoverPage = renderTemplateSafely(coverPage, templateInput)
55
-
56
- await page.setContent(renderedCoverPage)
57
-
58
- // Apply header, footer, and watermark using Puppeteer
59
- // Apply header, footer, and watermark using Puppeteer
60
- if (header || footer || watermark) {
61
- await page.evaluate(
62
- ({ header, footer, watermark, isLandscape }) => {
63
- const setPositioning = (element, position) => {
64
- element.style.position = 'fixed'
65
- element.style.left = '0'
66
- element.style.width = '100%'
67
- element.style.textAlign = 'center'
68
- element.style.fontSize = '12px'
69
- if (position === 'header') {
70
- element.style.top = '0'
71
- } else if (position === 'footer') {
72
- element.style.bottom = '0'
73
- }
74
- }
75
-
76
- // if (header) {
77
- // const headerElement = document.createElement('div')
78
- // headerElement.innerHTML = header
79
- // setPositioning(headerElement, 'header')
80
- // document.body.appendChild(headerElement)
81
- // }
82
-
83
- // if (footer) {
84
- // const footerElement = document.createElement('div')
85
- // footerElement.innerHTML = footer
86
- // setPositioning(footerElement, 'footer')
87
- // document.body.appendChild(footerElement)
88
- // }
89
-
90
- if (watermark) {
91
- const watermarkElement = document.createElement('div')
92
- watermarkElement.innerHTML = watermark
93
- watermarkElement.style.position = 'fixed'
94
- watermarkElement.style.top = isLandscape ? '40%' : '50%'
95
- watermarkElement.style.left = '50%'
96
- watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)'
97
- watermarkElement.style.opacity = '0.2'
98
- watermarkElement.style.fontSize = '50px'
99
- watermarkElement.style.color = 'red'
100
- watermarkElement.style.pointerEvents = 'none'
101
- watermarkElement.style.zIndex = '9999'
102
- document.body.appendChild(watermarkElement)
103
- }
104
- },
105
- { header, footer, watermark, isLandscape: landscape }
106
- )
107
- }
108
-
109
- const pageOptions = {
110
- format,
111
- width,
112
- height,
113
- margin: {
114
- top: marginTop,
115
- right: marginRight,
116
- bottom: marginBottom,
117
- left: marginLeft
118
- },
119
- scale,
120
- printBackground,
121
- landscape,
122
- displayHeaderFooter: false, // Handled via Puppeteer directly
123
- preferCSSPageSize
124
- }
125
-
126
- const coverPageBuffer = await page.pdf(pageOptions)
127
- const coverPageDoc = await PDFDocument.load(coverPageBuffer)
128
- const coverPages = await pdfDoc.copyPages(coverPageDoc, coverPageDoc.getPageIndices())
129
- coverPages.forEach(page => pdfDoc.addPage(page))
34
+ const renderedCoverPage = pdfUtil.renderTemplate(coverPage, templateInput)
35
+ await pdfUtil.processPageAndGeneratePDF(stepOptions, renderedCoverPage)
130
36
  }
131
37
 
132
38
  var lastPageBuffer
133
39
 
134
40
  // Process Last Page (if provided) but add it later in the save task
135
41
  if (lastPage) {
136
- const renderedLastPage = renderTemplateSafely(lastPage, templateInput)
137
- await page.setContent(renderedLastPage)
138
-
139
- // Apply header, footer, and watermark using Puppeteer
140
- if (header || footer || watermark) {
141
- await page.evaluate(
142
- ({ header, footer, watermark, isLandscape }) => {
143
- const setPositioning = (element, position) => {
144
- element.style.position = 'fixed'
145
- element.style.left = '0'
146
- element.style.width = '100%'
147
- element.style.textAlign = 'center'
148
- element.style.fontSize = '12px'
149
- if (position === 'header') {
150
- element.style.top = '0'
151
- } else if (position === 'footer') {
152
- element.style.bottom = '0'
153
- }
154
- }
155
-
156
- // if (header) {
157
- // const headerElement = document.createElement('div')
158
- // headerElement.innerHTML = header
159
- // setPositioning(headerElement, 'header')
160
- // document.body.appendChild(headerElement)
161
- // }
162
-
163
- // if (footer) {
164
- // const footerElement = document.createElement('div')
165
- // footerElement.innerHTML = footer
166
- // setPositioning(footerElement, 'footer')
167
- // document.body.appendChild(footerElement)
168
- // }
42
+ const renderedLastPage = pdfUtil.renderTemplate(lastPage, templateInput)
43
+ lastPageBuffer = await pdfUtil.generateLastPageBuffer(stepOptions, renderedLastPage)
44
+ }
169
45
 
170
- if (watermark) {
171
- const watermarkElement = document.createElement('div')
172
- watermarkElement.innerHTML = watermark
173
- watermarkElement.style.position = 'fixed'
174
- watermarkElement.style.top = isLandscape ? '40%' : '50%'
175
- watermarkElement.style.left = '50%'
176
- watermarkElement.style.transform = 'translate(-50%, -50%) rotate(-45deg)'
177
- watermarkElement.style.opacity = '0.2'
178
- watermarkElement.style.fontSize = '50px'
179
- watermarkElement.style.color = 'red'
180
- watermarkElement.style.pointerEvents = 'none'
181
- watermarkElement.style.zIndex = '9999'
182
- document.body.appendChild(watermarkElement)
183
- }
184
- },
185
- { header, footer, watermark, isLandscape: landscape }
186
- )
187
- }
46
+ await pdfUtil.closeBrowser()
188
47
 
189
- const pageOptions = {
190
- format,
191
- width,
192
- height,
193
- margin: {
194
- top: marginTop,
195
- right: marginRight,
196
- bottom: marginBottom,
197
- left: marginLeft
198
- },
199
- scale,
200
- printBackground,
201
- landscape,
202
- displayHeaderFooter: false, // Handled via Puppeteer directly
203
- preferCSSPageSize
204
- }
48
+ pdfInfo.lastPageBuffer = lastPageBuffer
49
+ pdfInfo.pageCount = pdfDoc.getPageCount()
205
50
 
206
- lastPageBuffer = await page.pdf(pageOptions)
51
+ return {
52
+ data: pdfInfo
207
53
  }
208
-
209
- await page.close()
210
- headlessPool.release(browser)
211
54
  } catch (error) {
212
- if (browser) {
213
- await browser.close()
214
- }
55
+ await pdfUtil.closeBrowser()
215
56
  throw error
216
57
  }
217
-
218
- const pdfInfo = {
219
- pdfDoc,
220
- lastPageBuffer,
221
- header,
222
- footer,
223
- watermark,
224
- fileName,
225
- pageCount: pdfDoc.getPageCount()
226
- }
227
-
228
- context.__headless_pdf = pdfInfo
229
-
230
- return {
231
- data: pdfInfo
232
- }
233
58
  }
234
59
 
235
60
  HeadlessPDFOpen.parameterSpec = [
236
- {
237
- type: 'scenario-step-input',
238
- name: 'accessor',
239
- label: 'accessor'
240
- },
61
+ ...getCommonParameterSpec(),
241
62
  {
242
63
  type: 'textarea',
243
64
  name: 'coverPage',
@@ -269,91 +90,6 @@ HeadlessPDFOpen.parameterSpec = [
269
90
  type: 'string',
270
91
  name: 'fileName',
271
92
  label: 'filename'
272
- },
273
- {
274
- type: 'select',
275
- name: 'format',
276
- label: 'page-format',
277
- property: {
278
- options: [
279
- { display: '', value: '' },
280
- { display: 'A4', value: 'A4' },
281
- { display: 'A3', value: 'A3' },
282
- { display: 'Letter', value: 'Letter' },
283
- { display: 'Legal', value: 'Legal' }
284
- ]
285
- },
286
- description: 'Select the paper format for the PDF'
287
- },
288
- {
289
- type: 'string',
290
- name: 'width',
291
- label: 'page-width',
292
- placeholder: '(e.g., "8.5in", "21cm", "600px")',
293
- description: 'Specify the width of the page (e.g., "8.5in", "21cm", "600px")'
294
- },
295
- {
296
- type: 'string',
297
- name: 'height',
298
- label: 'page-height',
299
- placeholder: '(e.g., "11in", "29.7cm", "800px")',
300
- description: 'Specify the height of the page (e.g., "11in", "29.7cm", "800px")'
301
- },
302
- {
303
- type: 'string',
304
- name: 'marginTop',
305
- label: 'page-margin-top',
306
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
307
- description: 'Set the top margin for the page'
308
- },
309
- {
310
- type: 'string',
311
- name: 'marginBottom',
312
- label: 'page-margin-bottom',
313
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
314
- description: 'Set the bottom margin for the page'
315
- },
316
- {
317
- type: 'string',
318
- name: 'marginLeft',
319
- label: 'page-margin-left',
320
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
321
- description: 'Set the left margin for the page'
322
- },
323
- {
324
- type: 'string',
325
- name: 'marginRight',
326
- label: 'page-margin-right',
327
- placeholder: '(e.g., "0.5in", "1cm", "100px")',
328
- description: 'Set the right margin for the page'
329
- },
330
- {
331
- type: 'number',
332
- name: 'scale',
333
- label: 'page-scale',
334
- defaultValue: 1,
335
- description: 'Set the scale of the page content (default is 1)'
336
- },
337
- {
338
- type: 'boolean',
339
- name: 'printBackground',
340
- label: 'print-background',
341
- defaultValue: true,
342
- description: 'Include background graphics when printing the page'
343
- },
344
- {
345
- type: 'boolean',
346
- name: 'landscape',
347
- label: 'landscape',
348
- defaultValue: false,
349
- description: 'Print the PDF in landscape orientation'
350
- },
351
- {
352
- type: 'boolean',
353
- name: 'preferCSSPageSize',
354
- label: 'prefer-css-page-size',
355
- defaultValue: false,
356
- description: 'Whether to prefer the CSS-defined page size over the given width and height'
357
93
  }
358
94
  ]
359
95