@cj-tech-master/excelts 6.2.0 → 7.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 +45 -17
- package/README_zh.md +43 -15
- package/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.browser.js +1 -1
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +0 -2
- package/dist/browser/modules/excel/stream/workbook-writer.d.ts +2 -2
- package/dist/browser/modules/excel/types.d.ts +0 -2
- package/dist/browser/modules/pdf/excel-bridge.d.ts +29 -0
- package/dist/browser/modules/pdf/excel-bridge.js +423 -0
- package/dist/browser/modules/pdf/index.d.ts +22 -24
- package/dist/browser/modules/pdf/index.js +22 -25
- package/dist/browser/modules/pdf/pdf.d.ts +121 -0
- package/dist/browser/modules/pdf/pdf.js +255 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +10 -8
- package/dist/browser/modules/pdf/render/layout-engine.js +115 -209
- package/dist/browser/modules/pdf/render/pdf-exporter.d.ts +9 -62
- package/dist/browser/modules/pdf/render/pdf-exporter.js +38 -78
- package/dist/browser/modules/pdf/render/style-converter.d.ts +20 -18
- package/dist/browser/modules/pdf/render/style-converter.js +24 -23
- package/dist/browser/modules/pdf/types.d.ts +193 -11
- package/dist/browser/modules/pdf/types.js +22 -1
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/modules/pdf/excel-bridge.js +426 -0
- package/dist/cjs/modules/pdf/index.js +25 -28
- package/dist/cjs/modules/pdf/pdf.js +258 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +116 -210
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +37 -79
- package/dist/cjs/modules/pdf/render/style-converter.js +24 -23
- package/dist/cjs/modules/pdf/types.js +23 -2
- package/dist/esm/index.browser.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/modules/pdf/excel-bridge.js +423 -0
- package/dist/esm/modules/pdf/index.js +22 -25
- package/dist/esm/modules/pdf/pdf.js +255 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +115 -209
- package/dist/esm/modules/pdf/render/pdf-exporter.js +38 -78
- package/dist/esm/modules/pdf/render/style-converter.js +24 -23
- package/dist/esm/modules/pdf/types.js +22 -1
- package/dist/iife/excelts.iife.js +728 -251
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +34 -34
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +0 -2
- package/dist/types/modules/excel/stream/workbook-writer.d.ts +2 -2
- package/dist/types/modules/excel/types.d.ts +0 -2
- package/dist/types/modules/pdf/excel-bridge.d.ts +29 -0
- package/dist/types/modules/pdf/index.d.ts +22 -24
- package/dist/types/modules/pdf/pdf.d.ts +121 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +10 -8
- package/dist/types/modules/pdf/render/pdf-exporter.d.ts +9 -62
- package/dist/types/modules/pdf/render/style-converter.d.ts +20 -18
- package/dist/types/modules/pdf/types.d.ts +193 -11
- package/package.json +1 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified PDF generation API.
|
|
3
|
+
*
|
|
4
|
+
* Provides a concise way to create PDFs from plain data — no need to manually
|
|
5
|
+
* construct Map objects, compute bounds, or specify cell types.
|
|
6
|
+
*
|
|
7
|
+
* @example Simplest — pass a 2D array:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { pdf } from "@cj-tech-master/excelts/pdf";
|
|
10
|
+
*
|
|
11
|
+
* const bytes = pdf([
|
|
12
|
+
* ["Product", "Revenue"],
|
|
13
|
+
* ["Widget", 1000],
|
|
14
|
+
* ["Gadget", 2500]
|
|
15
|
+
* ]);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example With options:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const bytes = pdf([
|
|
21
|
+
* ["Name", "Score"],
|
|
22
|
+
* ["Alice", 95],
|
|
23
|
+
* ["Bob", 87]
|
|
24
|
+
* ], { showGridLines: true, title: "Scores" });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Multiple sheets:
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const bytes = pdf({
|
|
30
|
+
* sheets: [
|
|
31
|
+
* { name: "Sales", data: [["Product", "Revenue"], ["Widget", 1000]] },
|
|
32
|
+
* { name: "Costs", data: [["Item", "Amount"], ["Rent", 500]] }
|
|
33
|
+
* ]
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example With column widths and styles:
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const bytes = pdf({
|
|
40
|
+
* name: "Report",
|
|
41
|
+
* columns: [{ width: 25 }, { width: 15 }],
|
|
42
|
+
* data: [
|
|
43
|
+
* ["Product", "Revenue"],
|
|
44
|
+
* ["Widget", "$1,000"]
|
|
45
|
+
* ]
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
import { PdfCellType } from "./types.js";
|
|
50
|
+
import { exportPdf } from "./render/pdf-exporter.js";
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Public API
|
|
53
|
+
// =============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Generate a PDF.
|
|
56
|
+
*
|
|
57
|
+
* Accepts anything from a plain 2D array to a multi-sheet workbook.
|
|
58
|
+
*
|
|
59
|
+
* @param input - 2D array, sheet object, or workbook object
|
|
60
|
+
* @param options - PDF export options (page size, margins, etc.)
|
|
61
|
+
* @returns PDF file as Uint8Array
|
|
62
|
+
*/
|
|
63
|
+
export function pdf(input, options) {
|
|
64
|
+
const workbook = normalizeInput(input);
|
|
65
|
+
return exportPdf(workbook, options);
|
|
66
|
+
}
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// Normalization
|
|
69
|
+
// =============================================================================
|
|
70
|
+
function normalizeInput(input) {
|
|
71
|
+
// 2D array → single sheet
|
|
72
|
+
if (Array.isArray(input)) {
|
|
73
|
+
return {
|
|
74
|
+
sheets: [normalizeSheet({ data: input })]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Workbook with sheets array
|
|
78
|
+
if ("sheets" in input) {
|
|
79
|
+
return {
|
|
80
|
+
title: input.title,
|
|
81
|
+
creator: input.author,
|
|
82
|
+
sheets: input.sheets.map((s, i) => normalizeSheet(s, i))
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Single sheet object
|
|
86
|
+
return {
|
|
87
|
+
sheets: [normalizeSheet(input)]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function normalizeSheet(sheet, index) {
|
|
91
|
+
const data = sheet.data;
|
|
92
|
+
const sheetName = sheet.name ?? `Sheet${(index ?? 0) + 1}`;
|
|
93
|
+
// Check if columns have headers
|
|
94
|
+
const columnHeaders = sheet.columns?.map(c => (typeof c === "number" ? undefined : c.header));
|
|
95
|
+
const hasHeaders = columnHeaders?.some(h => h !== undefined) ?? false;
|
|
96
|
+
// Determine dimensions — consider data rows, column definitions, and images
|
|
97
|
+
let maxCols = 0;
|
|
98
|
+
for (const row of data) {
|
|
99
|
+
if (row.length > maxCols) {
|
|
100
|
+
maxCols = row.length;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
let colCount = Math.max(maxCols, sheet.columns?.length ?? 0);
|
|
104
|
+
let totalRows = data.length + (hasHeaders ? 1 : 0);
|
|
105
|
+
// Pre-scan images to extend bounds before the empty check
|
|
106
|
+
if (sheet.images) {
|
|
107
|
+
for (const img of sheet.images) {
|
|
108
|
+
const imgCol = img.col + 1; // 0-indexed → 1-indexed
|
|
109
|
+
const imgRow = img.row + 1;
|
|
110
|
+
if (imgCol > colCount) {
|
|
111
|
+
colCount = imgCol;
|
|
112
|
+
}
|
|
113
|
+
if (imgRow > totalRows) {
|
|
114
|
+
totalRows = imgRow;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (colCount === 0) {
|
|
119
|
+
return {
|
|
120
|
+
name: sheetName,
|
|
121
|
+
bounds: { top: 0, left: 0, bottom: 0, right: 0 },
|
|
122
|
+
columns: new Map(),
|
|
123
|
+
rows: new Map()
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Build columns
|
|
127
|
+
const columns = new Map();
|
|
128
|
+
if (sheet.columns) {
|
|
129
|
+
for (let i = 0; i < sheet.columns.length; i++) {
|
|
130
|
+
const col = sheet.columns[i];
|
|
131
|
+
const width = typeof col === "number" ? col : col.width;
|
|
132
|
+
columns.set(i + 1, { width: width ?? 12 });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Fill missing columns with default width
|
|
136
|
+
for (let c = 1; c <= colCount; c++) {
|
|
137
|
+
if (!columns.has(c)) {
|
|
138
|
+
columns.set(c, { width: 12 });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Build rows
|
|
142
|
+
const rows = new Map();
|
|
143
|
+
let rowOffset = 1;
|
|
144
|
+
// Insert column headers as the first row if provided
|
|
145
|
+
if (hasHeaders && columnHeaders) {
|
|
146
|
+
const cells = new Map();
|
|
147
|
+
for (let c = 0; c < columnHeaders.length; c++) {
|
|
148
|
+
const text = columnHeaders[c];
|
|
149
|
+
if (text === undefined) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
cells.set(c + 1, {
|
|
153
|
+
type: PdfCellType.String,
|
|
154
|
+
value: text,
|
|
155
|
+
text,
|
|
156
|
+
col: c + 1,
|
|
157
|
+
style: { font: { bold: true } }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
rows.set(1, { cells });
|
|
161
|
+
rowOffset = 2;
|
|
162
|
+
}
|
|
163
|
+
// Insert data rows
|
|
164
|
+
for (let r = 0; r < data.length; r++) {
|
|
165
|
+
const rowNum = r + rowOffset;
|
|
166
|
+
const row = data[r];
|
|
167
|
+
const cells = new Map();
|
|
168
|
+
for (let c = 0; c < row.length; c++) {
|
|
169
|
+
const cell = normalizeCell(row[c], c + 1);
|
|
170
|
+
if (cell) {
|
|
171
|
+
cells.set(c + 1, cell);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
rows.set(rowNum, { cells });
|
|
175
|
+
}
|
|
176
|
+
// Normalize images
|
|
177
|
+
let images;
|
|
178
|
+
if (sheet.images && sheet.images.length > 0) {
|
|
179
|
+
images = sheet.images.map(img => ({
|
|
180
|
+
data: img.data,
|
|
181
|
+
format: img.format,
|
|
182
|
+
range: {
|
|
183
|
+
tl: { col: img.col, row: img.row },
|
|
184
|
+
ext: { width: img.width, height: img.height }
|
|
185
|
+
}
|
|
186
|
+
}));
|
|
187
|
+
// Ensure columns and rows exist for the image-extended bounds
|
|
188
|
+
for (let c = 1; c <= colCount; c++) {
|
|
189
|
+
if (!columns.has(c)) {
|
|
190
|
+
columns.set(c, { width: 12 });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (let r = 1; r <= totalRows; r++) {
|
|
194
|
+
if (!rows.has(r)) {
|
|
195
|
+
rows.set(r, { cells: new Map() });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
name: sheetName,
|
|
201
|
+
bounds: { top: 1, left: 1, bottom: totalRows, right: colCount },
|
|
202
|
+
columns,
|
|
203
|
+
rows,
|
|
204
|
+
images
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function normalizeCell(value, col) {
|
|
208
|
+
if (value === null || value === undefined) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
// Styled cell object
|
|
212
|
+
if (typeof value === "object" && !(value instanceof Date) && "value" in value) {
|
|
213
|
+
const cell = value;
|
|
214
|
+
const inner = normalizeCell(cell.value, col);
|
|
215
|
+
if (!inner) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
// Apply style overrides
|
|
219
|
+
const style = {};
|
|
220
|
+
if (cell.bold || cell.italic || cell.fontSize || cell.fontColor) {
|
|
221
|
+
style.font = {
|
|
222
|
+
bold: cell.bold,
|
|
223
|
+
italic: cell.italic,
|
|
224
|
+
size: cell.fontSize,
|
|
225
|
+
color: cell.fontColor ? { argb: cell.fontColor } : undefined
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (cell.fillColor) {
|
|
229
|
+
style.fill = {
|
|
230
|
+
type: "pattern",
|
|
231
|
+
pattern: "solid",
|
|
232
|
+
fgColor: { argb: cell.fillColor }
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (cell.align) {
|
|
236
|
+
style.alignment = { horizontal: cell.align };
|
|
237
|
+
}
|
|
238
|
+
inner.style = style;
|
|
239
|
+
return inner;
|
|
240
|
+
}
|
|
241
|
+
// Primitive values
|
|
242
|
+
if (typeof value === "string") {
|
|
243
|
+
return { type: PdfCellType.String, value, text: value, col };
|
|
244
|
+
}
|
|
245
|
+
if (typeof value === "number") {
|
|
246
|
+
return { type: PdfCellType.Number, value, text: String(value), col };
|
|
247
|
+
}
|
|
248
|
+
if (typeof value === "boolean") {
|
|
249
|
+
return { type: PdfCellType.Boolean, value, text: value ? "TRUE" : "FALSE", col };
|
|
250
|
+
}
|
|
251
|
+
if (value instanceof Date) {
|
|
252
|
+
return { type: PdfCellType.Date, value, text: value.toLocaleDateString(), col };
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|