@things-factory/integration-headless 7.0.53 → 7.0.55
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.js +1 -1
- package/dist-server/engine/task/headless-pdf-capture-board.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-open.js +27 -257
- package/dist-server/engine/task/headless-pdf-open.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-save.js +32 -20
- package/dist-server/engine/task/headless-pdf-save.js.map +1 -1
- package/dist-server/engine/task/pdf-capture-util.d.ts +10 -2
- package/dist-server/engine/task/pdf-capture-util.js +42 -7
- package/dist-server/engine/task/pdf-capture-util.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/engine/task/headless-pdf-capture-board.ts +1 -2
- package/server/engine/task/headless-pdf-open.ts +30 -294
- package/server/engine/task/headless-pdf-save.ts +34 -21
- package/server/engine/task/pdf-capture-util.ts +58 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/integration-headless",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.55",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"things-factory": true,
|
|
6
6
|
"author": "heartyoh <heartyoh@hatiolab.com>",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"clean": "npm run clean:server"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@things-factory/attachment-base": "^7.0.
|
|
26
|
-
"@things-factory/board-service": "^7.0.
|
|
27
|
-
"@things-factory/integration-base": "^7.0.
|
|
28
|
-
"@things-factory/shell": "^7.0.
|
|
25
|
+
"@things-factory/attachment-base": "^7.0.55",
|
|
26
|
+
"@things-factory/board-service": "^7.0.55",
|
|
27
|
+
"@things-factory/integration-base": "^7.0.55",
|
|
28
|
+
"@things-factory/shell": "^7.0.55",
|
|
29
29
|
"ejs": "^3.1.10",
|
|
30
30
|
"pdf-lib": "^1.17.1"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "20eb64e468407b2044e76d9c7019a837281141ad"
|
|
33
33
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { TaskRegistry } from '@things-factory/integration-base'
|
|
2
|
-
|
|
3
2
|
import { PDFCaptureUtil, getCommonParameterSpec } from './pdf-capture-util'
|
|
4
3
|
import { BoardFunc } from '@things-factory/board-service'
|
|
5
4
|
|
|
@@ -76,7 +75,7 @@ export async function HeadlessPDFCaptureBoard(step, context) {
|
|
|
76
75
|
|
|
77
76
|
// PDF 생성 및 추가
|
|
78
77
|
const htmlContent = '' // 보드에서 직접 렌더링되므로 HTML 컨텐츠가 비어있음
|
|
79
|
-
await pdfUtil.processPageAndGeneratePDF(
|
|
78
|
+
await pdfUtil.processPageAndGeneratePDF(pageOptions, htmlContent) // pageOptions 사용
|
|
80
79
|
|
|
81
80
|
return {
|
|
82
81
|
data: context.__headless_pdf
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
accessor,
|
|
11
|
-
coverPage,
|
|
12
|
-
lastPage,
|
|
13
|
-
header,
|
|
14
|
-
footer,
|
|
15
|
-
watermark,
|
|
16
|
-
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
|
|
7
|
+
const { connection: connectionName, params: stepOptions } = step
|
|
8
|
+
const { accessor, coverPage, lastPage, header, footer, watermark, fileName } = stepOptions || {}
|
|
30
9
|
|
|
31
|
-
const
|
|
32
|
-
let browser
|
|
10
|
+
const { data } = context
|
|
33
11
|
|
|
34
12
|
// Create a new PDF document using pdf-lib
|
|
35
13
|
const pdfDoc = await PDFDocument.create()
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
15
|
+
const pdfInfo = {
|
|
16
|
+
pdfDoc,
|
|
17
|
+
watermark,
|
|
18
|
+
fileName,
|
|
19
|
+
pageCount: 0
|
|
20
|
+
} as any
|
|
40
21
|
|
|
41
|
-
|
|
22
|
+
context.__headless_pdf = pdfInfo
|
|
42
23
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return template
|
|
49
|
-
}
|
|
50
|
-
}
|
|
24
|
+
const pdfUtil = new PDFCaptureUtil(context)
|
|
25
|
+
await pdfUtil.initBrowser(connectionName)
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const templateInput = access(accessor, data)
|
|
51
29
|
|
|
52
30
|
// Convert Cover Page to PDF and add it to the document (if provided)
|
|
53
31
|
if (coverPage) {
|
|
54
|
-
const renderedCoverPage =
|
|
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))
|
|
32
|
+
const renderedCoverPage = pdfUtil.renderTemplate(coverPage, templateInput)
|
|
33
|
+
await pdfUtil.processPageAndGeneratePDF(stepOptions, renderedCoverPage)
|
|
130
34
|
}
|
|
131
35
|
|
|
132
36
|
var lastPageBuffer
|
|
133
37
|
|
|
134
38
|
// Process Last Page (if provided) but add it later in the save task
|
|
135
39
|
if (lastPage) {
|
|
136
|
-
const renderedLastPage =
|
|
137
|
-
await
|
|
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
|
-
// }
|
|
40
|
+
const renderedLastPage = pdfUtil.renderTemplate(lastPage, templateInput)
|
|
41
|
+
lastPageBuffer = await pdfUtil.generateLastPageBuffer(stepOptions, renderedLastPage)
|
|
42
|
+
}
|
|
169
43
|
|
|
170
|
-
|
|
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
|
-
}
|
|
44
|
+
await pdfUtil.closeBrowser()
|
|
188
45
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
}
|
|
46
|
+
pdfInfo.lastPageBuffer = lastPageBuffer
|
|
47
|
+
pdfInfo.pageCount = pdfDoc.getPageCount()
|
|
48
|
+
pdfInfo.header = header
|
|
49
|
+
pdfInfo.footer = footer
|
|
205
50
|
|
|
206
|
-
|
|
51
|
+
return {
|
|
52
|
+
data: pdfInfo
|
|
207
53
|
}
|
|
208
|
-
|
|
209
|
-
await page.close()
|
|
210
|
-
headlessPool.release(browser)
|
|
211
54
|
} catch (error) {
|
|
212
|
-
|
|
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
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { PDFDocument } from 'pdf-lib'
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { Readable } from 'stream'
|
|
4
3
|
import { TaskRegistry } from '@things-factory/integration-base'
|
|
5
4
|
import { Attachment, createAttachment } from '@things-factory/attachment-base'
|
|
6
5
|
import { getRepository } from '@things-factory/shell'
|
|
7
6
|
|
|
8
7
|
async function HeadlessPDFSave(step, context) {
|
|
9
|
-
|
|
8
|
+
const { domain, user, __headless_pdf } = context
|
|
10
9
|
|
|
11
10
|
if (!__headless_pdf || !__headless_pdf.pdfDoc) {
|
|
12
11
|
throw new Error(
|
|
@@ -18,16 +17,39 @@ async function HeadlessPDFSave(step, context) {
|
|
|
18
17
|
|
|
19
18
|
// Add last page if it exists
|
|
20
19
|
if (__headless_pdf.lastPageBuffer) {
|
|
21
|
-
|
|
22
|
-
const copiedLastPages = await pdfDoc.copyPages(lastPageDoc, lastPageDoc.getPageIndices())
|
|
23
|
-
copiedLastPages.forEach(page => pdfDoc.addPage(page))
|
|
20
|
+
await appendLastPageToPDF(pdfDoc, __headless_pdf.lastPageBuffer)
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
const pdfBytes = await pdfDoc.save()
|
|
24
|
+
const finalFileName = generateFileName(__headless_pdf.fileName)
|
|
25
|
+
|
|
26
|
+
const savedAttachment = await savePDFToFile(pdfBytes, finalFileName, domain, user)
|
|
27
|
+
const attachment = await getAttachmentDetails(savedAttachment.id)
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
return {
|
|
30
|
+
data: {
|
|
31
|
+
id: attachment.id,
|
|
32
|
+
name: attachment.name,
|
|
33
|
+
path: attachment.path,
|
|
34
|
+
mimetype: attachment.mimetype,
|
|
35
|
+
refBy: attachment.refBy,
|
|
36
|
+
fullpath: attachment.fullpath
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function appendLastPageToPDF(pdfDoc, lastPageBuffer) {
|
|
42
|
+
const lastPageDoc = await PDFDocument.load(lastPageBuffer)
|
|
43
|
+
const copiedLastPages = await pdfDoc.copyPages(lastPageDoc, lastPageDoc.getPageIndices())
|
|
44
|
+
copiedLastPages.forEach(page => pdfDoc.addPage(page))
|
|
45
|
+
}
|
|
29
46
|
|
|
30
|
-
|
|
47
|
+
function generateFileName(fileName) {
|
|
48
|
+
return fileName || `document-${Date.now()}.pdf`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function savePDFToFile(pdfBytes, finalFileName, domain, user) {
|
|
52
|
+
return await createAttachment(
|
|
31
53
|
null,
|
|
32
54
|
{
|
|
33
55
|
attachment: {
|
|
@@ -51,21 +73,12 @@ async function HeadlessPDFSave(step, context) {
|
|
|
51
73
|
state: { domain, user }
|
|
52
74
|
}
|
|
53
75
|
)
|
|
76
|
+
}
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
78
|
+
async function getAttachmentDetails(attachmentId) {
|
|
79
|
+
return await getRepository(Attachment).findOne({
|
|
80
|
+
where: { id: attachmentId }
|
|
57
81
|
})
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
data: {
|
|
61
|
-
id: attachment.id,
|
|
62
|
-
name: attachment.name,
|
|
63
|
-
path: attachment.path,
|
|
64
|
-
mimetype: attachment.mimetype,
|
|
65
|
-
refBy: attachment.refBy,
|
|
66
|
-
fullpath: attachment.fullpath
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
HeadlessPDFSave.parameterSpec = []
|
|
@@ -58,8 +58,7 @@ export class PDFCaptureUtil {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
async generatePDFContent(page: Page,
|
|
62
|
-
await page.setContent(htmlContent)
|
|
61
|
+
async generatePDFContent(page: Page, pageOptions: any): Promise<PDFDocument> {
|
|
63
62
|
const contentBuffer = await page.pdf(pageOptions)
|
|
64
63
|
const contentPdfDoc = await PDFDocument.load(contentBuffer)
|
|
65
64
|
return contentPdfDoc
|
|
@@ -76,15 +75,19 @@ export class PDFCaptureUtil {
|
|
|
76
75
|
|
|
77
76
|
const page = await this.browser.newPage()
|
|
78
77
|
|
|
78
|
+
// 페이지 컨텐츠 설정
|
|
79
|
+
await page.setContent(htmlContent, { waitUntil: 'networkidle0' }) // HTML 컨텐츠가 완전히 로드될 때까지 대기
|
|
80
|
+
|
|
81
|
+
// 페이지가 완전히 로드된 후에 워터마크와 머릿글 적용
|
|
79
82
|
const { __headless_pdf } = this.context
|
|
80
83
|
const { header, footer, watermark, landscape } = __headless_pdf
|
|
81
84
|
|
|
82
85
|
if (header || footer || watermark) {
|
|
83
|
-
await this.applyHeaderFooterWatermark(page, { header, footer, watermark, landscape })
|
|
86
|
+
await this.applyHeaderFooterWatermark(page, { header, footer, watermark, landscape, data: __headless_pdf })
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
const pageOptions = this.buildPageOptions(stepOptions)
|
|
87
|
-
const contentPdfDoc = await this.generatePDFContent(page,
|
|
90
|
+
const contentPdfDoc = await this.generatePDFContent(page, pageOptions)
|
|
88
91
|
await this.appendPDFContentToDocument(__headless_pdf.pdfDoc, contentPdfDoc)
|
|
89
92
|
await page.close()
|
|
90
93
|
}
|
|
@@ -127,10 +130,16 @@ export class PDFCaptureUtil {
|
|
|
127
130
|
|
|
128
131
|
async applyHeaderFooterWatermark(
|
|
129
132
|
page: Page,
|
|
130
|
-
{
|
|
133
|
+
{
|
|
134
|
+
header,
|
|
135
|
+
footer,
|
|
136
|
+
watermark,
|
|
137
|
+
landscape,
|
|
138
|
+
data
|
|
139
|
+
}: { header: string; footer: string; watermark: string; landscape: boolean; data: any }
|
|
131
140
|
) {
|
|
132
141
|
await page.evaluate(
|
|
133
|
-
({ header, footer, watermark, landscape }) => {
|
|
142
|
+
({ header, footer, watermark, landscape, data }) => {
|
|
134
143
|
const setPositioning = (element: HTMLElement, position: 'header' | 'footer') => {
|
|
135
144
|
element.style.position = 'fixed'
|
|
136
145
|
element.style.left = '0'
|
|
@@ -139,6 +148,8 @@ export class PDFCaptureUtil {
|
|
|
139
148
|
element.style.fontSize = '12px'
|
|
140
149
|
element.style.margin = '0'
|
|
141
150
|
element.style.padding = '0'
|
|
151
|
+
element.style.zIndex = '1000'
|
|
152
|
+
|
|
142
153
|
if (position === 'header') {
|
|
143
154
|
element.style.top = '0'
|
|
144
155
|
} else if (position === 'footer') {
|
|
@@ -175,9 +186,49 @@ export class PDFCaptureUtil {
|
|
|
175
186
|
document.body.appendChild(watermarkElement)
|
|
176
187
|
}
|
|
177
188
|
},
|
|
178
|
-
{
|
|
189
|
+
{
|
|
190
|
+
header: header && this.renderTemplate(header, data),
|
|
191
|
+
footer: footer && this.renderTemplate(footer, data),
|
|
192
|
+
watermark: watermark && this.renderTemplate(watermark, data),
|
|
193
|
+
landscape,
|
|
194
|
+
data
|
|
195
|
+
}
|
|
179
196
|
)
|
|
180
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generates a PDF buffer for the last page.
|
|
201
|
+
* @param stepOptions - Options used for generating the PDF.
|
|
202
|
+
* @param htmlContent - HTML content to be converted to PDF.
|
|
203
|
+
* @returns {Promise<Uint8Array>} - The generated PDF as a buffer.
|
|
204
|
+
*/
|
|
205
|
+
async generateLastPageBuffer(stepOptions: StepOptions, htmlContent: string): Promise<Uint8Array> {
|
|
206
|
+
if (!this.browser) throw new Error('Browser not initialized. Call initBrowser() first.')
|
|
207
|
+
|
|
208
|
+
const page = await this.browser.newPage()
|
|
209
|
+
|
|
210
|
+
// 페이지에 HTML 콘텐츠를 설정합니다.
|
|
211
|
+
await page.setContent(htmlContent, { waitUntil: 'networkidle0' })
|
|
212
|
+
|
|
213
|
+
// 페이지가 완전히 로드된 후에 워터마크와 머릿글 적용
|
|
214
|
+
const { __headless_pdf } = this.context
|
|
215
|
+
const { header, footer, watermark, landscape } = __headless_pdf
|
|
216
|
+
|
|
217
|
+
if (header || footer || watermark) {
|
|
218
|
+
await this.applyHeaderFooterWatermark(page, { header, footer, watermark, landscape, data: __headless_pdf })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 페이지 옵션 설정
|
|
222
|
+
const pageOptions = this.buildPageOptions(stepOptions)
|
|
223
|
+
|
|
224
|
+
// PDF를 생성하고 버퍼를 반환합니다.
|
|
225
|
+
const lastPageBuffer = await page.pdf(pageOptions)
|
|
226
|
+
|
|
227
|
+
// 페이지 닫기
|
|
228
|
+
await page.close()
|
|
229
|
+
|
|
230
|
+
return lastPageBuffer
|
|
231
|
+
}
|
|
181
232
|
}
|
|
182
233
|
|
|
183
234
|
export function getCommonParameterSpec() {
|