@shad0wz7/nestjs-pdf 2.5.0 → 2.5.2
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/README.md +3 -1
- package/dist/index.cjs +256 -255
- package/dist/index.d.cts +46 -169
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +53 -176
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +255 -255
- package/dist/index.mjs.map +1 -1
- package/package.json +107 -86
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# nestjs-pdf
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
|
|
4
|
+
[](https://github.com/ShadowZ11/nestjs-pdf?tab=MIT-1-ov-file)
|
|
4
5
|
[](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
|
|
5
6
|
[](https://github.com/Shadowz11/nestjs-pdf/actions/workflows/ci.yml)
|
|
6
7
|
[](https://codecov.io/github/ShadowZ11/nestjs-pdf)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
A NestJS library to generate and manipulate PDFs using Puppeteer and multiple template engines, including Handlebars, EJS, Pug, MJML, Nunjucks, Eta and Mustache.
|
|
10
12
|
|
package/dist/index.cjs
CHANGED
|
@@ -22,24 +22,186 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
enumerable: true
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
//#endregion
|
|
25
|
-
let _nestjs_common = require("@nestjs/common");
|
|
26
|
-
let node_fs = require("node:fs");
|
|
27
25
|
let node_path = require("node:path");
|
|
28
|
-
let
|
|
29
|
-
let
|
|
30
|
-
let
|
|
26
|
+
let _nestjs_common = require("@nestjs/common");
|
|
27
|
+
let pdf_lib = require("pdf-lib");
|
|
28
|
+
let _gboutte_nestjs_hbs = require("@gboutte/nestjs-hbs");
|
|
31
29
|
let p_limit = require("p-limit");
|
|
32
30
|
p_limit = __toESM(p_limit);
|
|
33
|
-
let
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
let
|
|
31
|
+
let deepmerge_ts = require("deepmerge-ts");
|
|
32
|
+
let node_fs = require("node:fs");
|
|
33
|
+
let _puppeteer_browsers = require("@puppeteer/browsers");
|
|
34
|
+
let puppeteer = require("puppeteer");
|
|
37
35
|
let ejs = require("ejs");
|
|
38
|
-
let nunjucks = require("nunjucks");
|
|
39
36
|
let eta = require("eta");
|
|
37
|
+
let mjml = require("mjml");
|
|
38
|
+
mjml = __toESM(mjml);
|
|
40
39
|
let mustache = require("mustache");
|
|
41
40
|
mustache = __toESM(mustache);
|
|
42
|
-
let
|
|
41
|
+
let nunjucks = require("nunjucks");
|
|
42
|
+
nunjucks = __toESM(nunjucks);
|
|
43
|
+
let pug = require("pug");
|
|
44
|
+
//#region src/helpers/getRelativePath.helper.ts
|
|
45
|
+
/**
|
|
46
|
+
* Get the relative path to a value from the module directory.
|
|
47
|
+
* @param moduleDir the path of the module, typically __dirname
|
|
48
|
+
* @param relativeToModule the path to the value relative to the module directory
|
|
49
|
+
* @returns the relative path from the current working directory to the value
|
|
50
|
+
* @example
|
|
51
|
+
* getRelativePathToValue(__dirname, './templates/my-template.hbs');
|
|
52
|
+
* // returns 'context/pdf/templates/my-template.hbs' if the current working directory is '/
|
|
53
|
+
*/
|
|
54
|
+
function getRelativePathToValue(moduleDir, relativeToModule) {
|
|
55
|
+
const fullTemplatePath = (0, node_path.resolve)(moduleDir, relativeToModule);
|
|
56
|
+
return (0, node_path.relative)(process.cwd(), fullTemplatePath);
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/helpers/signature.helper.ts
|
|
60
|
+
let pdfjsPromise;
|
|
61
|
+
async function loadPdfjs() {
|
|
62
|
+
if (!pdfjsPromise) try {
|
|
63
|
+
pdfjsPromise = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
64
|
+
} catch (error) {
|
|
65
|
+
pdfjsPromise = void 0;
|
|
66
|
+
throw new Error([
|
|
67
|
+
"Failed to load pdfjs-dist.",
|
|
68
|
+
"If you use anchor-based signature placement in Node.js, make sure @napi-rs/canvas is installed.",
|
|
69
|
+
"Try: npm install @napi-rs/canvas",
|
|
70
|
+
"",
|
|
71
|
+
`Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
72
|
+
].join("\n"), { cause: error });
|
|
73
|
+
}
|
|
74
|
+
return pdfjsPromise;
|
|
75
|
+
}
|
|
76
|
+
function toU8(input) {
|
|
77
|
+
return new Uint8Array(input);
|
|
78
|
+
}
|
|
79
|
+
async function findAnchorPosition(pdfBytes, anchorText) {
|
|
80
|
+
const pdfjsLib = await loadPdfjs();
|
|
81
|
+
const loadingTask = pdfjsLib.getDocument({ data: pdfBytes });
|
|
82
|
+
try {
|
|
83
|
+
const pdf = await loadingTask.promise;
|
|
84
|
+
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
85
|
+
const page = await pdf.getPage(pageNum);
|
|
86
|
+
const viewport = page.getViewport({ scale: 1 });
|
|
87
|
+
const textContent = await page.getTextContent();
|
|
88
|
+
for (const itemRaw of textContent.items) {
|
|
89
|
+
const item = itemRaw;
|
|
90
|
+
if (!item.str?.includes(anchorText)) continue;
|
|
91
|
+
const [_a, _b, _c, _d, e, f] = pdfjsLib.Util.transform(viewport.transform, item.transform);
|
|
92
|
+
const x = e;
|
|
93
|
+
const y = viewport.height - f;
|
|
94
|
+
return {
|
|
95
|
+
pageIndex: pageNum - 1,
|
|
96
|
+
x,
|
|
97
|
+
y,
|
|
98
|
+
pageWidth: viewport.width,
|
|
99
|
+
pageHeight: viewport.height
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
} finally {
|
|
105
|
+
await loadingTask.destroy();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function addSignatureFieldAt(pdfDoc, pageIndex, opts) {
|
|
109
|
+
const { fieldName, x, y, width, height } = opts;
|
|
110
|
+
const context = pdfDoc.context;
|
|
111
|
+
const page = pdfDoc.getPages()[pageIndex];
|
|
112
|
+
if (!page) throw new Error(`Page ${pageIndex} not found`);
|
|
113
|
+
const rectArray = context.obj([
|
|
114
|
+
pdf_lib.PDFNumber.of(x),
|
|
115
|
+
pdf_lib.PDFNumber.of(y),
|
|
116
|
+
pdf_lib.PDFNumber.of(x + width),
|
|
117
|
+
pdf_lib.PDFNumber.of(y + height)
|
|
118
|
+
]);
|
|
119
|
+
const widgetDict = context.obj({
|
|
120
|
+
Type: pdf_lib.PDFName.of("Annot"),
|
|
121
|
+
Subtype: pdf_lib.PDFName.of("Widget"),
|
|
122
|
+
Rect: rectArray,
|
|
123
|
+
F: pdf_lib.PDFNumber.of(4),
|
|
124
|
+
P: page.ref
|
|
125
|
+
});
|
|
126
|
+
const widgetRef = context.register(widgetDict);
|
|
127
|
+
const fieldDict = context.obj({
|
|
128
|
+
FT: pdf_lib.PDFName.of("Sig"),
|
|
129
|
+
T: pdf_lib.PDFString.of(fieldName),
|
|
130
|
+
Ff: pdf_lib.PDFNumber.of(0),
|
|
131
|
+
Kids: context.obj([widgetRef])
|
|
132
|
+
});
|
|
133
|
+
const fieldRef = context.register(fieldDict);
|
|
134
|
+
widgetDict.set(pdf_lib.PDFName.of("Parent"), fieldRef);
|
|
135
|
+
let annots = page.node.lookupMaybe(pdf_lib.PDFName.of("Annots"), pdf_lib.PDFArray);
|
|
136
|
+
if (!annots) {
|
|
137
|
+
annots = context.obj([]);
|
|
138
|
+
page.node.set(pdf_lib.PDFName.of("Annots"), annots);
|
|
139
|
+
}
|
|
140
|
+
annots.push(widgetRef);
|
|
141
|
+
let acroForm = pdfDoc.catalog.lookupMaybe(pdf_lib.PDFName.of("AcroForm"), pdf_lib.PDFDict);
|
|
142
|
+
if (!acroForm) {
|
|
143
|
+
acroForm = context.obj({
|
|
144
|
+
Fields: context.obj([]),
|
|
145
|
+
SigFlags: pdf_lib.PDFNumber.of(3)
|
|
146
|
+
});
|
|
147
|
+
const acroFormRef = context.register(acroForm);
|
|
148
|
+
pdfDoc.catalog.set(pdf_lib.PDFName.of("AcroForm"), acroFormRef);
|
|
149
|
+
}
|
|
150
|
+
let fields = acroForm.lookupMaybe(pdf_lib.PDFName.of("Fields"), pdf_lib.PDFArray);
|
|
151
|
+
if (!fields) {
|
|
152
|
+
fields = context.obj([]);
|
|
153
|
+
acroForm.set(pdf_lib.PDFName.of("Fields"), fields);
|
|
154
|
+
}
|
|
155
|
+
fields.push(fieldRef);
|
|
156
|
+
return {
|
|
157
|
+
fieldRef,
|
|
158
|
+
widgetRef
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function addSignatureFieldUsingAnchor(pdf, fieldName = "SignatureDebtor", anchorText = "__SIG_DEBTOR_ANCHOR__") {
|
|
162
|
+
const bytes = toU8(pdf);
|
|
163
|
+
const anchor = await findAnchorPosition(bytes.slice(), anchorText);
|
|
164
|
+
const pdfDoc = await pdf_lib.PDFDocument.load(bytes);
|
|
165
|
+
const sigWidth = 240;
|
|
166
|
+
const sigHeight = 50;
|
|
167
|
+
if (anchor) {
|
|
168
|
+
const pageIndex = anchor.pageIndex;
|
|
169
|
+
const { width: pw, height: ph } = pdfDoc.getPages()[pageIndex].getSize();
|
|
170
|
+
const padX = 40;
|
|
171
|
+
const padY = 7;
|
|
172
|
+
let x = anchor.x - sigWidth / 2 + padX;
|
|
173
|
+
let y = anchor.y - sigHeight + padY;
|
|
174
|
+
const margin = 10;
|
|
175
|
+
x = Math.max(margin, Math.min(x, pw - sigWidth - margin));
|
|
176
|
+
y = Math.max(margin, Math.min(y, ph - sigHeight - margin));
|
|
177
|
+
addSignatureFieldAt(pdfDoc, anchor.pageIndex, {
|
|
178
|
+
fieldName,
|
|
179
|
+
x,
|
|
180
|
+
y,
|
|
181
|
+
width: sigWidth,
|
|
182
|
+
height: sigHeight
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
const pages = pdfDoc.getPages();
|
|
186
|
+
const pageIndex = pages.length - 1;
|
|
187
|
+
const { width: pageWidth } = pages[pageIndex].getSize();
|
|
188
|
+
addSignatureFieldAt(pdfDoc, pageIndex, {
|
|
189
|
+
fieldName,
|
|
190
|
+
x: (pageWidth - sigWidth) / 2,
|
|
191
|
+
y: 90,
|
|
192
|
+
width: sigWidth,
|
|
193
|
+
height: sigHeight
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return await pdfDoc.save();
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/helpers/deepMergePdfparams.ts
|
|
200
|
+
function mergePuppeteerParameters(defaults, overrides) {
|
|
201
|
+
if (!overrides) return { ...defaults };
|
|
202
|
+
return (0, deepmerge_ts.deepmerge)(defaults, overrides);
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
43
205
|
//#region src/helpers/tokens.ts
|
|
44
206
|
const PDF_PARAMETERS = "PDF_PARAMETERS";
|
|
45
207
|
const HANDLEBARS_PARAMETERS = "HANDLEBARS_PARAMETERS";
|
|
@@ -271,8 +433,8 @@ let BrowserService = class BrowserService {
|
|
|
271
433
|
this.useLockedBrowser = useLockedBrowser;
|
|
272
434
|
}
|
|
273
435
|
async hasBrowserInstalled(browser, buildId) {
|
|
274
|
-
return (await (0, _puppeteer_browsers.getInstalledBrowsers)({ cacheDir: this.cacheDir })).find((
|
|
275
|
-
return
|
|
436
|
+
return (await (0, _puppeteer_browsers.getInstalledBrowsers)({ cacheDir: this.cacheDir })).find((insBrowser) => {
|
|
437
|
+
return insBrowser.browser === browser && insBrowser.buildId === buildId;
|
|
276
438
|
}) !== void 0;
|
|
277
439
|
}
|
|
278
440
|
async getExecutablePath() {
|
|
@@ -285,8 +447,8 @@ let BrowserService = class BrowserService {
|
|
|
285
447
|
_nestjs_common.Logger.log(`Using locked browser ${buildId}`, "NestJsPdf");
|
|
286
448
|
}
|
|
287
449
|
buildId ??= await (0, _puppeteer_browsers.resolveBuildId)(browser, browserPlatform, versionTag);
|
|
288
|
-
const installedBrowser = (await (0, _puppeteer_browsers.getInstalledBrowsers)({ cacheDir: this.cacheDir })).find((
|
|
289
|
-
return
|
|
450
|
+
const installedBrowser = (await (0, _puppeteer_browsers.getInstalledBrowsers)({ cacheDir: this.cacheDir })).find((insBrowser) => {
|
|
451
|
+
return insBrowser.browser === browser && insBrowser.buildId === buildId;
|
|
290
452
|
});
|
|
291
453
|
if (installedBrowser === void 0) {
|
|
292
454
|
const newinstalledBrowser = await this.install();
|
|
@@ -333,45 +495,6 @@ BrowserService = __decorate([
|
|
|
333
495
|
__decorateMetadata("design:paramtypes", [Object])
|
|
334
496
|
], BrowserService);
|
|
335
497
|
//#endregion
|
|
336
|
-
//#region src/helpers/deepMergePdfparams.ts
|
|
337
|
-
function mergePuppeteerParameters(defaults, overrides) {
|
|
338
|
-
if (!overrides) return { ...defaults };
|
|
339
|
-
return (0, deepmerge_ts.deepmerge)(defaults, overrides);
|
|
340
|
-
}
|
|
341
|
-
//#endregion
|
|
342
|
-
//#region src/puppeteer/engines/mjml/mjml.service.ts
|
|
343
|
-
let MjmlService = class MjmlService {
|
|
344
|
-
async render(template, options) {
|
|
345
|
-
const { html } = await (0, mjml.default)(template, options);
|
|
346
|
-
return html;
|
|
347
|
-
}
|
|
348
|
-
async renderFile(filePath, options) {
|
|
349
|
-
const template = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
350
|
-
return await this.render(template, options);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
MjmlService = __decorate([(0, _nestjs_common.Injectable)()], MjmlService);
|
|
354
|
-
//#endregion
|
|
355
|
-
//#region src/puppeteer/engines/pug/pug.service.ts
|
|
356
|
-
let PugService = class PugService {
|
|
357
|
-
render(template, data = {}, options) {
|
|
358
|
-
try {
|
|
359
|
-
return (0, pug.compile)(template, { ...options })(data);
|
|
360
|
-
} catch (error) {
|
|
361
|
-
throw new Error(`Pug rendering failed: ${String(error)}`, { cause: error });
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
renderFile(filePath, data = {}, options) {
|
|
365
|
-
try {
|
|
366
|
-
const template = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
367
|
-
return this.render(template, data, options);
|
|
368
|
-
} catch (error) {
|
|
369
|
-
throw new Error(`Pug file rendering failed: ${String(error)}`, { cause: error });
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
PugService = __decorate([(0, _nestjs_common.Injectable)()], PugService);
|
|
374
|
-
//#endregion
|
|
375
498
|
//#region src/puppeteer/engines/ejs/ejs.service.ts
|
|
376
499
|
let EjsService = class EjsService {
|
|
377
500
|
async render(template, data = {}, options) {
|
|
@@ -397,52 +520,6 @@ let EjsService = class EjsService {
|
|
|
397
520
|
};
|
|
398
521
|
EjsService = __decorate([(0, _nestjs_common.Injectable)()], EjsService);
|
|
399
522
|
//#endregion
|
|
400
|
-
//#region src/puppeteer/engines/nunjucks/nunjucks.service.ts
|
|
401
|
-
let NunjucksService = class NunjucksService {
|
|
402
|
-
constructor() {
|
|
403
|
-
(0, nunjucks.configure)({
|
|
404
|
-
noCache: true,
|
|
405
|
-
trimBlocks: true,
|
|
406
|
-
lstripBlocks: true
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
render(template, data = {}, options) {
|
|
410
|
-
try {
|
|
411
|
-
if (options) {
|
|
412
|
-
const configOptions = {
|
|
413
|
-
noCache: options.noCache ?? true,
|
|
414
|
-
watch: options.watch ?? false,
|
|
415
|
-
throwOnUndefined: options.throwOnUndefined ?? false,
|
|
416
|
-
trimBlocks: options.trimBlocks ?? true,
|
|
417
|
-
lstripBlocks: options.lstripBlocks ?? true
|
|
418
|
-
};
|
|
419
|
-
(0, nunjucks.configure)(configOptions);
|
|
420
|
-
}
|
|
421
|
-
return (0, nunjucks.renderString)(template, data);
|
|
422
|
-
} catch (error) {
|
|
423
|
-
throw new Error(`Nunjucks rendering failed: ${String(error)}`, { cause: error });
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
renderFile(filePath, data = {}, options) {
|
|
427
|
-
try {
|
|
428
|
-
if (options) {
|
|
429
|
-
const configOptions = {
|
|
430
|
-
noCache: options.noCache ?? true,
|
|
431
|
-
watch: options.watch ?? false,
|
|
432
|
-
throwOnUndefined: options.throwOnUndefined ?? false,
|
|
433
|
-
trimBlocks: options.trimBlocks ?? true,
|
|
434
|
-
lstripBlocks: options.lstripBlocks ?? true
|
|
435
|
-
};
|
|
436
|
-
(0, nunjucks.configure)(configOptions);
|
|
437
|
-
}
|
|
438
|
-
return (0, nunjucks.render)(filePath, data);
|
|
439
|
-
} catch (error) {
|
|
440
|
-
throw new Error(`Nunjucks file rendering failed: ${String(error)}`, { cause: error });
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
NunjucksService = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [])], NunjucksService);
|
|
445
|
-
//#endregion
|
|
446
523
|
//#region src/puppeteer/engines/eta/eta.service.ts
|
|
447
524
|
let EtaService = class EtaService {
|
|
448
525
|
eta;
|
|
@@ -512,6 +589,19 @@ let EtaService = class EtaService {
|
|
|
512
589
|
};
|
|
513
590
|
EtaService = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [])], EtaService);
|
|
514
591
|
//#endregion
|
|
592
|
+
//#region src/puppeteer/engines/mjml/mjml.service.ts
|
|
593
|
+
let MjmlService = class MjmlService {
|
|
594
|
+
async render(template, options) {
|
|
595
|
+
const { html } = await (0, mjml.default)(template, options);
|
|
596
|
+
return html;
|
|
597
|
+
}
|
|
598
|
+
async renderFile(filePath, options) {
|
|
599
|
+
const template = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
600
|
+
return await this.render(template, options);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
MjmlService = __decorate([(0, _nestjs_common.Injectable)()], MjmlService);
|
|
604
|
+
//#endregion
|
|
515
605
|
//#region src/puppeteer/engines/mustache/mustache.service.ts
|
|
516
606
|
let MustacheService = class MustacheService {
|
|
517
607
|
templateCache = /* @__PURE__ */ new Map();
|
|
@@ -570,6 +660,72 @@ let MustacheService = class MustacheService {
|
|
|
570
660
|
};
|
|
571
661
|
MustacheService = __decorate([(0, _nestjs_common.Injectable)()], MustacheService);
|
|
572
662
|
//#endregion
|
|
663
|
+
//#region src/puppeteer/engines/nunjucks/nunjucks.service.ts
|
|
664
|
+
let NunjucksService = class NunjucksService {
|
|
665
|
+
constructor() {
|
|
666
|
+
nunjucks.default.configure({
|
|
667
|
+
noCache: true,
|
|
668
|
+
trimBlocks: true,
|
|
669
|
+
lstripBlocks: true
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
render(template, data = {}, options) {
|
|
673
|
+
try {
|
|
674
|
+
if (options) {
|
|
675
|
+
const configOptions = {
|
|
676
|
+
noCache: options.noCache ?? true,
|
|
677
|
+
watch: options.watch ?? false,
|
|
678
|
+
throwOnUndefined: options.throwOnUndefined ?? false,
|
|
679
|
+
trimBlocks: options.trimBlocks ?? true,
|
|
680
|
+
lstripBlocks: options.lstripBlocks ?? true
|
|
681
|
+
};
|
|
682
|
+
nunjucks.default.configure(configOptions);
|
|
683
|
+
}
|
|
684
|
+
return nunjucks.default.renderString(template, data);
|
|
685
|
+
} catch (error) {
|
|
686
|
+
throw new Error(`Nunjucks rendering failed: ${String(error)}`, { cause: error });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
renderFile(filePath, data = {}, options) {
|
|
690
|
+
try {
|
|
691
|
+
if (options) {
|
|
692
|
+
const configOptions = {
|
|
693
|
+
noCache: options.noCache ?? true,
|
|
694
|
+
watch: options.watch ?? false,
|
|
695
|
+
throwOnUndefined: options.throwOnUndefined ?? false,
|
|
696
|
+
trimBlocks: options.trimBlocks ?? true,
|
|
697
|
+
lstripBlocks: options.lstripBlocks ?? true
|
|
698
|
+
};
|
|
699
|
+
nunjucks.default.configure(configOptions);
|
|
700
|
+
}
|
|
701
|
+
return nunjucks.default.render(filePath, data);
|
|
702
|
+
} catch (error) {
|
|
703
|
+
throw new Error(`Nunjucks file rendering failed: ${String(error)}`, { cause: error });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
NunjucksService = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [])], NunjucksService);
|
|
708
|
+
//#endregion
|
|
709
|
+
//#region src/puppeteer/engines/pug/pug.service.ts
|
|
710
|
+
let PugService = class PugService {
|
|
711
|
+
render(template, data = {}, options) {
|
|
712
|
+
try {
|
|
713
|
+
return (0, pug.compile)(template, { ...options })(data);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
throw new Error(`Pug rendering failed: ${String(error)}`, { cause: error });
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
renderFile(filePath, data = {}, options) {
|
|
719
|
+
try {
|
|
720
|
+
const template = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
721
|
+
return this.render(template, data, options);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
throw new Error(`Pug file rendering failed: ${String(error)}`, { cause: error });
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
PugService = __decorate([(0, _nestjs_common.Injectable)()], PugService);
|
|
728
|
+
//#endregion
|
|
573
729
|
//#region src/puppeteer/libs/requireService.utils.ts
|
|
574
730
|
function requireService(service, name) {
|
|
575
731
|
if (!service) throw new Error(`${name} service is not available. If the problem persists, open an issue in the repo.`);
|
|
@@ -750,146 +906,6 @@ PuppeteerService = __decorate([
|
|
|
750
906
|
])
|
|
751
907
|
], PuppeteerService);
|
|
752
908
|
//#endregion
|
|
753
|
-
//#region src/helpers/signature.helper.ts
|
|
754
|
-
let pdfjsPromise;
|
|
755
|
-
async function loadPdfjs() {
|
|
756
|
-
if (!pdfjsPromise) try {
|
|
757
|
-
pdfjsPromise = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
758
|
-
} catch (error) {
|
|
759
|
-
pdfjsPromise = void 0;
|
|
760
|
-
throw new Error([
|
|
761
|
-
"Failed to load pdfjs-dist.",
|
|
762
|
-
"If you use anchor-based signature placement in Node.js, make sure @napi-rs/canvas is installed.",
|
|
763
|
-
"Try: npm install @napi-rs/canvas",
|
|
764
|
-
"",
|
|
765
|
-
`Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
766
|
-
].join("\n"), { cause: error });
|
|
767
|
-
}
|
|
768
|
-
return pdfjsPromise;
|
|
769
|
-
}
|
|
770
|
-
function toU8(input) {
|
|
771
|
-
return new Uint8Array(input);
|
|
772
|
-
}
|
|
773
|
-
async function findAnchorPosition(pdfBytes, anchorText) {
|
|
774
|
-
const pdfjsLib = await loadPdfjs();
|
|
775
|
-
const loadingTask = pdfjsLib.getDocument({ data: pdfBytes });
|
|
776
|
-
try {
|
|
777
|
-
const pdf = await loadingTask.promise;
|
|
778
|
-
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
779
|
-
const page = await pdf.getPage(pageNum);
|
|
780
|
-
const viewport = page.getViewport({ scale: 1 });
|
|
781
|
-
const textContent = await page.getTextContent();
|
|
782
|
-
for (const itemRaw of textContent.items) {
|
|
783
|
-
const item = itemRaw;
|
|
784
|
-
if (!item.str?.includes(anchorText)) continue;
|
|
785
|
-
const [_a, _b, _c, _d, e, f] = pdfjsLib.Util.transform(viewport.transform, item.transform);
|
|
786
|
-
const x = e;
|
|
787
|
-
const y = viewport.height - f;
|
|
788
|
-
return {
|
|
789
|
-
pageIndex: pageNum - 1,
|
|
790
|
-
x,
|
|
791
|
-
y,
|
|
792
|
-
pageWidth: viewport.width,
|
|
793
|
-
pageHeight: viewport.height
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
return null;
|
|
798
|
-
} finally {
|
|
799
|
-
await loadingTask.destroy();
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
function addSignatureFieldAt(pdfDoc, pageIndex, opts) {
|
|
803
|
-
const { fieldName, x, y, width, height } = opts;
|
|
804
|
-
const context = pdfDoc.context;
|
|
805
|
-
const page = pdfDoc.getPages()[pageIndex];
|
|
806
|
-
if (!page) throw new Error(`Page ${pageIndex} not found`);
|
|
807
|
-
const rectArray = context.obj([
|
|
808
|
-
pdf_lib.PDFNumber.of(x),
|
|
809
|
-
pdf_lib.PDFNumber.of(y),
|
|
810
|
-
pdf_lib.PDFNumber.of(x + width),
|
|
811
|
-
pdf_lib.PDFNumber.of(y + height)
|
|
812
|
-
]);
|
|
813
|
-
const widgetDict = context.obj({
|
|
814
|
-
Type: pdf_lib.PDFName.of("Annot"),
|
|
815
|
-
Subtype: pdf_lib.PDFName.of("Widget"),
|
|
816
|
-
Rect: rectArray,
|
|
817
|
-
F: pdf_lib.PDFNumber.of(4),
|
|
818
|
-
P: page.ref
|
|
819
|
-
});
|
|
820
|
-
const widgetRef = context.register(widgetDict);
|
|
821
|
-
const fieldDict = context.obj({
|
|
822
|
-
FT: pdf_lib.PDFName.of("Sig"),
|
|
823
|
-
T: pdf_lib.PDFString.of(fieldName),
|
|
824
|
-
Ff: pdf_lib.PDFNumber.of(0),
|
|
825
|
-
Kids: context.obj([widgetRef])
|
|
826
|
-
});
|
|
827
|
-
const fieldRef = context.register(fieldDict);
|
|
828
|
-
widgetDict.set(pdf_lib.PDFName.of("Parent"), fieldRef);
|
|
829
|
-
let annots = page.node.lookupMaybe(pdf_lib.PDFName.of("Annots"), pdf_lib.PDFArray);
|
|
830
|
-
if (!annots) {
|
|
831
|
-
annots = context.obj([]);
|
|
832
|
-
page.node.set(pdf_lib.PDFName.of("Annots"), annots);
|
|
833
|
-
}
|
|
834
|
-
annots.push(widgetRef);
|
|
835
|
-
let acroForm = pdfDoc.catalog.lookupMaybe(pdf_lib.PDFName.of("AcroForm"), pdf_lib.PDFDict);
|
|
836
|
-
if (!acroForm) {
|
|
837
|
-
acroForm = context.obj({
|
|
838
|
-
Fields: context.obj([]),
|
|
839
|
-
SigFlags: pdf_lib.PDFNumber.of(3)
|
|
840
|
-
});
|
|
841
|
-
const acroFormRef = context.register(acroForm);
|
|
842
|
-
pdfDoc.catalog.set(pdf_lib.PDFName.of("AcroForm"), acroFormRef);
|
|
843
|
-
}
|
|
844
|
-
let fields = acroForm.lookupMaybe(pdf_lib.PDFName.of("Fields"), pdf_lib.PDFArray);
|
|
845
|
-
if (!fields) {
|
|
846
|
-
fields = context.obj([]);
|
|
847
|
-
acroForm.set(pdf_lib.PDFName.of("Fields"), fields);
|
|
848
|
-
}
|
|
849
|
-
fields.push(fieldRef);
|
|
850
|
-
return {
|
|
851
|
-
fieldRef,
|
|
852
|
-
widgetRef
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
async function addSignatureFieldUsingAnchor(pdf, fieldName = "SignatureDebtor", anchorText = "__SIG_DEBTOR_ANCHOR__") {
|
|
856
|
-
const bytes = toU8(pdf);
|
|
857
|
-
const anchor = await findAnchorPosition(bytes.slice(), anchorText);
|
|
858
|
-
const pdfDoc = await pdf_lib.PDFDocument.load(bytes);
|
|
859
|
-
const sigWidth = 240;
|
|
860
|
-
const sigHeight = 50;
|
|
861
|
-
if (anchor) {
|
|
862
|
-
const pageIndex = anchor.pageIndex;
|
|
863
|
-
const { width: pw, height: ph } = pdfDoc.getPages()[pageIndex].getSize();
|
|
864
|
-
const padX = 40;
|
|
865
|
-
const padY = 7;
|
|
866
|
-
let x = anchor.x - sigWidth / 2 + padX;
|
|
867
|
-
let y = anchor.y - sigHeight + padY;
|
|
868
|
-
const margin = 10;
|
|
869
|
-
x = Math.max(margin, Math.min(x, pw - sigWidth - margin));
|
|
870
|
-
y = Math.max(margin, Math.min(y, ph - sigHeight - margin));
|
|
871
|
-
addSignatureFieldAt(pdfDoc, anchor.pageIndex, {
|
|
872
|
-
fieldName,
|
|
873
|
-
x,
|
|
874
|
-
y,
|
|
875
|
-
width: sigWidth,
|
|
876
|
-
height: sigHeight
|
|
877
|
-
});
|
|
878
|
-
} else {
|
|
879
|
-
const pages = pdfDoc.getPages();
|
|
880
|
-
const pageIndex = pages.length - 1;
|
|
881
|
-
const { width: pageWidth } = pages[pageIndex].getSize();
|
|
882
|
-
addSignatureFieldAt(pdfDoc, pageIndex, {
|
|
883
|
-
fieldName,
|
|
884
|
-
x: (pageWidth - sigWidth) / 2,
|
|
885
|
-
y: 90,
|
|
886
|
-
width: sigWidth,
|
|
887
|
-
height: sigHeight
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
return await pdfDoc.save();
|
|
891
|
-
}
|
|
892
|
-
//#endregion
|
|
893
909
|
//#region src/nestjs-pdf.service.ts
|
|
894
910
|
let NestjsPdfService = class NestjsPdfService {
|
|
895
911
|
puppeteerService;
|
|
@@ -3762,21 +3778,6 @@ let NestjsPdfModule = _NestjsPdfModule = class NestjsPdfModule {
|
|
|
3762
3778
|
};
|
|
3763
3779
|
NestjsPdfModule = _NestjsPdfModule = __decorate([(0, _nestjs_common.Module)({})], NestjsPdfModule);
|
|
3764
3780
|
//#endregion
|
|
3765
|
-
//#region src/helpers/getRelativePath.helper.ts
|
|
3766
|
-
/**
|
|
3767
|
-
* Get the relative path to a value from the module directory.
|
|
3768
|
-
* @param moduleDir the path of the module, typically __dirname
|
|
3769
|
-
* @param relativeToModule the path to the value relative to the module directory
|
|
3770
|
-
* @returns the relative path from the current working directory to the value
|
|
3771
|
-
* @example
|
|
3772
|
-
* getRelativePathToValue(__dirname, './templates/my-template.hbs');
|
|
3773
|
-
* // returns 'context/pdf/templates/my-template.hbs' if the current working directory is '/
|
|
3774
|
-
*/
|
|
3775
|
-
function getRelativePathToValue(moduleDir, relativeToModule) {
|
|
3776
|
-
const fullTemplatePath = (0, node_path.resolve)(moduleDir, relativeToModule);
|
|
3777
|
-
return (0, node_path.relative)(process.cwd(), fullTemplatePath);
|
|
3778
|
-
}
|
|
3779
|
-
//#endregion
|
|
3780
3781
|
Object.defineProperty(exports, "EjsService", {
|
|
3781
3782
|
enumerable: true,
|
|
3782
3783
|
get: function() {
|