@things-factory/integration-headless 7.0.56 → 7.0.58
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 +78 -22
- package/dist-server/engine/task/headless-pdf-capture-board.js.map +1 -1
- package/dist-server/engine/task/index.d.ts +3 -0
- package/dist-server/engine/task/index.js +4 -0
- package/dist-server/engine/task/index.js.map +1 -1
- package/dist-server/engine/task/pdf-capture-util.d.ts +1 -1
- package/dist-server/engine/task/pdf-capture-util.js +8 -4
- package/dist-server/engine/task/pdf-capture-util.js.map +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +2 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/engine/task/headless-pdf-capture-board.ts +109 -38
- package/server/engine/task/index.ts +4 -0
- package/server/engine/task/pdf-capture-util.ts +8 -4
- package/server/index.ts +2 -0
- package/translations/en.json +2 -1
- package/translations/ja.json +2 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
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.58",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"things-factory": true,
|
|
6
6
|
"author": "heartyoh <heartyoh@hatiolab.com>",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"ejs": "^3.1.10",
|
|
30
30
|
"pdf-lib": "^1.17.1"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "b0eea04d732f7fa30e7e7803cca8e3258b750f17"
|
|
33
33
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TaskRegistry } from '@things-factory/integration-base'
|
|
2
2
|
import { PDFCaptureUtil, getCommonParameterSpec } from './pdf-capture-util'
|
|
3
3
|
import { BoardFunc } from '@things-factory/board-service'
|
|
4
|
+
import { access } from '@things-factory/utils'
|
|
4
5
|
|
|
5
6
|
const PAGE_FORMATS = {
|
|
6
7
|
A4: { width: 595.28, height: 841.89 },
|
|
@@ -9,73 +10,138 @@ const PAGE_FORMATS = {
|
|
|
9
10
|
Legal: { width: 612, height: 1008 }
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
function convertToPixels(value: string): number {
|
|
14
|
+
const dpi = 96 // PDF에서 기본적으로 사용하는 DPI
|
|
15
|
+
|
|
16
|
+
if (value.endsWith('px')) {
|
|
17
|
+
return parseFloat(value)
|
|
18
|
+
} else if (value.endsWith('in')) {
|
|
19
|
+
const inches = parseFloat(value)
|
|
20
|
+
return inches * dpi
|
|
21
|
+
} else if (value.endsWith('cm')) {
|
|
22
|
+
const cm = parseFloat(value)
|
|
23
|
+
return cm * (dpi / 2.54)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return 0
|
|
27
|
+
}
|
|
13
28
|
|
|
14
29
|
export async function HeadlessPDFCaptureBoard(step, context) {
|
|
15
30
|
const pdfUtil = new PDFCaptureUtil(context)
|
|
16
31
|
await pdfUtil.initBrowser(step.connection)
|
|
17
32
|
|
|
18
33
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
var {
|
|
35
|
+
board: boardObject,
|
|
36
|
+
accessor,
|
|
37
|
+
boardAccessor,
|
|
38
|
+
draft,
|
|
39
|
+
format = 'A4',
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
42
|
+
landscape,
|
|
43
|
+
marginLeft = 0,
|
|
44
|
+
marginRight = 0,
|
|
45
|
+
marginTop = 0,
|
|
46
|
+
marginBottom = 0
|
|
47
|
+
} = step.params
|
|
48
|
+
var { domain, data, user, logger } = context
|
|
49
|
+
|
|
50
|
+
const boardId = boardAccessor ? access(boardAccessor, data) : boardObject?.id
|
|
51
|
+
|
|
52
|
+
if (!boardId) {
|
|
22
53
|
throw new Error('The board property must be set')
|
|
23
54
|
}
|
|
24
55
|
|
|
25
|
-
const
|
|
26
|
-
|
|
56
|
+
const boardInput = access(accessor, data)
|
|
57
|
+
|
|
58
|
+
const { model, base } = await BoardFunc.headlessModel({ domain, id: boardId }, draft)
|
|
59
|
+
const [fontsToUse, fontStyles] = await BoardFunc.fonts(domain)
|
|
27
60
|
|
|
28
61
|
model.fonts = fontsToUse
|
|
29
62
|
model.fontStyles = fontStyles
|
|
30
63
|
|
|
31
|
-
|
|
32
|
-
|
|
64
|
+
var widthN = width ? convertToPixels(width) : 0
|
|
65
|
+
var heightN = height ? convertToPixels(height) : 0
|
|
33
66
|
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
67
|
+
if (!widthN && !heightN && PAGE_FORMATS[format]) {
|
|
68
|
+
const pageDimensions = PAGE_FORMATS[format]
|
|
69
|
+
widthN = pageDimensions.width * (96 / 72)
|
|
70
|
+
heightN = pageDimensions.height * (96 / 72)
|
|
37
71
|
}
|
|
38
72
|
|
|
39
|
-
if (landscape &&
|
|
40
|
-
;[
|
|
73
|
+
if (landscape && widthN && heightN) {
|
|
74
|
+
;[widthN, heightN] = [heightN, widthN]
|
|
41
75
|
}
|
|
42
76
|
|
|
77
|
+
marginLeft = convertToPixels(marginLeft)
|
|
78
|
+
marginTop = convertToPixels(marginTop)
|
|
79
|
+
marginBottom = convertToPixels(marginBottom)
|
|
80
|
+
marginRight = convertToPixels(marginRight)
|
|
81
|
+
|
|
82
|
+
const contentWidth = widthN - marginLeft - marginRight
|
|
83
|
+
const contentHeight = heightN - marginTop - marginBottom
|
|
84
|
+
|
|
85
|
+
const page = await pdfUtil.browser!.newPage()
|
|
86
|
+
|
|
87
|
+
await page.setViewport({ width: Math.round(contentWidth), height: Math.round(contentHeight) })
|
|
88
|
+
await page.setRequestInterception(true)
|
|
89
|
+
await page.setDefaultTimeout(10000)
|
|
90
|
+
|
|
91
|
+
page.on('console', async msg => {
|
|
92
|
+
console.log(`[browser ${msg.type()}] ${msg.text()}`)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
page.on('requestfailed', request => {
|
|
96
|
+
console.log('Request failed:', request.url())
|
|
97
|
+
})
|
|
98
|
+
|
|
43
99
|
const protocol = 'http'
|
|
44
100
|
const host = 'localhost'
|
|
45
101
|
const port = process.env.PORT ? `:${process.env.PORT}` : ''
|
|
46
102
|
const path = '/internal-board-service-view'
|
|
47
103
|
const url = `${protocol}://${host}${port}${path}`
|
|
48
104
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
105
|
+
const token = await user?.sign()
|
|
106
|
+
|
|
107
|
+
page.on('request', request => {
|
|
108
|
+
if (request.url() === url) {
|
|
109
|
+
request.continue({
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'x-things-factory-domain': domain?.subdomain,
|
|
114
|
+
Authorization: 'Bearer ' + token
|
|
115
|
+
},
|
|
116
|
+
postData: JSON.stringify({
|
|
117
|
+
model,
|
|
118
|
+
base
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
} else if (request.url().startsWith(`${protocol}://${host}${port}`)) {
|
|
122
|
+
request.continue({
|
|
123
|
+
headers: {
|
|
124
|
+
...request.headers(),
|
|
125
|
+
'x-things-factory-domain': domain?.subdomain,
|
|
126
|
+
Authorization: 'Bearer ' + token
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
} else {
|
|
130
|
+
request.continue()
|
|
131
|
+
}
|
|
54
132
|
})
|
|
55
133
|
|
|
56
134
|
await page.goto(url)
|
|
57
135
|
|
|
58
|
-
await page.evaluate(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
},
|
|
66
|
-
{ model, base }
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
// 페이지 옵션 설정
|
|
70
|
-
const pageOptions = pdfUtil.buildPageOptions({
|
|
71
|
-
...step.params,
|
|
72
|
-
width: width ? `${width}px` : undefined,
|
|
73
|
-
height: height ? `${height}px` : undefined
|
|
74
|
-
})
|
|
136
|
+
await page.evaluate(data => {
|
|
137
|
+
//@ts-ignore
|
|
138
|
+
s.data = data
|
|
139
|
+
return new Promise(resolve => {
|
|
140
|
+
requestAnimationFrame(() => resolve(0))
|
|
141
|
+
})
|
|
142
|
+
}, boardInput)
|
|
75
143
|
|
|
76
|
-
|
|
77
|
-
const htmlContent = '' // 보드에서 직접 렌더링되므로 HTML 컨텐츠가 비어있음
|
|
78
|
-
await pdfUtil.processPageAndGeneratePDF(pageOptions, htmlContent) // pageOptions 사용
|
|
144
|
+
await pdfUtil.processPageAndGeneratePDF(step.params, null, page)
|
|
79
145
|
|
|
80
146
|
return {
|
|
81
147
|
data: context.__headless_pdf
|
|
@@ -89,6 +155,11 @@ export async function HeadlessPDFCaptureBoard(step, context) {
|
|
|
89
155
|
|
|
90
156
|
HeadlessPDFCaptureBoard.parameterSpec = [
|
|
91
157
|
...getCommonParameterSpec(),
|
|
158
|
+
{
|
|
159
|
+
type: 'string',
|
|
160
|
+
name: 'boardAccessor',
|
|
161
|
+
label: 'board-accessor'
|
|
162
|
+
},
|
|
92
163
|
{
|
|
93
164
|
type: 'resource-object',
|
|
94
165
|
name: 'board',
|
|
@@ -70,13 +70,17 @@ export class PDFCaptureUtil {
|
|
|
70
70
|
this.context.__headless_pdf.pageCount += copiedPages.length
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
async processPageAndGeneratePDF(stepOptions: StepOptions, htmlContent: string) {
|
|
73
|
+
async processPageAndGeneratePDF(stepOptions: StepOptions, htmlContent: string, page?: Page) {
|
|
74
74
|
if (!this.browser) throw new Error('Browser not initialized. Call initBrowser() first.')
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
if (!page) {
|
|
77
|
+
page = page || (await this.browser.newPage())
|
|
78
|
+
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
if (htmlContent) {
|
|
81
|
+
// 페이지 컨텐츠 설정
|
|
82
|
+
await page.setContent(htmlContent, { waitUntil: 'networkidle0' }) // HTML 컨텐츠가 완전히 로드될 때까지 대기
|
|
83
|
+
}
|
|
80
84
|
|
|
81
85
|
// 페이지가 완전히 로드된 후에 워터마크와 머릿글 적용
|
|
82
86
|
const { __headless_pdf } = this.context
|
package/server/index.ts
CHANGED
package/translations/en.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"label.
|
|
2
|
+
"label.board-accessor": "board ID accessor",
|
|
3
3
|
"label.board-draft": "draft board",
|
|
4
|
+
"label.display-header/footer": "display header/footer",
|
|
4
5
|
"label.filename": "filename",
|
|
5
6
|
"label.footer": "footer template",
|
|
6
7
|
"label.header": "header template",
|
package/translations/ja.json
CHANGED
package/translations/ko.json
CHANGED
package/translations/ms.json
CHANGED