@things-factory/integration-headless 7.0.37 → 7.0.39
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/connector/headless-connector.js +1 -1
- package/dist-server/engine/connector/headless-connector.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-capture.js +51 -67
- package/dist-server/engine/task/headless-pdf-capture.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-open.js +39 -12
- package/dist-server/engine/task/headless-pdf-open.js.map +1 -1
- package/dist-server/engine/task/headless-pdf-save.js +63 -10
- package/dist-server/engine/task/headless-pdf-save.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/engine/connector/headless-connector.ts +1 -1
- package/server/engine/task/headless-pdf-capture.ts +55 -89
- package/server/engine/task/headless-pdf-open.ts +42 -14
- package/server/engine/task/headless-pdf-save.ts +72 -10
- package/translations/en.json +7 -2
- package/translations/ja.json +7 -2
- package/translations/ko.json +7 -2
- package/translations/ms.json +7 -2
- package/translations/zh.json +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-connector.ts"],"names":[],"mappings":";;;AAAA,uEAA+E;AAC/E,mDAAyE;AAEzE,MAAa,iBAAiB;IAC5B,KAAK,CAAC,KAAK,CAAC,iBAAiB;QAC3B,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;IAC3E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAU;QACtB,IAAI,EACF,QAAQ,EAAE,GAAG,GAAG,GAAG,EACnB,MAAM,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAC9B,GAAG,UAAU,CAAA;QAEd,MAAM,YAAY,GAAG,IAAA,kCAAkB,EAAC;YACtC,GAAG;YACH,GAAG;YACH,YAAY,EAAE,IAAI;YAClB,oBAAoB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAC3B,iCAAiC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,gBAAgB,CACxF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAU;QACzB,MAAM,YAAY,GAAG,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAA;QACxE,IAAA,mCAAmB,EAAC,YAAY,CAAC,CAAA;QAEjC,oCAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAA;QAEtD,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,UAAU,CAAC,IAAI,mBAAmB,CAAC,CAAA;IACpG,CAAC;IAED,IAAI,aAAa;QACf,OAAO;YACL;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;SACF,CAAA;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"headless-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-connector.ts"],"names":[],"mappings":";;;AAAA,uEAA+E;AAC/E,mDAAyE;AAEzE,MAAa,iBAAiB;IAC5B,KAAK,CAAC,KAAK,CAAC,iBAAiB;QAC3B,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;IAC3E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAU;QACtB,IAAI,EACF,QAAQ,EAAE,GAAG,GAAG,GAAG,EACnB,MAAM,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAC9B,GAAG,UAAU,CAAA;QAEd,MAAM,YAAY,GAAG,IAAA,kCAAkB,EAAC;YACtC,GAAG;YACH,GAAG;YACH,YAAY,EAAE,IAAI;YAClB,oBAAoB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAC3B,iCAAiC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,gBAAgB,CACxF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAU;QACzB,MAAM,YAAY,GAAG,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAA;QACxE,IAAA,mCAAmB,EAAC,YAAY,CAAC,CAAA;QAEjC,oCAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAA;QAEtD,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,UAAU,CAAC,IAAI,mBAAmB,CAAC,CAAA;IACpG,CAAC;IAED,IAAI,aAAa;QACf,OAAO;YACL;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;SACF,CAAA;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,CAAC,cAAc,CAAC,CAAA;IACzB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,yBAAyB,CAAA;IAClC,CAAC;CACF;AA1DD,8CA0DC;AAED,oCAAiB,CAAC,iBAAiB,CAAC,oBAAoB,EAAE,IAAI,iBAAiB,EAAE,CAAC,CAAA","sourcesContent":["import { ConnectionManager, Connector } from '@things-factory/integration-base'\nimport { createHeadlessPool, destroyHeadlessPool } from './headless-pool'\n\nexport class HeadlessConnector implements Connector {\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n\n ConnectionManager.logger.info('headless-connector connections are ready')\n }\n\n async connect(connection) {\n var {\n endpoint: uri = '1',\n params: { min = 2, max = 10 }\n } = connection\n\n const headlessPool = createHeadlessPool({\n min,\n max,\n testOnBorrow: true,\n acquireTimeoutMillis: 15000\n })\n\n ConnectionManager.addConnectionInstance(connection, headlessPool)\n\n ConnectionManager.logger.info(\n `headless-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async disconnect(connection) {\n const headlessPool = ConnectionManager.getConnectionInstance(connection)\n destroyHeadlessPool(headlessPool)\n\n ConnectionManager.removeConnectionInstance(connection)\n\n ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`)\n }\n\n get parameterSpec() {\n return [\n {\n type: 'number',\n name: 'min',\n label: 'pool-size-min'\n },\n {\n type: 'number',\n name: 'max',\n label: 'pool-size-max'\n }\n ]\n }\n\n get taskPrefixes() {\n return ['headless-pdf']\n }\n\n get description() {\n return 'Headless Pool Connector'\n }\n}\n\nConnectionManager.registerConnector('headless-connector', new HeadlessConnector())\n"]}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const ejs = tslib_1.__importStar(require("ejs"));
|
|
5
3
|
const pdf_lib_1 = require("pdf-lib");
|
|
6
4
|
const integration_base_1 = require("@things-factory/integration-base");
|
|
7
5
|
const integration_base_2 = require("@things-factory/integration-base");
|
|
8
6
|
async function HeadlessPDFCapture(step, context) {
|
|
9
7
|
var { connection: connectionName, params: stepOptions } = step;
|
|
10
|
-
var { htmlContent, format, width, height,
|
|
11
|
-
var { domain,
|
|
8
|
+
var { htmlContent, format, width, height, marginTop, marginBottom, marginLeft, marginRight, scale, printBackground, landscape, preferCSSPageSize } = stepOptions || {};
|
|
9
|
+
var { domain, __headless_pdf } = context;
|
|
12
10
|
if (!__headless_pdf) {
|
|
13
|
-
throw new Error('
|
|
11
|
+
throw new Error('It requires headless-pdf-open to be performed first');
|
|
14
12
|
}
|
|
15
|
-
var { pdf,
|
|
13
|
+
var { pdf, pageCount } = __headless_pdf;
|
|
16
14
|
var headlessPool = integration_base_2.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
17
15
|
let browser;
|
|
18
16
|
try {
|
|
@@ -30,13 +28,16 @@ async function HeadlessPDFCapture(step, context) {
|
|
|
30
28
|
format,
|
|
31
29
|
width,
|
|
32
30
|
height,
|
|
33
|
-
margin
|
|
31
|
+
margin: {
|
|
32
|
+
top: marginTop,
|
|
33
|
+
right: marginRight,
|
|
34
|
+
bottom: marginBottom,
|
|
35
|
+
left: marginLeft
|
|
36
|
+
},
|
|
34
37
|
scale,
|
|
35
38
|
printBackground,
|
|
36
39
|
landscape,
|
|
37
|
-
displayHeaderFooter,
|
|
38
|
-
headerTemplate: displayHeaderFooter ? headerTemplate : undefined,
|
|
39
|
-
footerTemplate: displayHeaderFooter ? footerTemplate : undefined,
|
|
40
|
+
displayHeaderFooter: false, // 첫 번째 패스에서는 헤더/푸터를 렌더링하지 않음
|
|
40
41
|
preferCSSPageSize
|
|
41
42
|
};
|
|
42
43
|
// 페이지를 PDF로 변환
|
|
@@ -44,38 +45,7 @@ async function HeadlessPDFCapture(step, context) {
|
|
|
44
45
|
// PDF-lib을 사용하여 PDF를 로드하고 추가 조작
|
|
45
46
|
const pdfDoc = await pdf_lib_1.PDFDocument.load(pageBuffer);
|
|
46
47
|
const pages = await pdf.copyPages(pdfDoc, pdfDoc.getPageIndices());
|
|
47
|
-
pages.forEach(
|
|
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
|
-
}
|
|
48
|
+
pages.forEach(newPage => {
|
|
79
49
|
pdf.addPage(newPage);
|
|
80
50
|
});
|
|
81
51
|
// 페이지 카운트 업데이트
|
|
@@ -95,7 +65,7 @@ async function HeadlessPDFCapture(step, context) {
|
|
|
95
65
|
}
|
|
96
66
|
HeadlessPDFCapture.parameterSpec = [
|
|
97
67
|
{
|
|
98
|
-
type: '
|
|
68
|
+
type: 'textarea',
|
|
99
69
|
name: 'htmlContent',
|
|
100
70
|
label: 'html-content'
|
|
101
71
|
},
|
|
@@ -103,42 +73,63 @@ HeadlessPDFCapture.parameterSpec = [
|
|
|
103
73
|
type: 'select',
|
|
104
74
|
name: 'format',
|
|
105
75
|
label: 'page-format',
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
76
|
+
property: {
|
|
77
|
+
options: [
|
|
78
|
+
{ display: '', value: '' },
|
|
79
|
+
{ display: 'A4', value: 'A4' },
|
|
80
|
+
{ display: 'A3', value: 'A3' },
|
|
81
|
+
{ display: 'Letter', value: 'Letter' },
|
|
82
|
+
{ display: 'Legal', value: 'Legal' }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
112
85
|
description: 'Select the paper format for the PDF'
|
|
113
86
|
},
|
|
114
87
|
{
|
|
115
88
|
type: 'string',
|
|
116
89
|
name: 'width',
|
|
117
90
|
label: 'page-width',
|
|
91
|
+
placeholder: '(e.g., "8.5in", "21cm", "600px")',
|
|
118
92
|
description: 'Specify the width of the page (e.g., "8.5in", "21cm", "600px")'
|
|
119
93
|
},
|
|
120
94
|
{
|
|
121
95
|
type: 'string',
|
|
122
96
|
name: 'height',
|
|
123
97
|
label: 'page-height',
|
|
98
|
+
placeholder: '(e.g., "11in", "29.7cm", "800px")',
|
|
124
99
|
description: 'Specify the height of the page (e.g., "11in", "29.7cm", "800px")'
|
|
125
100
|
},
|
|
126
101
|
{
|
|
127
|
-
type: '
|
|
128
|
-
name: '
|
|
129
|
-
label: 'page-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
102
|
+
type: 'string',
|
|
103
|
+
name: 'marginTop',
|
|
104
|
+
label: 'page-margin-top',
|
|
105
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
106
|
+
description: 'Set the top margin for the page'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: 'string',
|
|
110
|
+
name: 'marginBottom',
|
|
111
|
+
label: 'page-margin-bottom',
|
|
112
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
113
|
+
description: 'Set the bottom margin for the page'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: 'string',
|
|
117
|
+
name: 'marginLeft',
|
|
118
|
+
label: 'page-margin-left',
|
|
119
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
120
|
+
description: 'Set the left margin for the page'
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'string',
|
|
124
|
+
name: 'marginRight',
|
|
125
|
+
label: 'page-margin-right',
|
|
126
|
+
placeholder: '(e.g., "0.5in", "1cm", "100px")',
|
|
127
|
+
description: 'Set the right margin for the page'
|
|
137
128
|
},
|
|
138
129
|
{
|
|
139
130
|
type: 'number',
|
|
140
131
|
name: 'scale',
|
|
141
|
-
label: 'scale',
|
|
132
|
+
label: 'page-scale',
|
|
142
133
|
defaultValue: 1,
|
|
143
134
|
description: 'Set the scale of the page content (default is 1)'
|
|
144
135
|
},
|
|
@@ -156,13 +147,6 @@ HeadlessPDFCapture.parameterSpec = [
|
|
|
156
147
|
defaultValue: false,
|
|
157
148
|
description: 'Print the PDF in landscape orientation'
|
|
158
149
|
},
|
|
159
|
-
{
|
|
160
|
-
type: 'boolean',
|
|
161
|
-
name: 'displayHeaderFooter',
|
|
162
|
-
label: 'display-header/footer',
|
|
163
|
-
defaultValue: false,
|
|
164
|
-
description: 'Display header and footer in the PDF'
|
|
165
|
-
},
|
|
166
150
|
{
|
|
167
151
|
type: 'boolean',
|
|
168
152
|
name: 'preferCSSPageSize',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-pdf-capture.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-capture.ts"],"names":[],"mappings":"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","sourcesContent":["import * as ejs from 'ejs'\nimport { PDFDocument, rgb, degrees } from 'pdf-lib'\n\nimport { TaskRegistry } from '@things-factory/integration-base'\nimport { ConnectionManager } from '@things-factory/integration-base'\n\nasync function HeadlessPDFCapture(step, context) {\n var { connection: connectionName, params: stepOptions } = step\n var {\n htmlContent,\n format,\n width,\n height,\n margin,\n scale,\n printBackground,\n landscape,\n displayHeaderFooter,\n headerTemplate,\n footerTemplate,\n preferCSSPageSize\n } = stepOptions || {}\n var { domain, data, __headless_pdf } = context\n\n if (!__headless_pdf) {\n throw new Error('it requires headless-pdf-open to be performed first')\n }\n\n var {\n pdf,\n header: headerTemplateFromContext,\n footer: footerTemplateFromContext,\n watermark,\n pageCount\n } = __headless_pdf\n\n var headlessPool = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n let browser\n\n try {\n // Puppeteer를 사용하여 브라우저와 페이지 인스턴스를 생성\n browser = await headlessPool.acquire()\n const page = await browser.newPage()\n\n // 설정된 HTML 콘텐츠를 페이지에 설정\n await page.setContent(htmlContent)\n\n // PDF 페이지 생성 옵션 설정\n if (preferCSSPageSize) {\n width = undefined\n height = undefined\n }\n\n const pageOptions = {\n format,\n width,\n height,\n margin,\n scale,\n printBackground,\n landscape,\n displayHeaderFooter,\n headerTemplate: displayHeaderFooter ? headerTemplate : undefined,\n footerTemplate: displayHeaderFooter ? footerTemplate : undefined,\n preferCSSPageSize\n }\n\n // 페이지를 PDF로 변환\n const pageBuffer = await page.pdf(pageOptions)\n\n // PDF-lib을 사용하여 PDF를 로드하고 추가 조작\n const pdfDoc = await PDFDocument.load(pageBuffer)\n const pages = await pdf.copyPages(pdfDoc, pdfDoc.getPageIndices())\n\n pages.forEach((newPage, index) => {\n const currentPageNumber = pageCount + index + 1\n\n // 헤더와 푸터 렌더링 (페이지 번호 및 총 페이지 수 포함)\n if (displayHeaderFooter && (headerTemplateFromContext || footerTemplateFromContext)) {\n const renderedHeader = headerTemplateFromContext\n ? ejs.render(headerTemplateFromContext, {\n ...data,\n pageNumber: currentPageNumber,\n totalPages: pageCount + pages.length\n })\n : ''\n const renderedFooter = footerTemplateFromContext\n ? ejs.render(footerTemplateFromContext, {\n ...data,\n pageNumber: currentPageNumber,\n totalPages: pageCount + pages.length\n })\n : ''\n\n // Header/Footer 그리기\n if (renderedHeader) {\n newPage.drawText(renderedHeader, { x: 50, y: newPage.getHeight() - 30, size: 12, color: rgb(0, 0, 0) })\n }\n if (renderedFooter) {\n newPage.drawText(renderedFooter, { x: 50, y: 30, size: 12, color: rgb(0, 0, 0) })\n }\n }\n\n // 워터마크 추가\n if (watermark) {\n const fontSize = 50\n const pageWidth = newPage.getWidth()\n const pageHeight = newPage.getHeight()\n\n newPage.drawText(watermark, {\n x: pageWidth / 2 - (fontSize * watermark.length) / 4,\n y: pageHeight / 2 - fontSize / 2,\n size: fontSize,\n color: rgb(0.75, 0.75, 0.75),\n rotate: degrees(-45),\n opacity: 0.5\n })\n }\n\n pdf.addPage(newPage)\n })\n\n // 페이지 카운트 업데이트\n __headless_pdf.pageCount += pages.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\nHeadlessPDFCapture.parameterSpec = [\n {\n type: 'string',\n name: 'htmlContent',\n label: 'html-content'\n },\n {\n type: 'select',\n name: 'format',\n label: 'page-format',\n options: [\n { label: 'A4', value: 'A4' },\n { label: 'A3', value: 'A3' },\n { label: 'Letter', value: 'Letter' },\n { label: 'Legal', value: 'Legal' }\n ],\n description: 'Select the paper format for the PDF'\n },\n {\n type: 'string',\n name: 'width',\n label: 'page-width',\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 description: 'Specify the height of the page (e.g., \"11in\", \"29.7cm\", \"800px\")'\n },\n {\n type: 'json',\n name: 'margin',\n label: 'page-margins',\n fields: [\n { type: 'string', name: 'top', label: 'Top Margin', description: 'Top margin (e.g., \"1in\")' },\n { type: 'string', name: 'right', label: 'Right Margin', description: 'Right margin (e.g., \"1in\")' },\n { type: 'string', name: 'bottom', label: 'Bottom Margin', description: 'Bottom margin (e.g., \"1in\")' },\n { type: 'string', name: 'left', label: 'Left Margin', description: 'Left margin (e.g., \"1in\")' }\n ],\n description: 'Set the margins for the page'\n },\n {\n type: 'number',\n name: 'scale',\n label: '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: 'displayHeaderFooter',\n label: 'display-header/footer',\n defaultValue: false,\n description: 'Display header and footer in the PDF'\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\nHeadlessPDFCapture.help = 'integration/task/headless-pdf-capture'\n\nTaskRegistry.registerTaskHandler('headless-pdf-capture', HeadlessPDFCapture)\n"]}
|
|
1
|
+
{"version":3,"file":"headless-pdf-capture.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-capture.ts"],"names":[],"mappings":"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","sourcesContent":["import { PDFDocument } from 'pdf-lib'\n\nimport { TaskRegistry } from '@things-factory/integration-base'\nimport { ConnectionManager } from '@things-factory/integration-base'\n\nasync function HeadlessPDFCapture(step, context) {\n var { connection: connectionName, params: stepOptions } = step\n var {\n htmlContent,\n format,\n width,\n height,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n scale,\n printBackground,\n landscape,\n preferCSSPageSize\n } = stepOptions || {}\n var { domain, __headless_pdf } = context\n\n if (!__headless_pdf) {\n throw new Error('It requires headless-pdf-open to be performed first')\n }\n\n var { pdf, pageCount } = __headless_pdf\n\n var headlessPool = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n let browser\n\n try {\n // Puppeteer를 사용하여 브라우저와 페이지 인스턴스를 생성\n browser = await headlessPool.acquire()\n const page = await browser.newPage()\n\n // 설정된 HTML 콘텐츠를 페이지에 설정\n await page.setContent(htmlContent)\n\n // PDF 페이지 생성 옵션 설정\n if (preferCSSPageSize) {\n width = undefined\n height = undefined\n }\n\n const pageOptions = {\n format,\n width,\n height,\n margin: {\n top: marginTop,\n right: marginRight,\n bottom: marginBottom,\n left: marginLeft\n },\n scale,\n printBackground,\n landscape,\n displayHeaderFooter: false, // 첫 번째 패스에서는 헤더/푸터를 렌더링하지 않음\n preferCSSPageSize\n }\n\n // 페이지를 PDF로 변환\n const pageBuffer = await page.pdf(pageOptions)\n\n // PDF-lib을 사용하여 PDF를 로드하고 추가 조작\n const pdfDoc = await PDFDocument.load(pageBuffer)\n const pages = await pdf.copyPages(pdfDoc, pdfDoc.getPageIndices())\n\n pages.forEach(newPage => {\n pdf.addPage(newPage)\n })\n\n // 페이지 카운트 업데이트\n __headless_pdf.pageCount += pages.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\nHeadlessPDFCapture.parameterSpec = [\n {\n type: 'textarea',\n name: 'htmlContent',\n label: 'html-content'\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\nHeadlessPDFCapture.help = 'integration/task/headless-pdf-capture'\n\nTaskRegistry.registerTaskHandler('headless-pdf-capture', HeadlessPDFCapture)\n"]}
|
|
@@ -2,13 +2,38 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const pdf_lib_1 = require("pdf-lib");
|
|
4
4
|
const integration_base_1 = require("@things-factory/integration-base");
|
|
5
|
+
const integration_base_2 = require("@things-factory/integration-base");
|
|
5
6
|
async function HeadlessPDFOpen(step, context) {
|
|
6
7
|
var { connection: connectionName, params: stepOptions } = step;
|
|
7
8
|
var { coverPage, lastPage, header, footer, watermark, fileName // 추가된 파일 이름
|
|
8
9
|
} = stepOptions || {};
|
|
9
|
-
// var { domain, data, variables } = context
|
|
10
|
-
// var headlessPool = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
|
|
11
10
|
const pdf = await pdf_lib_1.PDFDocument.create();
|
|
11
|
+
const headlessPool = integration_base_2.ConnectionManager.getConnectionInstanceByName(context.domain, connectionName);
|
|
12
|
+
let browser;
|
|
13
|
+
try {
|
|
14
|
+
browser = await headlessPool.acquire();
|
|
15
|
+
const page = await browser.newPage();
|
|
16
|
+
// Cover Page를 PDF로 변환
|
|
17
|
+
if (coverPage) {
|
|
18
|
+
await page.setContent(coverPage);
|
|
19
|
+
const coverPageBuffer = await page.pdf();
|
|
20
|
+
coverPage = coverPageBuffer;
|
|
21
|
+
}
|
|
22
|
+
// Last Page를 PDF로 변환
|
|
23
|
+
if (lastPage) {
|
|
24
|
+
await page.setContent(lastPage);
|
|
25
|
+
const lastPageBuffer = await page.pdf();
|
|
26
|
+
lastPage = lastPageBuffer;
|
|
27
|
+
}
|
|
28
|
+
await page.close();
|
|
29
|
+
headlessPool.release(browser);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (browser) {
|
|
33
|
+
await browser.close();
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
12
37
|
const data = {
|
|
13
38
|
pdf,
|
|
14
39
|
coverPage,
|
|
@@ -16,8 +41,8 @@ async function HeadlessPDFOpen(step, context) {
|
|
|
16
41
|
header,
|
|
17
42
|
footer,
|
|
18
43
|
watermark,
|
|
19
|
-
fileName,
|
|
20
|
-
pageCount: 0
|
|
44
|
+
fileName,
|
|
45
|
+
pageCount: 0
|
|
21
46
|
};
|
|
22
47
|
context.__headless_pdf = data;
|
|
23
48
|
return {
|
|
@@ -26,36 +51,38 @@ async function HeadlessPDFOpen(step, context) {
|
|
|
26
51
|
}
|
|
27
52
|
HeadlessPDFOpen.parameterSpec = [
|
|
28
53
|
{
|
|
29
|
-
type: '
|
|
54
|
+
type: 'textarea',
|
|
30
55
|
name: 'coverPage',
|
|
31
56
|
label: 'pdf-cover-page'
|
|
32
57
|
},
|
|
33
58
|
{
|
|
34
|
-
type: '
|
|
59
|
+
type: 'textarea',
|
|
35
60
|
name: 'lastPage',
|
|
36
61
|
label: 'pdf-last-page'
|
|
37
62
|
},
|
|
38
63
|
{
|
|
39
64
|
type: 'string',
|
|
40
65
|
name: 'header',
|
|
41
|
-
label: 'header'
|
|
66
|
+
label: 'header',
|
|
67
|
+
placeholder: 'Page <%= pageNumber %> of <%= totalPages %>'
|
|
42
68
|
},
|
|
43
69
|
{
|
|
44
70
|
type: 'string',
|
|
45
71
|
name: 'footer',
|
|
46
|
-
label: 'footer'
|
|
72
|
+
label: 'footer',
|
|
73
|
+
placeholder: 'Page <%= pageNumber %> of <%= totalPages %>'
|
|
47
74
|
},
|
|
48
75
|
{
|
|
49
76
|
type: 'string',
|
|
50
77
|
name: 'watermark',
|
|
51
|
-
label: 'watermark'
|
|
78
|
+
label: 'watermark'
|
|
52
79
|
},
|
|
53
80
|
{
|
|
54
81
|
type: 'string',
|
|
55
82
|
name: 'fileName',
|
|
56
|
-
label: 'filename'
|
|
83
|
+
label: 'filename'
|
|
57
84
|
}
|
|
58
85
|
];
|
|
59
|
-
HeadlessPDFOpen.help = 'integration/task/headless-open';
|
|
60
|
-
integration_base_1.TaskRegistry.registerTaskHandler('headless-open', HeadlessPDFOpen);
|
|
86
|
+
HeadlessPDFOpen.help = 'integration/task/headless-pdf-open';
|
|
87
|
+
integration_base_1.TaskRegistry.registerTaskHandler('headless-pdf-open', HeadlessPDFOpen);
|
|
61
88
|
//# sourceMappingURL=headless-pdf-open.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-pdf-open.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-open.ts"],"names":[],"mappings":";;AACA,qCAAqC;
|
|
1
|
+
{"version":3,"file":"headless-pdf-open.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-open.ts"],"names":[],"mappings":";;AACA,qCAAqC;AACrC,uEAA+D;AAC/D,uEAAoE;AAEpE,KAAK,UAAU,eAAe,CAAC,IAAI,EAAE,OAAO;IAC1C,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAC9D,IAAI,EACF,SAAS,EACT,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,CAAC,YAAY;MACtB,GAAG,WAAW,IAAI,EAAE,CAAA;IAErB,MAAM,GAAG,GAAG,MAAM,qBAAW,CAAC,MAAM,EAAE,CAAA;IAEtC,MAAM,YAAY,GAAG,oCAAiB,CAAC,2BAA2B,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAClG,IAAI,OAAO,CAAA;IAEX,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,CAAA;QACtC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QAEpC,sBAAsB;QACtB,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;YAChC,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAA;YACxC,SAAS,GAAG,eAAe,CAAA;QAC7B,CAAC;QAED,qBAAqB;QACrB,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;YAC/B,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAA;YACvC,QAAQ,GAAG,cAAc,CAAA;QAC3B,CAAC;QAED,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QAClB,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACvB,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;IAED,MAAM,IAAI,GAAG;QACX,GAAG;QACH,SAAS;QACT,QAAQ;QACR,MAAM;QACN,MAAM;QACN,SAAS;QACT,QAAQ;QACR,SAAS,EAAE,CAAC;KACb,CAAA;IAED,OAAO,CAAC,cAAc,GAAG,IAAI,CAAA;IAE7B,OAAO;QACL,IAAI;KACL,CAAA;AACH,CAAC;AAED,eAAe,CAAC,aAAa,GAAG;IAC9B;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,gBAAgB;KACxB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,eAAe;KACvB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,6CAA6C;KAC3D;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,6CAA6C;KAC3D;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,WAAW;KACnB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;KAClB;CACF,CAAA;AACD,eAAe,CAAC,IAAI,GAAG,oCAAoC,CAAA;AAE3D,+BAAY,CAAC,mBAAmB,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAA","sourcesContent":["import * as ejs from 'ejs'\nimport { PDFDocument } from 'pdf-lib'\nimport { TaskRegistry } from '@things-factory/integration-base'\nimport { ConnectionManager } from '@things-factory/integration-base'\n\nasync function HeadlessPDFOpen(step, context) {\n var { connection: connectionName, params: stepOptions } = step\n var {\n coverPage,\n lastPage,\n header,\n footer,\n watermark,\n fileName // 추가된 파일 이름\n } = stepOptions || {}\n\n const pdf = await PDFDocument.create()\n\n const headlessPool = ConnectionManager.getConnectionInstanceByName(context.domain, connectionName)\n let browser\n\n try {\n browser = await headlessPool.acquire()\n const page = await browser.newPage()\n\n // Cover Page를 PDF로 변환\n if (coverPage) {\n await page.setContent(coverPage)\n const coverPageBuffer = await page.pdf()\n coverPage = coverPageBuffer\n }\n\n // Last Page를 PDF로 변환\n if (lastPage) {\n await page.setContent(lastPage)\n const lastPageBuffer = await page.pdf()\n lastPage = lastPageBuffer\n }\n\n await page.close()\n headlessPool.release(browser)\n } catch (error) {\n if (browser) {\n await browser.close()\n }\n throw error\n }\n\n const data = {\n pdf,\n coverPage,\n lastPage,\n header,\n footer,\n watermark,\n fileName,\n pageCount: 0\n }\n\n context.__headless_pdf = data\n\n return {\n data\n }\n}\n\nHeadlessPDFOpen.parameterSpec = [\n {\n type: 'textarea',\n name: 'coverPage',\n label: 'pdf-cover-page'\n },\n {\n type: 'textarea',\n name: 'lastPage',\n label: 'pdf-last-page'\n },\n {\n type: 'string',\n name: 'header',\n label: 'header',\n placeholder: 'Page <%= pageNumber %> of <%= totalPages %>'\n },\n {\n type: 'string',\n name: 'footer',\n label: 'footer',\n placeholder: 'Page <%= pageNumber %> of <%= totalPages %>'\n },\n {\n type: 'string',\n name: 'watermark',\n label: 'watermark'\n },\n {\n type: 'string',\n name: 'fileName',\n label: 'filename'\n }\n]\nHeadlessPDFOpen.help = 'integration/task/headless-pdf-open'\n\nTaskRegistry.registerTaskHandler('headless-pdf-open', HeadlessPDFOpen)\n"]}
|
|
@@ -3,20 +3,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const fs = tslib_1.__importStar(require("fs"));
|
|
5
5
|
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const ejs = tslib_1.__importStar(require("ejs"));
|
|
6
7
|
const pdf_lib_1 = require("pdf-lib");
|
|
7
8
|
const integration_base_1 = require("@things-factory/integration-base");
|
|
9
|
+
async function HeadlessPDFRenderHeadersAndFooters(pdf, headerTemplate, footerTemplate, watermark, totalPages) {
|
|
10
|
+
const pages = pdf.getPages();
|
|
11
|
+
pages.forEach((page, index) => {
|
|
12
|
+
const pageNumber = index + 1;
|
|
13
|
+
const pageWidth = page.getWidth();
|
|
14
|
+
// 헤더와 푸터 렌더링 (페이지 번호 및 총 페이지 수 포함)
|
|
15
|
+
if (headerTemplate || footerTemplate) {
|
|
16
|
+
const renderedHeader = headerTemplate
|
|
17
|
+
? ejs.render(headerTemplate, {
|
|
18
|
+
pageNumber,
|
|
19
|
+
totalPages
|
|
20
|
+
})
|
|
21
|
+
: '';
|
|
22
|
+
const renderedFooter = footerTemplate
|
|
23
|
+
? ejs.render(footerTemplate, {
|
|
24
|
+
pageNumber,
|
|
25
|
+
totalPages
|
|
26
|
+
})
|
|
27
|
+
: '';
|
|
28
|
+
const fontSize = 12; // 폰트 크기 설정
|
|
29
|
+
const textWidthHeader = fontSize * renderedHeader.length * 0.5;
|
|
30
|
+
const textWidthFooter = fontSize * renderedFooter.length * 0.5;
|
|
31
|
+
// Header/Footer 그리기 (가운데 정렬)
|
|
32
|
+
if (renderedHeader) {
|
|
33
|
+
page.drawText(renderedHeader, {
|
|
34
|
+
x: (pageWidth - textWidthHeader) / 2,
|
|
35
|
+
y: page.getHeight() - 30,
|
|
36
|
+
size: fontSize,
|
|
37
|
+
color: (0, pdf_lib_1.rgb)(0, 0, 0)
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (renderedFooter) {
|
|
41
|
+
page.drawText(renderedFooter, {
|
|
42
|
+
x: (pageWidth - textWidthFooter) / 2,
|
|
43
|
+
y: 30,
|
|
44
|
+
size: fontSize,
|
|
45
|
+
color: (0, pdf_lib_1.rgb)(0, 0, 0)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// 워터마크 추가
|
|
50
|
+
if (watermark) {
|
|
51
|
+
const fontSize = 50;
|
|
52
|
+
page.drawText(watermark, {
|
|
53
|
+
x: pageWidth / 2 - (fontSize * watermark.length) / 4,
|
|
54
|
+
y: page.getHeight() / 2 - fontSize / 2,
|
|
55
|
+
size: fontSize,
|
|
56
|
+
color: (0, pdf_lib_1.rgb)(0.75, 0.75, 0.75),
|
|
57
|
+
rotate: (0, pdf_lib_1.degrees)(-45),
|
|
58
|
+
opacity: 0.5
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
8
63
|
async function HeadlessPDFSave(step, context) {
|
|
9
64
|
var { params: stepOptions } = step;
|
|
10
65
|
var { outputPath } = stepOptions || {};
|
|
11
|
-
var {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
66
|
+
var { domain, data, __headless_pdf } = context;
|
|
67
|
+
var { pdf, coverPage, lastPage, watermark, fileName } = __headless_pdf;
|
|
68
|
+
const totalPages = pdf.getPages().length;
|
|
69
|
+
await HeadlessPDFRenderHeadersAndFooters(pdf, __headless_pdf.header, __headless_pdf.footer, watermark, totalPages);
|
|
70
|
+
const finalFileName = fileName || `document-${Date.now()}.pdf`;
|
|
71
|
+
const savePath = path.join(outputPath || './attachments', finalFileName);
|
|
16
72
|
if (coverPage) {
|
|
17
73
|
const coverPageDoc = await pdf_lib_1.PDFDocument.load(coverPage);
|
|
18
74
|
const [cover] = await pdf.copyPages(coverPageDoc, [0]);
|
|
19
|
-
// 커버 페이지에 워터마크 추가
|
|
20
75
|
if (watermark) {
|
|
21
76
|
const fontSize = 50;
|
|
22
77
|
const pageWidth = cover.getWidth();
|
|
@@ -35,7 +90,6 @@ async function HeadlessPDFSave(step, context) {
|
|
|
35
90
|
if (lastPage) {
|
|
36
91
|
const lastPageDoc = await pdf_lib_1.PDFDocument.load(lastPage);
|
|
37
92
|
const [last] = await pdf.copyPages(lastPageDoc, [0]);
|
|
38
|
-
// 마지막 페이지에 워터마크 추가
|
|
39
93
|
if (watermark) {
|
|
40
94
|
const fontSize = 50;
|
|
41
95
|
const pageWidth = last.getWidth();
|
|
@@ -51,12 +105,11 @@ async function HeadlessPDFSave(step, context) {
|
|
|
51
105
|
}
|
|
52
106
|
pdf.addPage(last);
|
|
53
107
|
}
|
|
54
|
-
// PDF 파일로 저장
|
|
55
108
|
const pdfBytes = await pdf.save();
|
|
56
109
|
fs.writeFileSync(savePath, pdfBytes);
|
|
57
110
|
return {
|
|
58
111
|
data: {
|
|
59
|
-
link: savePath
|
|
112
|
+
link: savePath
|
|
60
113
|
}
|
|
61
114
|
};
|
|
62
115
|
}
|
|
@@ -64,7 +117,7 @@ HeadlessPDFSave.parameterSpec = [
|
|
|
64
117
|
{
|
|
65
118
|
type: 'string',
|
|
66
119
|
name: 'outputPath',
|
|
67
|
-
label: 'output-path'
|
|
120
|
+
label: 'output-path'
|
|
68
121
|
}
|
|
69
122
|
];
|
|
70
123
|
HeadlessPDFSave.help = 'integration/task/headless-pdf-save';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-pdf-save.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-save.ts"],"names":[],"mappings":";;;AAAA,+CAAwB;AACxB,mDAA4B;AAC5B,qCAAmD;AAEnD,uEAA+D;AAE/D,KAAK,UAAU,eAAe,CAAC,IAAI,EAAE,OAAO;IAC1C,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAClC,IAAI,EAAE,UAAU,EAAE,GAAG,WAAW,IAAI,EAAE,CAAA;IACtC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"headless-pdf-save.js","sourceRoot":"","sources":["../../../server/engine/task/headless-pdf-save.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs'\nimport * as path from 'path'\nimport * as ejs from 'ejs'\nimport { PDFDocument, rgb, degrees } from 'pdf-lib'\n\nimport { TaskRegistry } from '@things-factory/integration-base'\n\nasync function HeadlessPDFRenderHeadersAndFooters(pdf, headerTemplate, footerTemplate, watermark, totalPages) {\n const pages = pdf.getPages()\n\n pages.forEach((page, index) => {\n const pageNumber = index + 1\n const pageWidth = page.getWidth()\n\n // 헤더와 푸터 렌더링 (페이지 번호 및 총 페이지 수 포함)\n if (headerTemplate || footerTemplate) {\n const renderedHeader = headerTemplate\n ? ejs.render(headerTemplate, {\n pageNumber,\n totalPages\n })\n : ''\n const renderedFooter = footerTemplate\n ? ejs.render(footerTemplate, {\n pageNumber,\n totalPages\n })\n : ''\n\n const fontSize = 12 // 폰트 크기 설정\n const textWidthHeader = fontSize * renderedHeader.length * 0.5\n const textWidthFooter = fontSize * renderedFooter.length * 0.5\n\n // Header/Footer 그리기 (가운데 정렬)\n if (renderedHeader) {\n page.drawText(renderedHeader, {\n x: (pageWidth - textWidthHeader) / 2,\n y: page.getHeight() - 30,\n size: fontSize,\n color: rgb(0, 0, 0)\n })\n }\n if (renderedFooter) {\n page.drawText(renderedFooter, {\n x: (pageWidth - textWidthFooter) / 2,\n y: 30,\n size: fontSize,\n color: rgb(0, 0, 0)\n })\n }\n }\n\n // 워터마크 추가\n if (watermark) {\n const fontSize = 50\n\n page.drawText(watermark, {\n x: pageWidth / 2 - (fontSize * watermark.length) / 4,\n y: page.getHeight() / 2 - fontSize / 2,\n size: fontSize,\n color: rgb(0.75, 0.75, 0.75),\n rotate: degrees(-45),\n opacity: 0.5\n })\n }\n })\n}\n\nasync function HeadlessPDFSave(step, context) {\n var { params: stepOptions } = step\n var { outputPath } = stepOptions || {}\n var { domain, data, __headless_pdf } = context\n\n var { pdf, coverPage, lastPage, watermark, fileName } = __headless_pdf\n\n const totalPages = pdf.getPages().length\n await HeadlessPDFRenderHeadersAndFooters(pdf, __headless_pdf.header, __headless_pdf.footer, watermark, totalPages)\n\n const finalFileName = fileName || `document-${Date.now()}.pdf`\n const savePath = path.join(outputPath || './attachments', finalFileName)\n\n if (coverPage) {\n const coverPageDoc = await PDFDocument.load(coverPage)\n const [cover] = await pdf.copyPages(coverPageDoc, [0])\n\n if (watermark) {\n const fontSize = 50\n const pageWidth = cover.getWidth()\n const pageHeight = cover.getHeight()\n\n cover.drawText(watermark, {\n x: pageWidth / 2 - (fontSize * watermark.length) / 4,\n y: pageHeight / 2 - fontSize / 2,\n size: fontSize,\n color: rgb(0.75, 0.75, 0.75),\n rotate: degrees(-45),\n opacity: 0.5\n })\n }\n\n pdf.insertPage(0, cover)\n }\n\n if (lastPage) {\n const lastPageDoc = await PDFDocument.load(lastPage)\n const [last] = await pdf.copyPages(lastPageDoc, [0])\n\n if (watermark) {\n const fontSize = 50\n const pageWidth = last.getWidth()\n const pageHeight = last.getHeight()\n\n last.drawText(watermark, {\n x: pageWidth / 2 - (fontSize * watermark.length) / 4,\n y: pageHeight / 2 - fontSize / 2,\n size: fontSize,\n color: rgb(0.75, 0.75, 0.75),\n rotate: degrees(-45),\n opacity: 0.5\n })\n }\n\n pdf.addPage(last)\n }\n\n const pdfBytes = await pdf.save()\n fs.writeFileSync(savePath, pdfBytes)\n\n return {\n data: {\n link: savePath\n }\n }\n}\n\nHeadlessPDFSave.parameterSpec = [\n {\n type: 'string',\n name: 'outputPath',\n label: 'output-path'\n }\n]\n\nHeadlessPDFSave.help = 'integration/task/headless-pdf-save'\n\nTaskRegistry.registerTaskHandler('headless-pdf-save', HeadlessPDFSave)\n"]}
|