demat-kyc-pdf 1.0.0
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 +62 -0
- package/index.js +177 -0
- package/package.json +36 -0
- package/views/kyc_template.ejs +2299 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# demat-kyc-pdf
|
|
2
|
+
|
|
3
|
+
`demat-kyc-pdf` is a Node.js package for:
|
|
4
|
+
|
|
5
|
+
- generating KYC PDFs from the bundled EJS template
|
|
6
|
+
- converting remote PDF documents into inline images for the template
|
|
7
|
+
- extracting selected pages from an existing PDF
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install demat-kyc-pdf
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Use as a package
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const { createKycPdf, extractPdfPages } = require("demat-kyc-pdf");
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const pdf = await createKycPdf({
|
|
22
|
+
id: "12345",
|
|
23
|
+
applicant_name: "Aman Sharma",
|
|
24
|
+
aadhar: "https://example.com/aadhar.pdf",
|
|
25
|
+
pancard: "https://example.com/pan.pdf",
|
|
26
|
+
statement: "https://example.com/statement.pdf",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const extracted = await extractPdfPages({
|
|
30
|
+
pdfUrl: "https://example.com/source.pdf",
|
|
31
|
+
pages: [1, 3],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log(pdf.length, extracted.length);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
main().catch(console.error);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Use as an API server
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
PORT=3000 CHROME_PATH=/path/to/chrome npm start
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Available routes:
|
|
47
|
+
|
|
48
|
+
- `POST /api/generate-kyc`
|
|
49
|
+
- `POST /api/kra`
|
|
50
|
+
|
|
51
|
+
## Exported API
|
|
52
|
+
|
|
53
|
+
- `createKycPdf(data, options?)`
|
|
54
|
+
- `extractPdfPages({ pdfUrl, pages })`
|
|
55
|
+
- `createKycApp(options?)`
|
|
56
|
+
- `getPdfAsImages(pdfUrl, options?)`
|
|
57
|
+
- `renderKycHtml(data, options?)`
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
|
|
61
|
+
- The package expects a working Chromium or Chrome executable for Puppeteer.
|
|
62
|
+
- By default it looks at `process.env.CHROME_PATH`, then `/snap/chromium/current/usr/lib/chromium-browser/chrome`.
|
package/index.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const ejs = require("ejs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const puppeteer = require("puppeteer");
|
|
5
|
+
const { pdfToPng } = require("pdf-to-png-converter");
|
|
6
|
+
const { PDFDocument } = require("pdf-lib");
|
|
7
|
+
|
|
8
|
+
const DEFAULT_TEMPLATE_PATH = path.join(__dirname, "views", "kyc_template.ejs");
|
|
9
|
+
const DEFAULT_CHROME_PATH =
|
|
10
|
+
process.env.CHROME_PATH || "/snap/chromium/current/usr/lib/chromium-browser/chrome";
|
|
11
|
+
|
|
12
|
+
async function fetchPdfBuffer(pdfUrl, headers = {}) {
|
|
13
|
+
const response = await fetch(pdfUrl, {
|
|
14
|
+
headers: {
|
|
15
|
+
"User-Agent":
|
|
16
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122 Safari/537.36",
|
|
17
|
+
Accept: "application/pdf",
|
|
18
|
+
...headers,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`Failed to fetch PDF: ${response.status} ${response.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
27
|
+
return Buffer.from(arrayBuffer);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getPdfAsImages(pdfUrl, options = {}) {
|
|
31
|
+
try {
|
|
32
|
+
const pdfBuffer = await fetchPdfBuffer(pdfUrl, options.headers);
|
|
33
|
+
const pngPages = await pdfToPng(pdfBuffer, {
|
|
34
|
+
disableFontFace: true,
|
|
35
|
+
useSystemFonts: true,
|
|
36
|
+
viewportScale: 2.0,
|
|
37
|
+
pagesToProcess: options.pagesToProcess || [1],
|
|
38
|
+
...options.converter,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return pngPages.map((page) => `data:image/png;base64,${page.content.toString("base64")}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (options.throwOnError) {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function renderKycHtml(data, options = {}) {
|
|
52
|
+
return ejs.renderFile(options.templatePath || DEFAULT_TEMPLATE_PATH, { data }, { async: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function createBrowser(browserOptions = {}) {
|
|
56
|
+
return puppeteer.launch({
|
|
57
|
+
executablePath: browserOptions.executablePath || DEFAULT_CHROME_PATH,
|
|
58
|
+
headless: true,
|
|
59
|
+
pipe: true,
|
|
60
|
+
timeout: 45000,
|
|
61
|
+
args: [
|
|
62
|
+
"--no-sandbox",
|
|
63
|
+
"--disable-setuid-sandbox",
|
|
64
|
+
"--disable-dev-shm-usage",
|
|
65
|
+
"--disable-gpu",
|
|
66
|
+
"--remote-debugging-pipe",
|
|
67
|
+
...(browserOptions.args || []),
|
|
68
|
+
],
|
|
69
|
+
...browserOptions,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function createKycPdf(input = {}, options = {}) {
|
|
74
|
+
const merged = { ...input };
|
|
75
|
+
const pdfImageFields = options.pdfImageFields || ["aadhar", "pancard", "statement"];
|
|
76
|
+
|
|
77
|
+
for (const field of pdfImageFields) {
|
|
78
|
+
if (merged[field]) {
|
|
79
|
+
merged[field] = await getPdfAsImages(merged[field], options.imageOptions);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const html = await renderKycHtml(merged, options);
|
|
84
|
+
const browser = await createBrowser(options.browser);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const page = await browser.newPage();
|
|
88
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
89
|
+
await page.emulateMediaType("screen");
|
|
90
|
+
|
|
91
|
+
return await page.pdf({
|
|
92
|
+
format: "A4",
|
|
93
|
+
printBackground: true,
|
|
94
|
+
margin: { top: "1mm", bottom: "1mm", left: "4mm", right: "4mm" },
|
|
95
|
+
...options.pdf,
|
|
96
|
+
});
|
|
97
|
+
} finally {
|
|
98
|
+
await browser.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function extractPdfPages({ pdfUrl, pages }) {
|
|
103
|
+
if (!pdfUrl) {
|
|
104
|
+
throw new Error("pdfUrl is required");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!Array.isArray(pages) || pages.length === 0) {
|
|
108
|
+
throw new Error("pages must be an array like [1, 2, 3]");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const pdfBuffer = await fetchPdfBuffer(pdfUrl);
|
|
112
|
+
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
113
|
+
const pageIndexes = pages.map((page) => page - 1);
|
|
114
|
+
const newPdf = await PDFDocument.create();
|
|
115
|
+
const copiedPages = await newPdf.copyPages(pdfDoc, pageIndexes);
|
|
116
|
+
|
|
117
|
+
copiedPages.forEach((page) => newPdf.addPage(page));
|
|
118
|
+
|
|
119
|
+
return Buffer.from(await newPdf.save());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createKycApp(options = {}) {
|
|
123
|
+
const app = express();
|
|
124
|
+
|
|
125
|
+
app.use(express.json({ limit: options.jsonLimit || "30mb" }));
|
|
126
|
+
|
|
127
|
+
app.post("/api/generate-kyc", async (req, res) => {
|
|
128
|
+
try {
|
|
129
|
+
const pdfBuffer = await createKycPdf(req.body || {}, options);
|
|
130
|
+
const filename = `KYC_${req.body?.id || "document"}.pdf`;
|
|
131
|
+
|
|
132
|
+
res.set({
|
|
133
|
+
"Content-Type": "application/pdf",
|
|
134
|
+
"Content-Disposition": `attachment; filename="${filename}"`,
|
|
135
|
+
"Content-Length": pdfBuffer.length,
|
|
136
|
+
});
|
|
137
|
+
res.send(pdfBuffer);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
res.status(500).json({
|
|
140
|
+
error: "Failed to generate PDF",
|
|
141
|
+
details: error.message,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
app.post("/api/kra", async (req, res) => {
|
|
147
|
+
try {
|
|
148
|
+
const pdfBuffer = await extractPdfPages(req.body || {});
|
|
149
|
+
|
|
150
|
+
res.set({
|
|
151
|
+
"Content-Type": "application/pdf",
|
|
152
|
+
"Content-Disposition": 'attachment; filename="extracted_pages.pdf"',
|
|
153
|
+
"Content-Length": pdfBuffer.length,
|
|
154
|
+
});
|
|
155
|
+
res.send(pdfBuffer);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
const statusCode =
|
|
158
|
+
error.message === "pdfUrl is required" || error.message.startsWith("pages must be")
|
|
159
|
+
? 400
|
|
160
|
+
: 500;
|
|
161
|
+
|
|
162
|
+
res.status(statusCode).json({ error: error.message });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return app;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
DEFAULT_TEMPLATE_PATH,
|
|
171
|
+
createKycApp,
|
|
172
|
+
createKycPdf,
|
|
173
|
+
extractPdfPages,
|
|
174
|
+
fetchPdfBuffer,
|
|
175
|
+
getPdfAsImages,
|
|
176
|
+
renderKycHtml,
|
|
177
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "demat-kyc-pdf",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generate KYC PDFs and extract PDF pages from Node.js.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"views"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server.js",
|
|
12
|
+
"dev": "nodemon server.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"kyc",
|
|
16
|
+
"pdf",
|
|
17
|
+
"puppeteer",
|
|
18
|
+
"ejs",
|
|
19
|
+
"node"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@sparticuz/chromium": "^143.0.4",
|
|
25
|
+
"dotenv": "^17.2.3",
|
|
26
|
+
"ejs": "^3.1.10",
|
|
27
|
+
"express": "^5.1.0",
|
|
28
|
+
"pdf-lib": "^1.17.1",
|
|
29
|
+
"pdf-to-png-converter": "^3.11.0",
|
|
30
|
+
"puppeteer": "^24.27.0",
|
|
31
|
+
"puppeteer-core": "^24.39.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"nodemon": "^3.1.10"
|
|
35
|
+
}
|
|
36
|
+
}
|