@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.
Files changed (74) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/FEATURES.md +294 -0
  3. package/README.md +628 -12
  4. package/dist/core/SharedStrings.js +6 -2
  5. package/dist/core/SharedStrings.js.map +1 -1
  6. package/dist/core/Workbook.d.ts +43 -1
  7. package/dist/core/Workbook.js +881 -58
  8. package/dist/core/Workbook.js.map +1 -1
  9. package/dist/core/WorkbookReader.d.ts +18 -4
  10. package/dist/core/WorkbookReader.js +1386 -20
  11. package/dist/core/WorkbookReader.js.map +1 -1
  12. package/dist/core/Worksheet.d.ts +136 -2
  13. package/dist/core/Worksheet.js +828 -63
  14. package/dist/core/Worksheet.js.map +1 -1
  15. package/dist/core/types.d.ts +311 -5
  16. package/dist/core/types.js +12 -1
  17. package/dist/core/types.js.map +1 -1
  18. package/dist/features/ChartBuilder.d.ts +9 -1
  19. package/dist/features/ChartBuilder.js +140 -14
  20. package/dist/features/ChartBuilder.js.map +1 -1
  21. package/dist/features/CsvModule.d.ts +11 -0
  22. package/dist/features/CsvModule.js +137 -0
  23. package/dist/features/CsvModule.js.map +1 -0
  24. package/dist/features/Encryption.d.ts +6 -0
  25. package/dist/features/Encryption.js +806 -0
  26. package/dist/features/Encryption.js.map +1 -0
  27. package/dist/features/FormControlBuilder.d.ts +6 -0
  28. package/dist/features/FormControlBuilder.js +135 -0
  29. package/dist/features/FormControlBuilder.js.map +1 -0
  30. package/dist/features/FormulaEngine.d.ts +22 -0
  31. package/dist/features/FormulaEngine.js +498 -0
  32. package/dist/features/FormulaEngine.js.map +1 -0
  33. package/dist/features/HtmlModule.d.ts +22 -0
  34. package/dist/features/HtmlModule.js +1441 -0
  35. package/dist/features/HtmlModule.js.map +1 -0
  36. package/dist/features/JsonModule.d.ts +10 -0
  37. package/dist/features/JsonModule.js +76 -0
  38. package/dist/features/JsonModule.js.map +1 -0
  39. package/dist/features/PdfModule.d.ts +30 -0
  40. package/dist/features/PdfModule.js +1567 -0
  41. package/dist/features/PdfModule.js.map +1 -0
  42. package/dist/features/PivotTableBuilder.d.ts +7 -0
  43. package/dist/features/PivotTableBuilder.js +170 -0
  44. package/dist/features/PivotTableBuilder.js.map +1 -0
  45. package/dist/features/Signing.d.ts +12 -0
  46. package/dist/features/Signing.js +326 -0
  47. package/dist/features/Signing.js.map +1 -0
  48. package/dist/features/TableBuilder.js +2 -2
  49. package/dist/features/TableBuilder.js.map +1 -1
  50. package/dist/index-min.js +609 -147
  51. package/dist/index.d.ts +19 -1
  52. package/dist/index.js +11 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/styles/StyleRegistry.d.ts +14 -0
  55. package/dist/styles/StyleRegistry.js +95 -30
  56. package/dist/styles/StyleRegistry.js.map +1 -1
  57. package/dist/utils/helpers.d.ts +4 -0
  58. package/dist/utils/helpers.js +64 -14
  59. package/dist/utils/helpers.js.map +1 -1
  60. package/dist/utils/zip.js +145 -73
  61. package/dist/utils/zip.js.map +1 -1
  62. package/dist/vba/VbaProject.d.ts +31 -0
  63. package/dist/vba/VbaProject.js +576 -0
  64. package/dist/vba/VbaProject.js.map +1 -0
  65. package/dist/vba/cfb.d.ts +7 -0
  66. package/dist/vba/cfb.js +352 -0
  67. package/dist/vba/cfb.js.map +1 -0
  68. package/dist/vba/ovba.d.ts +2 -0
  69. package/dist/vba/ovba.js +137 -0
  70. package/dist/vba/ovba.js.map +1 -0
  71. package/package.json +4 -3
  72. package/validator.cs +0 -155
  73. package/validatorEpplus.cs +0 -27
  74. package/validatorReadData.cs +0 -111
package/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # ExcelForge 📊
2
2
 
3
- A **complete TypeScript library** for reading and writing Excel `.xlsx` files with **zero external dependencies**. Works in browsers, Node.js, Deno, Bun, and edge runtimes.
3
+ [![npm version](https://badge.fury.io/js/%40node-projects%2Fexcelforge.svg)](https://badge.fury.io/js/%40node-projects%2Fexcelforge)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- Inspired by EPPlus (C#), 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
+ 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 or one-cell anchors |
21
- | **Tables** | Styled Excel tables with totals row, filter buttons, column definitions |
22
- | **Conditional Formatting** | Cell rules, color scales, data bars, icon sets, top/bottom N, above/below average |
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
- | **Page Setup** | Paper size, orientation, margins, headers/footers (odd/even/first), print options |
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
- | **Auto Filter** | Dropdown filters on column headers |
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
- | **Comments** | Cell comments with author |
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
- └── TableBuilder.ts — Excel table XML
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()`