@node-projects/excelforge 2.4.0 → 3.1.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/.github/FUNDING.yml +4 -0
- package/FEATURES.md +294 -0
- package/README.md +628 -12
- package/dist/core/SharedStrings.js +6 -2
- package/dist/core/SharedStrings.js.map +1 -1
- package/dist/core/Workbook.d.ts +43 -1
- package/dist/core/Workbook.js +881 -58
- package/dist/core/Workbook.js.map +1 -1
- package/dist/core/WorkbookReader.d.ts +18 -4
- package/dist/core/WorkbookReader.js +1386 -20
- package/dist/core/WorkbookReader.js.map +1 -1
- package/dist/core/Worksheet.d.ts +136 -2
- package/dist/core/Worksheet.js +828 -63
- package/dist/core/Worksheet.js.map +1 -1
- package/dist/core/types.d.ts +311 -5
- package/dist/core/types.js +12 -1
- package/dist/core/types.js.map +1 -1
- package/dist/features/ChartBuilder.d.ts +9 -1
- package/dist/features/ChartBuilder.js +140 -14
- package/dist/features/ChartBuilder.js.map +1 -1
- package/dist/features/CsvModule.d.ts +11 -0
- package/dist/features/CsvModule.js +137 -0
- package/dist/features/CsvModule.js.map +1 -0
- package/dist/features/Encryption.d.ts +6 -0
- package/dist/features/Encryption.js +806 -0
- package/dist/features/Encryption.js.map +1 -0
- package/dist/features/FormControlBuilder.d.ts +6 -0
- package/dist/features/FormControlBuilder.js +135 -0
- package/dist/features/FormControlBuilder.js.map +1 -0
- package/dist/features/FormulaEngine.d.ts +22 -0
- package/dist/features/FormulaEngine.js +498 -0
- package/dist/features/FormulaEngine.js.map +1 -0
- package/dist/features/HtmlModule.d.ts +22 -0
- package/dist/features/HtmlModule.js +1441 -0
- package/dist/features/HtmlModule.js.map +1 -0
- package/dist/features/JsonModule.d.ts +10 -0
- package/dist/features/JsonModule.js +76 -0
- package/dist/features/JsonModule.js.map +1 -0
- package/dist/features/PdfModule.d.ts +30 -0
- package/dist/features/PdfModule.js +1567 -0
- package/dist/features/PdfModule.js.map +1 -0
- package/dist/features/PivotTableBuilder.d.ts +7 -0
- package/dist/features/PivotTableBuilder.js +170 -0
- package/dist/features/PivotTableBuilder.js.map +1 -0
- package/dist/features/Signing.d.ts +12 -0
- package/dist/features/Signing.js +326 -0
- package/dist/features/Signing.js.map +1 -0
- package/dist/features/TableBuilder.js +2 -2
- package/dist/features/TableBuilder.js.map +1 -1
- package/dist/index-min.js +609 -147
- package/dist/index.d.ts +19 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/StyleRegistry.d.ts +14 -0
- package/dist/styles/StyleRegistry.js +95 -30
- package/dist/styles/StyleRegistry.js.map +1 -1
- package/dist/utils/helpers.d.ts +4 -0
- package/dist/utils/helpers.js +64 -14
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/zip.js +145 -73
- package/dist/utils/zip.js.map +1 -1
- package/dist/vba/VbaProject.d.ts +31 -0
- package/dist/vba/VbaProject.js +576 -0
- package/dist/vba/VbaProject.js.map +1 -0
- package/dist/vba/cfb.d.ts +7 -0
- package/dist/vba/cfb.js +352 -0
- package/dist/vba/cfb.js.map +1 -0
- package/dist/vba/ovba.d.ts +2 -0
- package/dist/vba/ovba.js +137 -0
- package/dist/vba/ovba.js.map +1 -0
- package/package.json +4 -3
- package/validator.cs +0 -155
- package/validatorEpplus.cs +0 -27
- package/validatorReadData.cs +0 -111
package/README.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# ExcelForge 📊
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/%40node-projects%2Fexcelforge)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A **complete TypeScript library** for reading and writing Excel `.xlsx` and `.xlsm` (macro-enabled) files with **zero external dependencies**. Works in browsers, Node.js, Deno, Bun, and edge runtimes.
|
|
7
|
+
|
|
8
|
+
ExcelForge gives you the full power of the OOXML spec — including real DEFLATE compression, round-trip editing of existing files, and rich property support.
|
|
6
9
|
|
|
7
10
|
---
|
|
8
11
|
|
|
@@ -13,22 +16,38 @@ Inspired by EPPlus (C#), ExcelForge gives you the full power of the OOXML spec
|
|
|
13
16
|
| **Read existing files** | Load `.xlsx` from file, `Uint8Array`, `base64`, or `Blob` |
|
|
14
17
|
| **Patch-only writes** | Re-serialise only changed sheets; preserve pivot tables, VBA, charts, unknown parts verbatim |
|
|
15
18
|
| **Compression** | Full LZ77 + Huffman DEFLATE (levels 0–9). Typical XML compresses 80–85% |
|
|
16
|
-
| **Cell Values** | Strings, numbers, booleans, dates, formulas, array formulas, rich text |
|
|
19
|
+
| **Cell Values** | Strings, numbers, booleans, dates, formulas, array formulas, dynamic arrays, shared formulas, rich text |
|
|
17
20
|
| **Styles** | Fonts, solid/pattern/gradient fills, all border styles, alignment, 30+ number format presets |
|
|
18
21
|
| **Layout** | Merge cells, freeze/split panes, column widths, row heights, hide rows/cols, outline grouping |
|
|
19
|
-
| **Charts** | Bar, column (stacked/100%), line, area, pie, doughnut, scatter, radar, bubble |
|
|
20
|
-
| **Images** | PNG, JPEG, GIF — two-cell
|
|
21
|
-
| **
|
|
22
|
-
| **
|
|
22
|
+
| **Charts** | Bar, column (stacked/100%), line, area, pie, doughnut, scatter, radar, bubble; chart sheets; modern styling with 18 color palettes, gradients, data labels, shadows; chart templates |
|
|
23
|
+
| **Images** | PNG, JPEG, GIF, BMP, SVG, WebP, ICO, EMF, WMF, TIFF — two-cell, one-cell, or absolute anchors |
|
|
24
|
+
| **In-Cell Pictures** | Embed images directly inside cells via richData/metadata (Excel 365+) |
|
|
25
|
+
| **Shapes** | 28 preset shapes (rect, ellipse, arrows, flowchart, etc.) with fill, line, text, rotation |
|
|
26
|
+
| **WordArt** | Text effects with 20 preset transforms (arch, wave, inflate, etc.) |
|
|
27
|
+
| **Tables** | Styled Excel tables with totals row, filter buttons, custom table styles, table slicers |
|
|
28
|
+
| **Conditional Formatting** | Cell rules, color scales, data bars, icon sets (incl. custom), cross-worksheet refs |
|
|
23
29
|
| **Data Validation** | Dropdowns, whole number, decimal, date, time, text length, custom formula |
|
|
24
30
|
| **Sparklines** | Line, bar, stacked — with high/low/first/last/negative colors |
|
|
25
|
-
| **
|
|
31
|
+
| **Pivot Tables** | Row/column/data fields, aggregation, calculated fields, grouping, custom styles, slicers |
|
|
32
|
+
| **Page Setup** | Paper size, orientation, margins, headers/footers (odd/even/first), print options, page breaks |
|
|
26
33
|
| **Protection** | Sheet protection with password, cell locking/hiding |
|
|
27
34
|
| **Named Ranges** | Workbook and sheet-scoped |
|
|
28
|
-
| **
|
|
35
|
+
| **Connections** | OLEDB, ODBC, text/CSV, web — create, read, round-trip; query tables |
|
|
36
|
+
| **Power Query** | Read M formulas from DataMashup; full round-trip preservation |
|
|
37
|
+
| **External Links** | Cross-workbook references with sheet names and defined names |
|
|
38
|
+
| **VBA Macros** | Create/read `.xlsm` with standard modules, class modules, document modules; code signing; full round-trip |
|
|
39
|
+
| **Auto Filter** | Dropdown filters — value, date, custom, top-10, dynamic filters |
|
|
29
40
|
| **Hyperlinks** | External URLs, mailto, internal navigation |
|
|
30
|
-
| **
|
|
41
|
+
| **Form Controls** | Button, checkbox, combobox, listbox, radio, groupbox, label, scrollbar, spinner — with macro assignment |
|
|
42
|
+
| **Dialog Sheets** | Excel 5 dialog sheets with dialog frame, OK/Cancel buttons, combo boxes |
|
|
43
|
+
| **Comments** | Cell comments with author, rich text formatting |
|
|
44
|
+
| **Themes** | Full Office theme XML with customizable colors and fonts |
|
|
31
45
|
| **Multiple Sheets** | Any number, hidden/veryHidden, tab colors |
|
|
46
|
+
| **Formula Engine** | 60+ functions including GETPIVOTDATA — tree-shakeable |
|
|
47
|
+
| **Export** | CSV, JSON, HTML (with CF visualization, sparklines, charts, shapes, form controls), PDF (styled, paginated) |
|
|
48
|
+
| **Encryption** | OOXML Agile Encryption with AES-256-CBC + SHA-512 via Web Crypto API |
|
|
49
|
+
| **Digital Signatures** | Package signing (XML-DSig) + VBA code signing (PKCS#7/CMS, SHA-256) |
|
|
50
|
+
| **Locale** | Configurable decimal/thousands separators, date format, currency symbol |
|
|
32
51
|
| **Core Properties** | Title, author, subject, keywords, description, language, revision, category… |
|
|
33
52
|
| **Extended Properties** | Company, manager, application, appVersion, hyperlinkBase, word/line/page counts… |
|
|
34
53
|
| **Custom Properties** | Typed key-value store: string, int, decimal, bool, date, r8, i8 |
|
|
@@ -383,18 +402,354 @@ ws.addChart({
|
|
|
383
402
|
|
|
384
403
|
Supported chart types: `bar`, `col`, `colStacked`, `col100`, `barStacked`, `bar100`, `line`, `lineStacked`, `area`, `pie`, `doughnut`, `scatter`, `radar`, `bubble`.
|
|
385
404
|
|
|
405
|
+
**Modern chart styling (Excel 2019+):**
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
ws.addChart({
|
|
409
|
+
type: 'column',
|
|
410
|
+
title: 'Styled Chart',
|
|
411
|
+
series: [{
|
|
412
|
+
name: 'Revenue', values: "'Sheet1'!$A$2:$D$2",
|
|
413
|
+
dataLabels: { showValue: true, position: 'outEnd' },
|
|
414
|
+
fillType: 'gradient',
|
|
415
|
+
gradientStops: [{ pos: 0, color: '4472C4' }, { pos: 100, color: 'B4C7E7' }],
|
|
416
|
+
}],
|
|
417
|
+
from: { col: 0, row: 5 }, to: { col: 8, row: 20 },
|
|
418
|
+
colorPalette: 'blue', // 18 palettes: office, blue, orange, green, red, purple, teal...
|
|
419
|
+
shadow: true,
|
|
420
|
+
roundedCorners: true,
|
|
421
|
+
dataLabels: { showPercent: true }, // global data labels
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Chart templates:**
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { saveChartTemplate, applyChartTemplate, serializeChartTemplate, deserializeChartTemplate } from 'excelforge';
|
|
429
|
+
|
|
430
|
+
// Save a chart's style as a template
|
|
431
|
+
const template = saveChartTemplate(chart);
|
|
432
|
+
const json = serializeChartTemplate(template); // serialize to JSON string
|
|
433
|
+
const restored = deserializeChartTemplate(json); // deserialize back
|
|
434
|
+
|
|
435
|
+
// Apply template to a new chart
|
|
436
|
+
const newChart = applyChartTemplate(template, {
|
|
437
|
+
series: [{ name: 'New', values: "'Sheet1'!$A$1:$A$5" }],
|
|
438
|
+
from: { col: 0, row: 0 }, to: { col: 5, row: 10 },
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
386
442
|
### Images
|
|
387
443
|
|
|
444
|
+
Supported formats: `png`, `jpeg`, `gif`, `bmp`, `svg`, `webp`, `ico`, `emf`, `wmf`, `tiff`.
|
|
445
|
+
|
|
388
446
|
```typescript
|
|
389
447
|
import { readFileSync } from 'fs';
|
|
390
448
|
const imgData = readFileSync('./logo.png');
|
|
391
449
|
|
|
450
|
+
// Floating image (two-cell anchor)
|
|
392
451
|
ws.addImage({
|
|
393
452
|
data: imgData, // Buffer, Uint8Array, or base64 string
|
|
394
453
|
format: 'png',
|
|
395
454
|
from: { row: 1, col: 1 },
|
|
396
455
|
to: { row: 8, col: 4 },
|
|
397
456
|
});
|
|
457
|
+
|
|
458
|
+
// One-cell anchor with explicit pixel size
|
|
459
|
+
ws.addImage({
|
|
460
|
+
data: readFileSync('./icon.svg'),
|
|
461
|
+
format: 'svg',
|
|
462
|
+
from: { row: 1, col: 6 },
|
|
463
|
+
width: 80,
|
|
464
|
+
height: 80,
|
|
465
|
+
altText: 'Company icon',
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Absolute positioning (not tied to any cell)
|
|
469
|
+
ws.addImage({
|
|
470
|
+
data: imgData,
|
|
471
|
+
format: 'png',
|
|
472
|
+
position: { x: 200, y: 100 }, // pixels from top-left of sheet
|
|
473
|
+
width: 120,
|
|
474
|
+
height: 80,
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### In-Cell Pictures
|
|
479
|
+
|
|
480
|
+
Embed images directly inside cells (Excel 365+ feature). Uses richData/metadata internally.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import type { CellImage } from '@node-projects/excelforge';
|
|
484
|
+
|
|
485
|
+
ws.addCellImage({
|
|
486
|
+
data: readFileSync('./photo.png'),
|
|
487
|
+
format: 'png',
|
|
488
|
+
cell: 'B2', // cell reference
|
|
489
|
+
altText: 'Product photo',
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Pivot tables
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
const wb = new Workbook();
|
|
497
|
+
|
|
498
|
+
// Source data sheet
|
|
499
|
+
const wsData = wb.addSheet('Data');
|
|
500
|
+
wsData.writeRow(1, 1, ['Region', 'Product', 'Sales', 'Units']);
|
|
501
|
+
wsData.writeArray(2, 1, [
|
|
502
|
+
['North', 'Widget', 12000, 150],
|
|
503
|
+
['South', 'Widget', 9500, 120],
|
|
504
|
+
['North', 'Gadget', 8700, 90],
|
|
505
|
+
['South', 'Gadget', 11200, 140],
|
|
506
|
+
]);
|
|
507
|
+
|
|
508
|
+
// Pivot table on a separate sheet
|
|
509
|
+
const wsPivot = wb.addSheet('Summary');
|
|
510
|
+
wsPivot.addPivotTable({
|
|
511
|
+
name: 'SalesBreakdown',
|
|
512
|
+
sourceSheet: 'Data',
|
|
513
|
+
sourceRef: 'A1:D5',
|
|
514
|
+
targetCell: 'A1',
|
|
515
|
+
rowFields: ['Region'],
|
|
516
|
+
colFields: ['Product'],
|
|
517
|
+
dataFields: [{ field: 'Sales', name: 'Sum of Sales', func: 'sum' }],
|
|
518
|
+
style: 'PivotStyleMedium9',
|
|
519
|
+
rowGrandTotals: true,
|
|
520
|
+
colGrandTotals: true,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
await wb.writeFile('./pivot_report.xlsx');
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Available aggregation functions: `sum`, `count`, `average`, `max`, `min`, `product`, `countNums`, `stdDev`, `stdDevp`, `var`, `varp`.
|
|
527
|
+
|
|
528
|
+
### VBA macros
|
|
529
|
+
|
|
530
|
+
ExcelForge can create, read, and round-trip `.xlsm` files with VBA macros. All module types are supported: standard modules, class modules, and document modules (auto-created for `ThisWorkbook` and each worksheet).
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
import { Workbook, VbaProject } from './src/index.js';
|
|
534
|
+
|
|
535
|
+
const wb = new Workbook();
|
|
536
|
+
const ws = wb.addSheet('Sheet1');
|
|
537
|
+
ws.setValue(1, 1, 'Hello');
|
|
538
|
+
|
|
539
|
+
const vba = new VbaProject();
|
|
540
|
+
|
|
541
|
+
// Standard module
|
|
542
|
+
vba.addModule({
|
|
543
|
+
name: 'Module1',
|
|
544
|
+
type: 'standard',
|
|
545
|
+
code: 'Sub HelloWorld()\r\n MsgBox "Hello from VBA!"\r\nEnd Sub\r\n',
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Class module
|
|
549
|
+
vba.addModule({
|
|
550
|
+
name: 'MyClass',
|
|
551
|
+
type: 'class',
|
|
552
|
+
code: [
|
|
553
|
+
'Private pValue As String',
|
|
554
|
+
'Public Property Get Value() As String',
|
|
555
|
+
' Value = pValue',
|
|
556
|
+
'End Property',
|
|
557
|
+
'Public Property Let Value(v As String)',
|
|
558
|
+
' pValue = v',
|
|
559
|
+
'End Property',
|
|
560
|
+
].join('\r\n') + '\r\n',
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
wb.vbaProject = vba;
|
|
564
|
+
await wb.writeFile('./macros.xlsm'); // must use .xlsm extension
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
Reading VBA from existing files:
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
const wb = await Workbook.fromFile('./macros.xlsm');
|
|
571
|
+
if (wb.vbaProject) {
|
|
572
|
+
for (const mod of wb.vbaProject.modules) {
|
|
573
|
+
console.log(`${mod.name} (${mod.type}): ${mod.code.length} chars`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Modify and re-save — existing modules are preserved
|
|
578
|
+
wb.vbaProject.addModule({ name: 'Module2', type: 'standard', code: '...' });
|
|
579
|
+
wb.vbaProject.removeModule('OldModule');
|
|
580
|
+
await wb.writeFile('./macros_updated.xlsm');
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
> **Note:** Document modules for `ThisWorkbook` and each worksheet are automatically created if not explicitly provided. VBA code uses `\r\n` line endings.
|
|
584
|
+
|
|
585
|
+
### VBA UserForms
|
|
586
|
+
|
|
587
|
+
ExcelForge supports creating VBA UserForm modules with form controls. UserForms are embedded in the VBA project with their designer data and can be viewed/edited in the VBA editor.
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { Workbook, VbaProject } from 'excelforge';
|
|
591
|
+
|
|
592
|
+
const wb = new Workbook();
|
|
593
|
+
wb.addSheet('Sheet1').setValue(1, 1, 'UserForm Demo');
|
|
594
|
+
|
|
595
|
+
const vba = new VbaProject();
|
|
596
|
+
|
|
597
|
+
// Standard module to show the form
|
|
598
|
+
vba.addModule({
|
|
599
|
+
name: 'Module1',
|
|
600
|
+
type: 'standard',
|
|
601
|
+
code: 'Sub ShowForm()\n MyForm.Show\nEnd Sub',
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
// UserForm with controls
|
|
605
|
+
vba.addModule({
|
|
606
|
+
name: 'MyForm',
|
|
607
|
+
type: 'userform',
|
|
608
|
+
controls: [
|
|
609
|
+
{ type: 'Label', name: 'Label1', caption: 'Enter name:', left: 10, top: 10, width: 100, height: 18 },
|
|
610
|
+
{ type: 'TextBox', name: 'TextBox1', caption: '', left: 10, top: 32, width: 160, height: 22 },
|
|
611
|
+
{ type: 'CommandButton', name: 'btnOK', caption: 'OK', left: 50, top: 64, width: 72, height: 26 },
|
|
612
|
+
],
|
|
613
|
+
code: [
|
|
614
|
+
'Private Sub btnOK_Click()',
|
|
615
|
+
' MsgBox "Hello, " & TextBox1.Text',
|
|
616
|
+
' Unload Me',
|
|
617
|
+
'End Sub',
|
|
618
|
+
].join('\n'),
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
wb.vbaProject = vba;
|
|
622
|
+
await wb.writeFile('./userform_demo.xlsm');
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Supported control types: `CommandButton`, `TextBox`, `Label`, `CheckBox`, `OptionButton`, `ComboBox`, `ListBox`, `Frame`, `Image`, `ScrollBar`, `SpinButton`.
|
|
626
|
+
|
|
627
|
+
### Workbook Calc Settings
|
|
628
|
+
|
|
629
|
+
Control how Excel recalculates formulas when the workbook is opened.
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
const wb = new Workbook();
|
|
633
|
+
|
|
634
|
+
wb.calcSettings = {
|
|
635
|
+
calcMode: 'manual', // 'auto' | 'manual' | 'autoNoTable'
|
|
636
|
+
iterate: true, // enable iterative calculation
|
|
637
|
+
iterateCount: 200, // max iterations
|
|
638
|
+
iterateDelta: 0.0001, // convergence threshold
|
|
639
|
+
fullCalcOnLoad: false, // don't force full recalc on open
|
|
640
|
+
calcOnSave: true, // recalculate before saving
|
|
641
|
+
fullPrecision: true, // use full 15-digit precision
|
|
642
|
+
concurrentCalc: false, // disable multi-threaded calc
|
|
643
|
+
};
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
Settings are preserved during round-trip editing. When reading an existing file, `wb.calcSettings` reflects the workbook's current calculation configuration.
|
|
647
|
+
|
|
648
|
+
### OLE Objects
|
|
649
|
+
|
|
650
|
+
Embed binary OLE objects (files, packages) into worksheets.
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
const wb = new Workbook();
|
|
654
|
+
const ws = wb.addSheet('Sheet1');
|
|
655
|
+
|
|
656
|
+
ws.addOleObject({
|
|
657
|
+
name: 'EmbeddedFile',
|
|
658
|
+
progId: 'Package', // OLE program ID
|
|
659
|
+
fileName: 'data.bin', // display name
|
|
660
|
+
data: fileBytes, // Uint8Array of the embedded content
|
|
661
|
+
from: { col: 1, row: 3 }, // top-left anchor
|
|
662
|
+
to: { col: 5, row: 10 }, // bottom-right anchor
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
await wb.writeFile('./with_ole.xlsx');
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### Encryption
|
|
669
|
+
|
|
670
|
+
Encrypt workbooks with a password using OOXML Agile Encryption (AES-256 + SHA-512).
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
import { Workbook, encryptWorkbook, decryptWorkbook, isEncrypted } from 'excelforge';
|
|
674
|
+
|
|
675
|
+
const wb = new Workbook();
|
|
676
|
+
wb.addSheet('Secret').setValue(1, 1, 'Confidential');
|
|
677
|
+
const xlsxData = await wb.build();
|
|
678
|
+
|
|
679
|
+
// Encrypt
|
|
680
|
+
const encrypted = await encryptWorkbook(xlsxData, 'myPassword');
|
|
681
|
+
|
|
682
|
+
// Save encrypted file (still uses .xlsx extension)
|
|
683
|
+
import { writeFileSync } from 'fs';
|
|
684
|
+
writeFileSync('./protected.xlsx', encrypted);
|
|
685
|
+
|
|
686
|
+
// Check if a file is encrypted
|
|
687
|
+
console.log(isEncrypted(encrypted)); // true
|
|
688
|
+
|
|
689
|
+
// Decrypt
|
|
690
|
+
const decrypted = await decryptWorkbook(encrypted, 'myPassword');
|
|
691
|
+
const wb2 = await Workbook.fromBytes(decrypted);
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### PDF Export
|
|
695
|
+
|
|
696
|
+
Export worksheets and workbooks as PDF documents with cell styling, pagination, and fit-to-width.
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
import { Workbook, worksheetToPdf, workbookToPdf } from 'excelforge';
|
|
700
|
+
|
|
701
|
+
const wb = new Workbook();
|
|
702
|
+
const ws = wb.addSheet('Report');
|
|
703
|
+
// ... populate cells with styles ...
|
|
704
|
+
|
|
705
|
+
// Single worksheet PDF
|
|
706
|
+
const pdf = worksheetToPdf(ws, {
|
|
707
|
+
paperSize: 'a4',
|
|
708
|
+
orientation: 'portrait',
|
|
709
|
+
fitToWidth: true, // auto-scale to fit page width
|
|
710
|
+
gridLines: true, // draw cell grid lines
|
|
711
|
+
headings: false, // row/column headings
|
|
712
|
+
repeatRows: 1, // repeat header row on each page
|
|
713
|
+
headerText: 'Sales Report',
|
|
714
|
+
footerText: 'Page &P of &N',
|
|
715
|
+
title: 'Sales Report',
|
|
716
|
+
author: 'ExcelForge',
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
import { writeFileSync } from 'fs';
|
|
720
|
+
writeFileSync('./report.pdf', pdf);
|
|
721
|
+
|
|
722
|
+
// Multi-sheet workbook PDF
|
|
723
|
+
const wbPdf = workbookToPdf(wb, { footerText: 'Page &P / &N' });
|
|
724
|
+
writeFileSync('./workbook.pdf', wbPdf);
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Digital Signatures
|
|
728
|
+
|
|
729
|
+
Sign OOXML packages and VBA projects using RSA with SHA-256 via Web Crypto API.
|
|
730
|
+
|
|
731
|
+
```typescript
|
|
732
|
+
import { signPackage, signVbaProject, signWorkbook } from 'excelforge';
|
|
733
|
+
|
|
734
|
+
// Sign the entire package
|
|
735
|
+
const parts = new Map<string, Uint8Array>();
|
|
736
|
+
parts.set('xl/workbook.xml', workbookBytes);
|
|
737
|
+
parts.set('xl/worksheets/sheet1.xml', sheetBytes);
|
|
738
|
+
|
|
739
|
+
const sigEntries = await signPackage(parts, {
|
|
740
|
+
certificate: pemCertificate, // PEM-encoded X.509 certificate
|
|
741
|
+
privateKey: pemPrivateKey, // PEM-encoded PKCS#8 private key
|
|
742
|
+
});
|
|
743
|
+
// sigEntries contains _xmlsignatures/sig1.xml, origin.sigs, and rels
|
|
744
|
+
|
|
745
|
+
// Sign a VBA project
|
|
746
|
+
const vbaSignature = await signVbaProject(vbaProjectBin, {
|
|
747
|
+
certificate: pemCertificate,
|
|
748
|
+
privateKey: pemPrivateKey,
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// Or sign both at once
|
|
752
|
+
const result = await signWorkbook(parts, { certificate, privateKey }, vbaProjectBin);
|
|
398
753
|
```
|
|
399
754
|
|
|
400
755
|
### Page setup
|
|
@@ -420,6 +775,244 @@ ws.headerFooter = {
|
|
|
420
775
|
};
|
|
421
776
|
```
|
|
422
777
|
|
|
778
|
+
### Page breaks
|
|
779
|
+
|
|
780
|
+
```typescript
|
|
781
|
+
// Add manual page breaks for printing
|
|
782
|
+
ws.addRowBreak(20); // page break after row 20
|
|
783
|
+
ws.addRowBreak(40); // page break after row 40
|
|
784
|
+
ws.addColBreak(5); // page break after column E
|
|
785
|
+
|
|
786
|
+
// Read page breaks from an existing file
|
|
787
|
+
const wb = await Workbook.fromBytes(data);
|
|
788
|
+
const ws = wb.getSheet('Sheet1')!;
|
|
789
|
+
for (const brk of ws.getRowBreaks()) {
|
|
790
|
+
console.log(`Row break at ${brk.id}, manual: ${brk.manual}`);
|
|
791
|
+
}
|
|
792
|
+
for (const brk of ws.getColBreaks()) {
|
|
793
|
+
console.log(`Col break at ${brk.id}, manual: ${brk.manual}`);
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
Page breaks are fully preserved during round-trip editing, even when sheets are modified.
|
|
798
|
+
|
|
799
|
+
### Named ranges
|
|
800
|
+
|
|
801
|
+
```typescript
|
|
802
|
+
// Define workbook-scoped named ranges
|
|
803
|
+
wb.addNamedRange({ name: 'SalesData', ref: 'Data!$A$1:$A$5' });
|
|
804
|
+
wb.addNamedRange({ name: 'Products', ref: 'Data!$B$1:$B$5', comment: 'Product list' });
|
|
805
|
+
|
|
806
|
+
// Define sheet-scoped named range
|
|
807
|
+
wb.addNamedRange({ name: 'LocalTotal', ref: 'Data!$A$6', scope: 'Data' });
|
|
808
|
+
|
|
809
|
+
// Use in formulas
|
|
810
|
+
ws.setFormula(1, 1, 'SUM(SalesData)');
|
|
811
|
+
|
|
812
|
+
// Read named ranges from an existing file
|
|
813
|
+
const wb2 = await Workbook.fromBytes(data);
|
|
814
|
+
const ranges = wb2.getNamedRanges(); // all named ranges
|
|
815
|
+
const sales = wb2.getNamedRange('SalesData'); // find by name
|
|
816
|
+
console.log(sales?.ref); // "Data!$A$1:$A$5"
|
|
817
|
+
|
|
818
|
+
// Remove a named range
|
|
819
|
+
wb2.removeNamedRange('SalesData');
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Named ranges (including scope and comments) are fully preserved during round-trip editing.
|
|
823
|
+
|
|
824
|
+
### Connections & Power Query
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
// Add a data connection (OLEDB, ODBC, text/CSV, web, etc.)
|
|
828
|
+
wb.addConnection({
|
|
829
|
+
id: 1,
|
|
830
|
+
name: 'SalesDB',
|
|
831
|
+
type: 'oledb', // 'odbc' | 'dao' | 'file' | 'web' | 'oledb' | 'text' | 'dsp'
|
|
832
|
+
connectionString: 'Provider=SQLOLEDB;Data Source=server;Initial Catalog=Sales;',
|
|
833
|
+
command: 'SELECT * FROM Orders',
|
|
834
|
+
commandType: 'sql', // 'sql' | 'table' | 'default' | 'web' | 'oledb'
|
|
835
|
+
description: 'Sales database connection',
|
|
836
|
+
background: true,
|
|
837
|
+
saveData: true,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
// Read connections from an existing file
|
|
841
|
+
const wb2 = await Workbook.fromBytes(data);
|
|
842
|
+
const conns = wb2.getConnections(); // all connections
|
|
843
|
+
const sales = wb2.getConnection('SalesDB'); // find by name
|
|
844
|
+
wb2.removeConnection('SalesDB'); // remove by name
|
|
845
|
+
|
|
846
|
+
// Read Power Query M formulas (extracted from DataMashup)
|
|
847
|
+
const queries = wb2.getPowerQueries(); // all queries
|
|
848
|
+
const q = wb2.getPowerQuery('MyQuery'); // find by name
|
|
849
|
+
console.log(q?.formula); // Power Query M code
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
Connections are fully preserved during round-trip editing. Power Query formulas (M code) stored in DataMashup binary blobs are automatically extracted for read access. Power Query/Power Pivot data models created in Excel are preserved verbatim during round-trip — you can safely open, modify cells, and save without losing any Power Query or Power Pivot features.
|
|
853
|
+
|
|
854
|
+
### Form Controls
|
|
855
|
+
|
|
856
|
+
```typescript
|
|
857
|
+
// Add a button with a macro
|
|
858
|
+
ws.addFormControl({
|
|
859
|
+
type: 'button',
|
|
860
|
+
from: { col: 1, row: 2 },
|
|
861
|
+
to: { col: 3, row: 4 },
|
|
862
|
+
text: 'Run Report',
|
|
863
|
+
macro: 'Sheet1.RunReport',
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// Button sized by width/height (no 'to' anchor needed)
|
|
867
|
+
ws.addFormControl({
|
|
868
|
+
type: 'button',
|
|
869
|
+
from: { col: 1, row: 5 },
|
|
870
|
+
width: 120, height: 30, // pixels
|
|
871
|
+
text: 'Compact Button',
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
// CheckBox linked to a cell
|
|
875
|
+
ws.addFormControl({
|
|
876
|
+
type: 'checkBox',
|
|
877
|
+
from: { col: 1, row: 7 },
|
|
878
|
+
to: { col: 3, row: 8 },
|
|
879
|
+
text: 'Enable Feature',
|
|
880
|
+
linkedCell: '$B$10',
|
|
881
|
+
checked: 'checked', // 'checked' | 'unchecked' | 'mixed'
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
// ComboBox (dropdown) with input range
|
|
885
|
+
ws.addFormControl({
|
|
886
|
+
type: 'comboBox',
|
|
887
|
+
from: { col: 1, row: 7 },
|
|
888
|
+
to: { col: 3, row: 8 },
|
|
889
|
+
linkedCell: '$B$11',
|
|
890
|
+
inputRange: '$D$1:$D$5',
|
|
891
|
+
dropLines: 5,
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// ListBox, OptionButton, GroupBox, Label, ScrollBar, Spinner
|
|
895
|
+
ws.addFormControl({
|
|
896
|
+
type: 'scrollBar',
|
|
897
|
+
from: { col: 4, row: 6 },
|
|
898
|
+
to: { col: 6, row: 7 },
|
|
899
|
+
linkedCell: '$B$14',
|
|
900
|
+
min: 0, max: 100, inc: 1, page: 10, val: 50,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Read form controls from an existing file
|
|
904
|
+
const wb2 = await Workbook.fromBytes(data);
|
|
905
|
+
const controls = ws.getFormControls();
|
|
906
|
+
for (const ctrl of controls) {
|
|
907
|
+
console.log(ctrl.type, ctrl.linkedCell, ctrl.macro);
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
Supported control types: `button`, `checkBox`, `comboBox`, `listBox`, `optionButton`, `groupBox`, `label`, `scrollBar`, `spinner`. All control types support `macro` assignment and are fully preserved during round-trip editing.
|
|
912
|
+
|
|
913
|
+
### Shapes
|
|
914
|
+
|
|
915
|
+
```typescript
|
|
916
|
+
ws.addShape({
|
|
917
|
+
type: 'roundRect',
|
|
918
|
+
from: { col: 1, row: 3 },
|
|
919
|
+
to: { col: 5, row: 8 },
|
|
920
|
+
fillColor: '4472C4',
|
|
921
|
+
lineColor: '2F5496',
|
|
922
|
+
text: 'Process Step',
|
|
923
|
+
rotation: 0,
|
|
924
|
+
});
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
Supported shape types: `rect`, `roundRect`, `ellipse`, `triangle`, `diamond`, `pentagon`, `hexagon`, `octagon`, `star5`, `star6`, `rightArrow`, `leftArrow`, `upArrow`, `downArrow`, `line`, `curvedConnector3`, `callout1`, `callout2`, `cloud`, `heart`, `lightningBolt`, `sun`, `moon`, `smileyFace`, `flowChartProcess`, `flowChartDecision`, `flowChartTerminator`, `flowChartDocument`.
|
|
928
|
+
|
|
929
|
+
### WordArt
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
ws.addWordArt({
|
|
933
|
+
text: 'SALE!',
|
|
934
|
+
preset: 'textArchUp',
|
|
935
|
+
font: { name: 'Impact', size: 48, bold: true },
|
|
936
|
+
fillColor: 'FF0000',
|
|
937
|
+
outlineColor: '990000',
|
|
938
|
+
from: { col: 1, row: 1 },
|
|
939
|
+
to: { col: 8, row: 6 },
|
|
940
|
+
});
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
Supported presets: `textPlain`, `textArchUp`, `textArchDown`, `textCircle`, `textWave1`, `textWave2`, `textInflate`, `textDeflate`, `textFadeUp`, `textFadeDown`, `textSlantUp`, `textSlantDown`, and more.
|
|
944
|
+
|
|
945
|
+
### Themes
|
|
946
|
+
|
|
947
|
+
```typescript
|
|
948
|
+
wb.theme = {
|
|
949
|
+
name: 'Corporate Theme',
|
|
950
|
+
colors: [
|
|
951
|
+
{ name: 'dk1', color: '000000' }, { name: 'lt1', color: 'FFFFFF' },
|
|
952
|
+
{ name: 'dk2', color: '44546A' }, { name: 'lt2', color: 'E7E6E6' },
|
|
953
|
+
{ name: 'accent1', color: '4472C4' }, { name: 'accent2', color: 'ED7D31' },
|
|
954
|
+
{ name: 'accent3', color: 'A5A5A5' }, { name: 'accent4', color: 'FFC000' },
|
|
955
|
+
{ name: 'accent5', color: '5B9BD5' }, { name: 'accent6', color: '70AD47' },
|
|
956
|
+
{ name: 'hlink', color: '0563C1' }, { name: 'folHlink', color: '954F72' },
|
|
957
|
+
],
|
|
958
|
+
majorFont: 'Calibri Light',
|
|
959
|
+
minorFont: 'Calibri',
|
|
960
|
+
};
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
### Table Slicers
|
|
964
|
+
|
|
965
|
+
```typescript
|
|
966
|
+
ws.addTableSlicer({
|
|
967
|
+
name: 'RegionSlicer',
|
|
968
|
+
tableName: 'SalesTable',
|
|
969
|
+
columnName: 'Region',
|
|
970
|
+
caption: 'Filter by Region',
|
|
971
|
+
style: 'SlicerStyleLight1',
|
|
972
|
+
});
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
### Pivot Slicers & Custom Pivot Styles
|
|
976
|
+
|
|
977
|
+
```typescript
|
|
978
|
+
wb.registerPivotStyle({
|
|
979
|
+
name: 'BrandedPivot',
|
|
980
|
+
elements: [
|
|
981
|
+
{ type: 'headerRow', style: { font: { bold: true, color: 'FFFFFF' }, fill: { type: 'pattern', pattern: 'solid', fgColor: '4472C4' } } },
|
|
982
|
+
],
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
wb.addPivotSlicer({
|
|
986
|
+
name: 'ProductSlicer',
|
|
987
|
+
pivotTableName: 'SalesPivot',
|
|
988
|
+
fieldName: 'Product',
|
|
989
|
+
caption: 'Product Filter',
|
|
990
|
+
});
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
### External Links
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
wb.addExternalLink({
|
|
997
|
+
target: 'file:///C:/Reports/Budget.xlsx',
|
|
998
|
+
sheets: [{ name: 'Sheet1' }, { name: 'Summary' }],
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
// Reference external data in formulas
|
|
1002
|
+
ws.setFormula(1, 1, '[1]Sheet1!A1');
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### Locale Settings
|
|
1006
|
+
|
|
1007
|
+
```typescript
|
|
1008
|
+
wb.locale = {
|
|
1009
|
+
decimalSeparator: ',',
|
|
1010
|
+
thousandsSeparator: '.',
|
|
1011
|
+
dateFormat: 'DD.MM.YYYY',
|
|
1012
|
+
currencySymbol: '€',
|
|
1013
|
+
};
|
|
1014
|
+
```
|
|
1015
|
+
|
|
423
1016
|
### Sheet protection
|
|
424
1017
|
|
|
425
1018
|
```typescript
|
|
@@ -498,8 +1091,17 @@ ExcelForge
|
|
|
498
1091
|
│ ├── StyleRegistry.ts — interns fonts/fills/borders/xfs, emits styles.xml
|
|
499
1092
|
│ └── builders.ts — fluent style() builder, Colors/NumFmt/Styles presets
|
|
500
1093
|
├── features/
|
|
501
|
-
│ ├── ChartBuilder.ts — DrawingML chart XML for 15+ chart types
|
|
502
|
-
│
|
|
1094
|
+
│ ├── ChartBuilder.ts — DrawingML chart XML for 15+ chart types, templates, modern styling
|
|
1095
|
+
│ ├── TableBuilder.ts — Excel table XML
|
|
1096
|
+
│ ├── PivotTableBuilder.ts — pivot table + cache XML
|
|
1097
|
+
│ ├── HtmlModule.ts — HTML/CSS export with charts, images, sparklines, shapes
|
|
1098
|
+
│ ├── PdfModule.ts — PDF export with cell styles, pagination, images
|
|
1099
|
+
│ ├── Encryption.ts — OOXML Agile Encryption (AES-256-CBC + SHA-512)
|
|
1100
|
+
│ └── Signing.ts — Digital signatures (XML-DSig + VBA PKCS#7/CMS)
|
|
1101
|
+
├── vba/
|
|
1102
|
+
│ ├── VbaProject.ts — VBA project build/parse, module management
|
|
1103
|
+
│ ├── cfb.ts — Compound Binary File (OLE2) reader & writer
|
|
1104
|
+
│ └── ovba.ts — MS-OVBA compression/decompression
|
|
503
1105
|
└── utils/
|
|
504
1106
|
├── zip.ts — ZIP writer with full LZ77+Huffman DEFLATE
|
|
505
1107
|
├── zipReader.ts — ZIP reader (STORE + DEFLATE via DecompressionStream)
|
|
@@ -553,6 +1155,20 @@ document.getElementById('file').addEventListener('change', async (e) => {
|
|
|
553
1155
|
|
|
554
1156
|
## Changelog
|
|
555
1157
|
|
|
1158
|
+
### v3.0 — More
|
|
1159
|
+
- **Form Controls** - create Form Controls
|
|
1160
|
+
- Wordart
|
|
1161
|
+
- Formula Objects
|
|
1162
|
+
- Chart Pages
|
|
1163
|
+
- Many more features....
|
|
1164
|
+
|
|
1165
|
+
### v2.4 — Pivot Tables & VBA Macros
|
|
1166
|
+
|
|
1167
|
+
- **Pivot tables** — create pivot tables with row/column/data fields, 11 aggregation functions, customisable styles
|
|
1168
|
+
- **VBA macros** — create, read, and round-trip `.xlsm` files with standard, class, and document modules
|
|
1169
|
+
- **CFB (OLE2) support** — MS-CFB reader/writer for vbaProject.bin, with MS-OVBA compression
|
|
1170
|
+
- **Automatic sheet modules** — document modules for ThisWorkbook and each worksheet are auto-generated
|
|
1171
|
+
|
|
556
1172
|
### v2.0 — Read, Modify, Compress
|
|
557
1173
|
|
|
558
1174
|
- **Read existing XLSX files** — `Workbook.fromFile()`, `fromBytes()`, `fromBase64()`, `fromBlob()`
|