@speresps/pdf-module 0.0.0-stage → 1.0.1
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 +1171 -3
- package/api/client/clientApi.js +45 -0
- package/api/server/serverApi.js +26 -0
- package/configManager/config.js +29 -0
- package/logManager/logger.js +11 -0
- package/package.json +42 -3
- package/service/generatePdfService.js +251 -0
- package/service/pdfBufferService.js +39 -0
- package/service/pdfFileService.js +187 -0
- package/service/validation/validateData.js +73 -0
- package/src/client/downloadPDF.js +52 -0
- package/src/client/index.js +13 -0
- package/src/index.js +1 -0
- package/src/server/index.js +10 -0
- package/template/generatePDFTable/generatePDFTable.js +115 -0
- package/template/generatePDFText/generatePDFText.js +42 -0
- package/template/generatePDFTextAndTable/generatePDFTextAndTable.js +126 -0
- package/template/generatePDFTextAndTableWithPagination/generatePDFTextAndTableWithPagination.js +146 -0
- package/template/utils/fontEncoder.cjs +45 -0
- package/template/utils/footer.js +34 -0
- package/template/utils/header.js +24 -0
- package/template/utils/vfs_fonts.js +17 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
function validateObject(data) {
|
|
2
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
3
|
+
throw new Error("PDF data must be an object.");
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function validateColumns(columns) {
|
|
8
|
+
if (
|
|
9
|
+
!Array.isArray(columns) ||
|
|
10
|
+
columns.length === 0 ||
|
|
11
|
+
!columns.every(col => typeof col === "string" && col.trim())
|
|
12
|
+
) {
|
|
13
|
+
throw new Error("Columns must be a non-empty array of strings.");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateRows(rows) {
|
|
18
|
+
if (!Array.isArray(rows)) {
|
|
19
|
+
throw new Error("Table data must be an array.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!rows.every(row =>
|
|
23
|
+
row !== null &&
|
|
24
|
+
typeof row === "object" &&
|
|
25
|
+
!Array.isArray(row)
|
|
26
|
+
)) {
|
|
27
|
+
throw new Error("Every table row must be an object.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateTableData(data) {
|
|
32
|
+
validateObject(data);
|
|
33
|
+
validateColumns(data.columns);
|
|
34
|
+
validateRows(data.tableData);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function validatePDFTextData(data) {
|
|
38
|
+
validateObject(data);
|
|
39
|
+
|
|
40
|
+
if (typeof data.text !== "string" || !data.text.trim()) {
|
|
41
|
+
throw new Error("PDF text must be a non-empty string.");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function validatePDFTextAndTableData(data) {
|
|
46
|
+
validatePDFTextData(data);
|
|
47
|
+
validateColumns(data.columns);
|
|
48
|
+
validateRows(data.tableData);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validatePDFPaginationData(data) {
|
|
52
|
+
validatePDFTextData(data);
|
|
53
|
+
validateColumns(data.columns);
|
|
54
|
+
|
|
55
|
+
if (!Array.isArray(data.paginatedData) || data.paginatedData.length === 0) {
|
|
56
|
+
throw new Error("Paginated data must be a non-empty array.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const page of data.paginatedData) {
|
|
60
|
+
if (!page || typeof page !== "object") {
|
|
61
|
+
throw new Error("Every pagination item must be an object.");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
validateRows(page.data);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
validateTableData,
|
|
70
|
+
validatePDFTextData,
|
|
71
|
+
validatePDFTextAndTableData,
|
|
72
|
+
validatePDFPaginationData
|
|
73
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { getPDFFile } from "../../api/client/clientApi.js";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export async function downloadPDF(fileName) {
|
|
7
|
+
|
|
8
|
+
if (!fileName) {
|
|
9
|
+
throw new Error("PDF file name is required.");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const result = await getPDFFile(fileName);
|
|
13
|
+
|
|
14
|
+
if (!result || !result.success) {
|
|
15
|
+
throw new Error("Unable to download PDF file.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const binary = atob(result.data);
|
|
19
|
+
|
|
20
|
+
const bytes = Uint8Array.from(
|
|
21
|
+
binary,
|
|
22
|
+
char => char.charCodeAt(0)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const blob = new Blob(
|
|
26
|
+
[bytes],
|
|
27
|
+
{ type: result.mimeType }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const url = URL.createObjectURL(blob);
|
|
31
|
+
|
|
32
|
+
const link = document.createElement("a");
|
|
33
|
+
|
|
34
|
+
link.href = url;
|
|
35
|
+
link.download = result.fileName;
|
|
36
|
+
|
|
37
|
+
document.body.appendChild(link);
|
|
38
|
+
|
|
39
|
+
link.click();
|
|
40
|
+
|
|
41
|
+
link.remove();
|
|
42
|
+
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
URL.revokeObjectURL(url);
|
|
45
|
+
}, 1000);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
fileName: result.fileName
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./server/index.js";
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import pdfMake from "pdfmake-rtl/build/pdfmake.js";
|
|
2
|
+
import pdfFonts from "../utils/vfs_fonts.js";
|
|
3
|
+
import { getHeader } from "../utils/header.js";
|
|
4
|
+
import { getFooter } from "../utils/footer.js";
|
|
5
|
+
|
|
6
|
+
pdfMake.addVirtualFileSystem(pdfFonts.vfs);
|
|
7
|
+
pdfMake.fonts = pdfFonts.fonts;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export function generatePDFTable({
|
|
11
|
+
tableData, // مقدار جدول
|
|
12
|
+
columns, // مقدار ستونها
|
|
13
|
+
headerTitle, // عنوان هدر
|
|
14
|
+
headerLogoBase64, // لوگو
|
|
15
|
+
footerInfo, // اطلاعات فوتر
|
|
16
|
+
fileName = "table.pdf", // اسم فایل
|
|
17
|
+
autoDownload = false, // وضعیت دانلود
|
|
18
|
+
}) {
|
|
19
|
+
const pageMargins = [40, 70, 40, 60]; // [left, top-bottom, top-right-left, bottom]
|
|
20
|
+
const pageWidthPortrait = 595.28;
|
|
21
|
+
const pageWidthLandscape = 841.89;
|
|
22
|
+
|
|
23
|
+
// محاسبه بیشترین طول محتوا در هر ستون
|
|
24
|
+
const maxContentWidths = columns.map(col => {
|
|
25
|
+
const maxLength = Math.max(
|
|
26
|
+
col.length,
|
|
27
|
+
...tableData.map(row => (row[col]?.toString() ?? "").length)
|
|
28
|
+
);
|
|
29
|
+
return maxLength;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const charWidth = 7;
|
|
33
|
+
let totalTableWidth = maxContentWidths.reduce((sum, len) => sum + len * charWidth, 0);
|
|
34
|
+
totalTableWidth += columns.length * 10; // padding
|
|
35
|
+
|
|
36
|
+
const usableWidthPortrait = pageWidthPortrait - pageMargins[0] - pageMargins[2]*2+8;
|
|
37
|
+
const usableWidthLandscape = pageWidthLandscape - pageMargins[0] - pageMargins[2]*2-8;
|
|
38
|
+
|
|
39
|
+
const pageOrientation = totalTableWidth > usableWidthPortrait ? "landscape" : "portrait";
|
|
40
|
+
const usableWidth = pageOrientation === "portrait" ? usableWidthPortrait : usableWidthLandscape;
|
|
41
|
+
|
|
42
|
+
// تعیین عرض هر ستون
|
|
43
|
+
let widths = maxContentWidths.map(w => w * charWidth + 10);
|
|
44
|
+
const totalWidths = widths.reduce((a, b) => a + b, 0);
|
|
45
|
+
|
|
46
|
+
// پخش فضای خالی بین ستونها اگر جا داریم
|
|
47
|
+
if (totalWidths < usableWidth) {
|
|
48
|
+
const extraSpace = (usableWidth - totalWidths) / columns.length;
|
|
49
|
+
widths = widths.map(w => w + extraSpace);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tableBody = [
|
|
53
|
+
columns.map(col => ({
|
|
54
|
+
text: col,
|
|
55
|
+
style: "tableHeader",
|
|
56
|
+
alignment: "center",
|
|
57
|
+
font: "Vazirmatn",
|
|
58
|
+
})),
|
|
59
|
+
...tableData.map(row =>
|
|
60
|
+
columns.map(col => ({
|
|
61
|
+
text: row[col]?.toString() ?? "",
|
|
62
|
+
alignment: "center",
|
|
63
|
+
font: "Vazirmatn",
|
|
64
|
+
}))
|
|
65
|
+
)
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const docDefinition = {
|
|
69
|
+
pageOrientation,
|
|
70
|
+
content: [
|
|
71
|
+
// { text: "", margin: [0, 7] },
|
|
72
|
+
{
|
|
73
|
+
table: {
|
|
74
|
+
headerRows: 1,
|
|
75
|
+
widths,
|
|
76
|
+
body: tableBody,
|
|
77
|
+
},
|
|
78
|
+
layout: {
|
|
79
|
+
fillColor: (rowIndex) => rowIndex === 0 ? '#eeeeee' : null,
|
|
80
|
+
hLineWidth: () => 0.5,
|
|
81
|
+
vLineWidth: () => 0.5,
|
|
82
|
+
hLineColor: () => '#aaa',
|
|
83
|
+
vLineColor: () => '#aaa',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
header: getHeader(headerTitle, headerLogoBase64, pageOrientation),
|
|
88
|
+
footer(currentPage, pageCount) {
|
|
89
|
+
return getFooter(currentPage, pageCount, footerInfo, pageOrientation);
|
|
90
|
+
},
|
|
91
|
+
defaultStyle: {
|
|
92
|
+
font: "Vazirmatn",
|
|
93
|
+
fontSize: 12,
|
|
94
|
+
alignment: "right",
|
|
95
|
+
},
|
|
96
|
+
styles: {
|
|
97
|
+
tableHeader: {
|
|
98
|
+
bold: true,
|
|
99
|
+
fontSize: 13,
|
|
100
|
+
color: 'black',
|
|
101
|
+
font: "Vazirmatn",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
pageMargins,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const pdf = pdfMake.createPdf(docDefinition);
|
|
108
|
+
|
|
109
|
+
// برای دانلود مستقیم همان لحظه
|
|
110
|
+
if (autoDownload) {
|
|
111
|
+
pdf.download(fileName);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return pdf;
|
|
115
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pdfMake from "pdfmake-rtl/build/pdfmake.js";
|
|
2
|
+
import pdfFonts from "../utils/vfs_fonts.js";
|
|
3
|
+
import { getHeader } from "../utils/header.js";
|
|
4
|
+
import { getFooter } from "../utils/footer.js";
|
|
5
|
+
|
|
6
|
+
pdfMake.addVirtualFileSystem(pdfFonts.vfs);
|
|
7
|
+
pdfMake.fonts = pdfFonts.fonts;
|
|
8
|
+
|
|
9
|
+
export function generatePDFText({
|
|
10
|
+
text,
|
|
11
|
+
headerTitle,
|
|
12
|
+
headerLogoBase64,
|
|
13
|
+
footerInfo,
|
|
14
|
+
fileName='text.pdf',
|
|
15
|
+
autoDownload= false }) {
|
|
16
|
+
const docDefinition = {
|
|
17
|
+
content: [
|
|
18
|
+
{ text: "", margin: [0, 7] },
|
|
19
|
+
{ text: text, font: "Vazirmatn", direction: "rtl", alignment: "right" }
|
|
20
|
+
],
|
|
21
|
+
header: getHeader(headerTitle, headerLogoBase64),
|
|
22
|
+
footer(currentPage, pageCount) {
|
|
23
|
+
return getFooter(currentPage, pageCount, footerInfo);
|
|
24
|
+
},
|
|
25
|
+
defaultStyle: {
|
|
26
|
+
font: "Vazirmatn",
|
|
27
|
+
fontSize: 16,
|
|
28
|
+
direction: "rtl",
|
|
29
|
+
alignment: "right",
|
|
30
|
+
},
|
|
31
|
+
pageMargins: [40, 70, 40, 60],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const pdfDoc = pdfMake.createPdf(docDefinition);
|
|
35
|
+
|
|
36
|
+
// برای دانلود مستقیم همان لحظه
|
|
37
|
+
if (autoDownload) {
|
|
38
|
+
pdfDoc.download(fileName);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return pdfDoc;
|
|
42
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import pdfMake from "pdfmake-rtl/build/pdfmake.js";
|
|
2
|
+
import pdfFonts from "../utils/vfs_fonts.js";
|
|
3
|
+
import { getHeader } from "../utils/header.js";
|
|
4
|
+
import { getFooter } from "../utils/footer.js";
|
|
5
|
+
|
|
6
|
+
pdfMake.addVirtualFileSystem(pdfFonts.vfs);
|
|
7
|
+
pdfMake.fonts = pdfFonts.fonts;
|
|
8
|
+
|
|
9
|
+
export function generatePDFTextAndTable({
|
|
10
|
+
text, // متن
|
|
11
|
+
columns, //مقدار ستونها
|
|
12
|
+
tableData, // ارایه ردیفهای جدول
|
|
13
|
+
headerTitle, // عنوان هدر
|
|
14
|
+
headerLogoBase64, // لوگو
|
|
15
|
+
footerInfo, // اطلاعات فوتر
|
|
16
|
+
fileName = "table.pdf", // اسم فایل
|
|
17
|
+
autoDownload = false, // وضعیت دانلود
|
|
18
|
+
}) {
|
|
19
|
+
const pageMargins = [40, 70, 40, 60]; // left, top-bottom, top-right-left, bottom
|
|
20
|
+
const pageWidthPortrait = 595.28;
|
|
21
|
+
const pageWidthLandscape = 841.89;
|
|
22
|
+
|
|
23
|
+
// محاسبه بیشترین طول برای هر ستون
|
|
24
|
+
const maxContentWidths = columns.map(col => {
|
|
25
|
+
const maxLength = Math.max(
|
|
26
|
+
col.length,
|
|
27
|
+
...tableData.map(row => (row[col]?.toString() ?? "").length)
|
|
28
|
+
);
|
|
29
|
+
return maxLength;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const charWidth = 7;
|
|
33
|
+
let totalTableWidth = maxContentWidths.reduce((sum, len) => sum + len * charWidth, 0);
|
|
34
|
+
totalTableWidth += columns.length * 10; // padding
|
|
35
|
+
|
|
36
|
+
const usableWidthPortrait = pageWidthPortrait - pageMargins[0] - pageMargins[2]*2+8;
|
|
37
|
+
const usableWidthLandscape = pageWidthLandscape - pageMargins[0] - pageMargins[2]*2-8;
|
|
38
|
+
|
|
39
|
+
const pageOrientation = totalTableWidth > usableWidthPortrait ? "landscape" : "portrait";
|
|
40
|
+
const usableWidth = pageOrientation === "portrait" ? usableWidthPortrait : usableWidthLandscape;
|
|
41
|
+
|
|
42
|
+
// تعیین عرض هر ستون
|
|
43
|
+
let widths = maxContentWidths.map(w => w * charWidth + 10);
|
|
44
|
+
const totalWidths = widths.reduce((a, b) => a + b, 0);
|
|
45
|
+
|
|
46
|
+
// اگر فضای خالی وجود دارد، بین ستونها پخش کن
|
|
47
|
+
if (totalWidths < usableWidth) {
|
|
48
|
+
const extraSpace = (usableWidth - totalWidths) / columns.length;
|
|
49
|
+
widths = widths.map(w => w + extraSpace);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tableBody = [
|
|
53
|
+
columns.map(col => ({
|
|
54
|
+
text: col,
|
|
55
|
+
style: "tableHeader",
|
|
56
|
+
alignment: "center",
|
|
57
|
+
font: "Vazirmatn",
|
|
58
|
+
})),
|
|
59
|
+
...tableData.map(row =>
|
|
60
|
+
columns.map(col => ({
|
|
61
|
+
text: row[col]?.toString() ?? "",
|
|
62
|
+
alignment: "center",
|
|
63
|
+
font: "Vazirmatn",
|
|
64
|
+
}))
|
|
65
|
+
),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const content = [
|
|
69
|
+
// { text: "", margin: [0, 7] },
|
|
70
|
+
{
|
|
71
|
+
text,
|
|
72
|
+
font: "Vazirmatn",
|
|
73
|
+
fontSize: 16,
|
|
74
|
+
direction: "rtl",
|
|
75
|
+
alignment: "right",
|
|
76
|
+
margin: [0, 0, 0, 20],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
table: {
|
|
80
|
+
headerRows: 1,
|
|
81
|
+
widths,
|
|
82
|
+
body: tableBody,
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
layout: {
|
|
86
|
+
fillColor: (rowIndex) => (rowIndex === 0 ? "#eeeeee" : null),
|
|
87
|
+
hLineWidth: () => 0.5,
|
|
88
|
+
vLineWidth: () => 0.5,
|
|
89
|
+
hLineColor: () => "#aaa",
|
|
90
|
+
vLineColor: () => "#aaa",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const docDefinition = {
|
|
96
|
+
pageOrientation,
|
|
97
|
+
content,
|
|
98
|
+
header: getHeader(headerTitle, headerLogoBase64, pageOrientation),
|
|
99
|
+
footer(currentPage, pageCount) {
|
|
100
|
+
return getFooter(currentPage, pageCount, footerInfo, pageOrientation);
|
|
101
|
+
},
|
|
102
|
+
defaultStyle: {
|
|
103
|
+
font: "Vazirmatn",
|
|
104
|
+
fontSize: 12,
|
|
105
|
+
alignment: "right",
|
|
106
|
+
},
|
|
107
|
+
styles: {
|
|
108
|
+
tableHeader: {
|
|
109
|
+
bold: true,
|
|
110
|
+
fontSize: 13,
|
|
111
|
+
color: "black",
|
|
112
|
+
font: "Vazirmatn",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
pageMargins,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const pdf = pdfMake.createPdf(docDefinition);
|
|
119
|
+
|
|
120
|
+
// برای دانلود مستقیم همان لحظه
|
|
121
|
+
if (autoDownload) {
|
|
122
|
+
pdf.download(fileName);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return pdf;
|
|
126
|
+
}
|
package/template/generatePDFTextAndTableWithPagination/generatePDFTextAndTableWithPagination.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import pdfMake from "pdfmake-rtl/build/pdfmake.js";
|
|
2
|
+
import pdfFonts from "../utils/vfs_fonts.js";
|
|
3
|
+
import { getHeader } from "../utils/header.js";
|
|
4
|
+
import { getFooter } from "../utils/footer.js";
|
|
5
|
+
|
|
6
|
+
pdfMake.addVirtualFileSystem(pdfFonts.vfs);
|
|
7
|
+
pdfMake.fonts = pdfFonts.fonts;
|
|
8
|
+
|
|
9
|
+
// این متد روی داده هایی که ساختار صفحه بندی شده دارند استفاده میشود
|
|
10
|
+
//const result=[
|
|
11
|
+
// {
|
|
12
|
+
// "page": 1,
|
|
13
|
+
// "per_page": 10,
|
|
14
|
+
// "total": 28,
|
|
15
|
+
// "total_pages": 3,
|
|
16
|
+
// "data": [
|
|
17
|
+
// {
|
|
18
|
+
// "id": 1,
|
|
19
|
+
// "email": "michael.lawson@reqres.in",
|
|
20
|
+
// "first_name": "Michael",
|
|
21
|
+
// "last_name": "Lawson",
|
|
22
|
+
// "avatar": "https://reqres.in/img/faces/7-image.jpg",
|
|
23
|
+
// "addr" : "my home"
|
|
24
|
+
// },
|
|
25
|
+
// ...
|
|
26
|
+
// ]}
|
|
27
|
+
export function generatePDFTextAndTableWithPagination({
|
|
28
|
+
text, // متن بالای جدول
|
|
29
|
+
columns, // مقدار ستونها
|
|
30
|
+
paginatedData, // داده های صفحه بندی شده
|
|
31
|
+
headerTitle, // عنوان هدر
|
|
32
|
+
headerLogoBase64, // لوگوی هدر
|
|
33
|
+
footerInfo, // اطلاعات فوتر
|
|
34
|
+
fileName='text-table-pagination.pdf', //نام فایل
|
|
35
|
+
autoDownload= false , // وضعیت دانلود
|
|
36
|
+
}) {
|
|
37
|
+
const pageMargins = [40, 70, 40, 60]; // left, top-bottom, top-right-left, bottom
|
|
38
|
+
const pageWidthPortrait = 595.28;
|
|
39
|
+
const pageWidthLandscape = 841.89;
|
|
40
|
+
|
|
41
|
+
const allData = paginatedData.flatMap(p => p.data);
|
|
42
|
+
|
|
43
|
+
const maxContentWidths = columns.map(col => {
|
|
44
|
+
const maxLength = Math.max(
|
|
45
|
+
col.length,
|
|
46
|
+
...allData.map(row => (row[col]?.toString() ?? "").length)
|
|
47
|
+
);
|
|
48
|
+
return maxLength;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const charWidth = 7;
|
|
52
|
+
let totalTableWidth = maxContentWidths.reduce((sum, len) => sum + len * charWidth, 0);
|
|
53
|
+
totalTableWidth += columns.length * 10;
|
|
54
|
+
|
|
55
|
+
const usableWidthPortrait = pageWidthPortrait - pageMargins[0] - pageMargins[2]*2+5;
|
|
56
|
+
const usableWidthLandscape = pageWidthLandscape - pageMargins[0] - pageMargins[2]*2-3;
|
|
57
|
+
|
|
58
|
+
const pageOrientation = totalTableWidth > usableWidthPortrait ? "landscape" : "portrait";
|
|
59
|
+
const usableWidth = pageOrientation === "portrait" ? usableWidthPortrait : usableWidthLandscape;
|
|
60
|
+
|
|
61
|
+
let widths = maxContentWidths.map(w => w * charWidth + 10);
|
|
62
|
+
const totalWidths = widths.reduce((a, b) => a + b, 0);
|
|
63
|
+
|
|
64
|
+
if (totalWidths < usableWidth) {
|
|
65
|
+
const extraSpace = (usableWidth - totalWidths) / columns.length;
|
|
66
|
+
widths = widths.map(w => w + extraSpace);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const content = [];
|
|
70
|
+
|
|
71
|
+
paginatedData.forEach((pageData, index) => {
|
|
72
|
+
const tableBody = [
|
|
73
|
+
columns.map(col => ({
|
|
74
|
+
text: col,
|
|
75
|
+
style: "tableHeader",
|
|
76
|
+
alignment: "center",
|
|
77
|
+
font: "Vazirmatn",
|
|
78
|
+
})),
|
|
79
|
+
...pageData.data.map(row =>
|
|
80
|
+
columns.map(col => ({
|
|
81
|
+
text: row[col]?.toString() ?? "",
|
|
82
|
+
alignment: "center",
|
|
83
|
+
font: "Vazirmatn",
|
|
84
|
+
}))
|
|
85
|
+
)
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
if (index > 0) content.push({ text: "", pageBreak: "before" });
|
|
89
|
+
|
|
90
|
+
content.push({
|
|
91
|
+
text: text,
|
|
92
|
+
font: "Vazirmatn",
|
|
93
|
+
fontSize: 16,
|
|
94
|
+
direction: "rtl",
|
|
95
|
+
alignment: "right",
|
|
96
|
+
margin: [0, 0, 0, 20],
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
content.push({
|
|
100
|
+
table: {
|
|
101
|
+
headerRows: 1,
|
|
102
|
+
widths,
|
|
103
|
+
body: tableBody,
|
|
104
|
+
},
|
|
105
|
+
layout: {
|
|
106
|
+
fillColor: (rowIndex) => rowIndex === 0 ? '#eeeeee' : null,
|
|
107
|
+
hLineWidth: () => 0.5,
|
|
108
|
+
vLineWidth: () => 0.5,
|
|
109
|
+
hLineColor: () => '#aaa',
|
|
110
|
+
vLineColor: () => '#aaa',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const docDefinition = {
|
|
116
|
+
pageOrientation,
|
|
117
|
+
content,
|
|
118
|
+
header: getHeader(headerTitle, headerLogoBase64, pageOrientation),
|
|
119
|
+
footer(currentPage, pageCount) {
|
|
120
|
+
return getFooter(currentPage, pageCount, footerInfo, pageOrientation);
|
|
121
|
+
},
|
|
122
|
+
defaultStyle: {
|
|
123
|
+
font: "Vazirmatn",
|
|
124
|
+
fontSize: 12,
|
|
125
|
+
alignment: "right",
|
|
126
|
+
},
|
|
127
|
+
styles: {
|
|
128
|
+
tableHeader: {
|
|
129
|
+
bold: true,
|
|
130
|
+
fontSize: 13,
|
|
131
|
+
color: 'black',
|
|
132
|
+
font: "Vazirmatn",
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
pageMargins,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const pdf = pdfMake.createPdf(docDefinition);
|
|
139
|
+
|
|
140
|
+
// برای دانلود مستقیم همان لحظه
|
|
141
|
+
if (autoDownload) {
|
|
142
|
+
pdf.download(fileName);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return pdf;
|
|
146
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const FONT_DIR = path.resolve(__dirname, "../../public/fonts/vazir/ttf");
|
|
5
|
+
|
|
6
|
+
const fontFiles = [
|
|
7
|
+
"Vazirmatn-Regular.ttf",
|
|
8
|
+
"Vazirmatn-Bold.ttf",
|
|
9
|
+
"Vazirmatn-Light.ttf",
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const vfs = {};
|
|
13
|
+
|
|
14
|
+
for (const file of fontFiles) {
|
|
15
|
+
const filePath = path.join(FONT_DIR, file);
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(filePath)) {
|
|
18
|
+
throw new Error(`Font file not found: ${filePath}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
vfs[file] = fs.readFileSync(filePath).toString("base64");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const fonts = {
|
|
25
|
+
Vazirmatn: {
|
|
26
|
+
normal: "Vazirmatn-Regular.ttf",
|
|
27
|
+
bold: "Vazirmatn-Bold.ttf",
|
|
28
|
+
italics: "Vazirmatn-Light.ttf",
|
|
29
|
+
bolditalics: "Vazirmatn-Bold.ttf",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const output = `const pdfFonts = {
|
|
34
|
+
vfs: ${JSON.stringify(vfs, null, 2)},
|
|
35
|
+
fonts: ${JSON.stringify(fonts, null, 2)},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default pdfFonts;
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
fs.writeFileSync(
|
|
42
|
+
path.resolve(__dirname, "./vfs_fonts.js"),
|
|
43
|
+
output,
|
|
44
|
+
"utf8"
|
|
45
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function getFooter(currentPage, pageCount, siteInfo = {}, pageOrientation) {
|
|
2
|
+
const {
|
|
3
|
+
siteName = "نام سایت شما",
|
|
4
|
+
address = "آدرس شما",
|
|
5
|
+
phone = "تلفن: ۰۱۲۳۴۵۶۷۸۹",
|
|
6
|
+
email = "ایمیل: info@example.com",
|
|
7
|
+
website = "www.example.com",
|
|
8
|
+
} = siteInfo;
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
columns: [
|
|
12
|
+
{
|
|
13
|
+
text: `${siteName} | ${address} | ${phone} | ${email} | ${website}`,
|
|
14
|
+
fontSize: 8,
|
|
15
|
+
font: "Vazirmatn",
|
|
16
|
+
alignment: "left",
|
|
17
|
+
textAlign: "left",
|
|
18
|
+
direction:"ltr",
|
|
19
|
+
margin: [30, 20, 0, 20], //margin: [left, top-bottom, top-right-left, bottom]
|
|
20
|
+
width: "56%",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
text: `صفحه ${currentPage} از ${pageCount}`,
|
|
24
|
+
fontSize: 10,
|
|
25
|
+
font: "Vazirmatn",
|
|
26
|
+
alignment: "right",
|
|
27
|
+
margin: [0, 20, 30, 20], //margin: [left, top-bottom, top-right-left, bottom]
|
|
28
|
+
width: "44%",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
margin: [0, 0, 0, 0],
|
|
32
|
+
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function getHeader(title = "عنوان پیش فرض هدر", logoBase64 = null, pageOrientation) {
|
|
2
|
+
return {
|
|
3
|
+
columns: [
|
|
4
|
+
logoBase64
|
|
5
|
+
? {
|
|
6
|
+
image: logoBase64,
|
|
7
|
+
width: 40,
|
|
8
|
+
height: 40,
|
|
9
|
+
margin: [-10, 0 , 0, 10], //margin: [left, top-bottom, top-right-left, bottom]
|
|
10
|
+
}
|
|
11
|
+
: {},
|
|
12
|
+
{
|
|
13
|
+
text: title,
|
|
14
|
+
fontSize: 16,
|
|
15
|
+
bold: true,
|
|
16
|
+
alignment: "right",
|
|
17
|
+
direction: "rtl",
|
|
18
|
+
margin: [0, 5 , 10, 0], //margin: [left, top-bottom, top-right-left, bottom]
|
|
19
|
+
font: "Vazirmatn",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
margin: [30, 20, 30, 20],
|
|
23
|
+
};
|
|
24
|
+
}
|