node-pptx-templater 1.1.2 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-pptx-templater",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.",
5
5
  "main": "./src/index.js",
6
6
  "type": "commonjs",
@@ -37,6 +37,8 @@
37
37
  "example:table-extraction": "node examples/table-extraction.js",
38
38
  "example:nested-rows": "node examples/nested-table-rows.js",
39
39
  "example:xml-folder": "node examples/xml-folder-workflow.js",
40
+ "example:shape-cells": "node examples/shape-cells.js",
41
+ "example:all": "node scripts/run-examples-check.js",
40
42
  "prepublishOnly": "npm run lint && npm run test"
41
43
  },
42
44
  "keywords": [
@@ -161,11 +161,11 @@ class OutputWriter {
161
161
  : e.compressionMethod === 0
162
162
  ? 'STORE'
163
163
  : `UNKNOWN(${e.compressionMethod})`
164
- console.log(e.name)
165
- console.log(`compressed: ${e.compressedSize}`)
166
- console.log(`uncompressed: ${e.uncompressedSize}`)
167
- console.log(`crc: ${e.crc32}`)
168
- console.log(`method: ${methodStr}`)
164
+ logger.info(e.name)
165
+ logger.info(`compressed: ${e.compressedSize}`)
166
+ logger.info(`uncompressed: ${e.uncompressedSize}`)
167
+ logger.info(`crc: ${e.crc32}`)
168
+ logger.info(`method: ${methodStr}`)
169
169
  })
170
170
  logger.info('--- End of ZIP debug output ---')
171
171
  }
@@ -1569,11 +1569,30 @@ class PPTXTemplater {
1569
1569
  }
1570
1570
 
1571
1571
  /**
1572
- * Appends one or more rows to a table. Supports flat arrays and nested arrays
1573
- * for rowspan-merged cells.
1572
+ * Appends one or more rows to a table. Supports flat arrays, nested arrays
1573
+ * for rowspan-merged cells, and **Shape Cell** configuration objects that insert
1574
+ * a graphic indicator directly inside a cell.
1575
+ *
1576
+ * ### Shape Cell Object
1577
+ * Instead of a string value you may pass an object with the following keys:
1578
+ *
1579
+ * | Key | Type | Default | Description |
1580
+ * |-----|------|---------|-------------|
1581
+ * | `type` | `string` | **required** | Shape preset: `'circle'`, `'square'`, `'rectangle'`, `'triangle'`, `'diamond'`, `'hexagon'`, `'line'`. |
1582
+ * | `text` | `string` | `''` | Optional label rendered next to the shape. |
1583
+ * | `color` | `string` | `'#4CAF50'` | Fill colour of the shape (hex). |
1584
+ * | `width` | `number` | `14` | Shape width in points. |
1585
+ * | `height` | `number` | `14` | Shape height in points. |
1586
+ * | `position` | `string` | `'center'` | Alignment inside the cell: `'center'`, `'left'`, `'right'`, `'top'`, `'bottom'`, `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`. |
1587
+ * | `offsetX` | `number` | `0` | Additional horizontal offset from the resolved anchor (pt). |
1588
+ * | `offsetY` | `number` | `0` | Additional vertical offset from the resolved anchor (pt). |
1589
+ *
1590
+ * The shape is added as a floating overlay anchored to the cell; the table
1591
+ * dimensions (row heights, column widths) are **never mutated**.
1574
1592
  *
1575
1593
  * @param {string} tableId - Table name or shape ID.
1576
- * @param {Array<string|Array<string>>} rowData - Row data. Nested arrays create rowspan cells.
1594
+ * @param {Array<string|Array<string>|Object>} rowData - Row data. Each element may be
1595
+ * a plain string, a nested array (rowspan), or a Shape Cell configuration object.
1577
1596
  * @param {Object} [options] - Row insertion options.
1578
1597
  * @param {'rowspan'|'auto'|'none'} [options.mergeStrategy='rowspan'] - How to handle nested arrays.
1579
1598
  * `'rowspan'` creates OpenXML vertical spans, `'auto'` merges identical adjacent values,
@@ -1586,12 +1605,33 @@ class PPTXTemplater {
1586
1605
  *
1587
1606
  * // Nested row with rowspan
1588
1607
  * ppt.addTableRow('SalesTable', ['Region', ['Q1', 'Q2'], '$5K'], { mergeStrategy: 'rowspan' });
1608
+ *
1609
+ * // KPI indicator – green circle placed in the cell centre, no label
1610
+ * await ppt.addTableRow('StatusTable', [
1611
+ * 'Alice',
1612
+ * 'Engineering',
1613
+ * { type: 'circle', color: '#4CAF50', width: 12, height: 12, position: 'center' },
1614
+ * ]);
1615
+ *
1616
+ * // Status badge – shape with accompanying text label, aligned to the left
1617
+ * await ppt.addTableRow('StatusTable', [
1618
+ * 'Bob',
1619
+ * 'Design',
1620
+ * { type: 'circle', color: '#F44336', text: 'Blocked', position: 'left' },
1621
+ * ]);
1589
1622
  */
1590
1623
  addTableRow(tableId, rowData, options = {}) {
1591
1624
  this.#assertLoaded()
1592
1625
  const targetIndices = this.#getTargetSlideIndices()
1593
1626
  for (const idx of targetIndices) {
1594
- this.#tableManager.addTableRow(idx, tableId, rowData, this.#slideManager, options)
1627
+ this.#tableManager.addTableRow(
1628
+ idx,
1629
+ tableId,
1630
+ rowData,
1631
+ this.#slideManager,
1632
+ this.#shapeManager,
1633
+ options
1634
+ )
1595
1635
  }
1596
1636
  return this
1597
1637
  }
@@ -1610,7 +1650,13 @@ class PPTXTemplater {
1610
1650
  this.#assertLoaded()
1611
1651
  const targetIndices = this.#getTargetSlideIndices()
1612
1652
  for (const idx of targetIndices) {
1613
- this.#tableManager.removeTableRow(idx, tableId, rowIndex, this.#slideManager)
1653
+ this.#tableManager.removeTableRow(
1654
+ idx,
1655
+ tableId,
1656
+ rowIndex,
1657
+ this.#slideManager,
1658
+ this.#shapeManager
1659
+ )
1614
1660
  }
1615
1661
  return this
1616
1662
  }
@@ -1630,7 +1676,14 @@ class PPTXTemplater {
1630
1676
  this.#assertLoaded()
1631
1677
  const targetIndices = this.#getTargetSlideIndices()
1632
1678
  for (const idx of targetIndices) {
1633
- this.#tableManager.insertTableRow(idx, tableId, rowIndex, rowData, this.#slideManager)
1679
+ this.#tableManager.insertTableRow(
1680
+ idx,
1681
+ tableId,
1682
+ rowIndex,
1683
+ rowData,
1684
+ this.#slideManager,
1685
+ this.#shapeManager
1686
+ )
1634
1687
  }
1635
1688
  return this
1636
1689
  }
@@ -1655,7 +1708,8 @@ class PPTXTemplater {
1655
1708
  tableId,
1656
1709
  sourceRowIndex,
1657
1710
  targetRowIndex,
1658
- this.#slideManager
1711
+ this.#slideManager,
1712
+ this.#shapeManager
1659
1713
  )
1660
1714
  }
1661
1715
  return this
@@ -1735,7 +1789,16 @@ class PPTXTemplater {
1735
1789
  }
1736
1790
 
1737
1791
  for (const idx of targetIndices) {
1738
- this.#tableManager.mergeCells(idx, tableId, sRow, sCol, eRow, eCol, this.#slideManager)
1792
+ this.#tableManager.mergeCells(
1793
+ idx,
1794
+ tableId,
1795
+ sRow,
1796
+ sCol,
1797
+ eRow,
1798
+ eCol,
1799
+ this.#slideManager,
1800
+ this.#shapeManager
1801
+ )
1739
1802
  }
1740
1803
  return this
1741
1804
  }
@@ -1786,9 +1849,25 @@ class PPTXTemplater {
1786
1849
 
1787
1850
  for (const idx of targetIndices) {
1788
1851
  if (isCellCoord) {
1789
- this.#tableManager.unmergeCells(idx, tableId, cellRow, cellCol, this.#slideManager)
1852
+ this.#tableManager.unmergeCells(
1853
+ idx,
1854
+ tableId,
1855
+ cellRow,
1856
+ cellCol,
1857
+ this.#slideManager,
1858
+ this.#shapeManager
1859
+ )
1790
1860
  } else {
1791
- this.#tableManager.unmergeCells(idx, tableId, sRow, sCol, eRow, eCol, this.#slideManager)
1861
+ this.#tableManager.unmergeCells(
1862
+ idx,
1863
+ tableId,
1864
+ sRow,
1865
+ sCol,
1866
+ eRow,
1867
+ eCol,
1868
+ this.#slideManager,
1869
+ this.#shapeManager
1870
+ )
1792
1871
  }
1793
1872
  }
1794
1873
  return this
@@ -2654,6 +2733,16 @@ class PPTXTemplater {
2654
2733
  return this.#shapeManager.validateShape(options)
2655
2734
  }
2656
2735
 
2736
+ /**
2737
+ * Adds a new shape to the targeted slide(s).
2738
+ *
2739
+ * @param {string|Object} typeOrOptions Either the shape type string (e.g., 'rect', 'ellipse') or a full options object.
2740
+ * @param {Object} [options={}] Additional configuration options if type was specified as a string.
2741
+ * @returns {Promise<PPTXTemplater>} The templater instance for chaining.
2742
+ *
2743
+ * @example
2744
+ * await ppt.addShape('rect', { id: 'MyShape', x: 100, y: 100, width: 200, height: 100 });
2745
+ */
2657
2746
  async addShape(typeOrOptions, options = {}) {
2658
2747
  this.#assertLoaded()
2659
2748
  let resolvedOptions = {}
@@ -2738,14 +2827,28 @@ class PPTXTemplater {
2738
2827
  */
2739
2828
  async addCellShape(tableId, rowIndex, colIndex, options) {
2740
2829
  this.#assertLoaded()
2830
+ let row = rowIndex
2831
+ let col = colIndex
2832
+ let opts = options
2833
+ if (rowIndex && typeof rowIndex === 'object') {
2834
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2835
+ col =
2836
+ rowIndex.column !== undefined
2837
+ ? rowIndex.column
2838
+ : rowIndex.col !== undefined
2839
+ ? rowIndex.col
2840
+ : rowIndex.colIndex
2841
+ opts = rowIndex
2842
+ }
2843
+
2741
2844
  const targetIndices = this.#getTargetSlideIndices()
2742
2845
  for (const idx of targetIndices) {
2743
2846
  this.#tableManager.addCellShape(
2744
2847
  idx,
2745
2848
  tableId,
2746
- rowIndex,
2747
- colIndex,
2748
- options,
2849
+ row,
2850
+ col,
2851
+ opts,
2749
2852
  this.#slideManager,
2750
2853
  this.#shapeManager
2751
2854
  )
@@ -2765,15 +2868,36 @@ class PPTXTemplater {
2765
2868
  */
2766
2869
  async updateCellShape(tableId, rowIndex, colIndex, shapeIndex, options) {
2767
2870
  this.#assertLoaded()
2871
+ let row = rowIndex
2872
+ let col = colIndex
2873
+ let shpIdx = shapeIndex
2874
+ let opts = options
2875
+ if (rowIndex && typeof rowIndex === 'object') {
2876
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2877
+ col =
2878
+ rowIndex.column !== undefined
2879
+ ? rowIndex.column
2880
+ : rowIndex.col !== undefined
2881
+ ? rowIndex.col
2882
+ : rowIndex.colIndex
2883
+ shpIdx =
2884
+ rowIndex.shapeIndex !== undefined
2885
+ ? rowIndex.shapeIndex
2886
+ : rowIndex.shape !== undefined
2887
+ ? rowIndex.shape
2888
+ : colIndex
2889
+ opts = options !== undefined ? options : rowIndex
2890
+ }
2891
+
2768
2892
  const targetIndices = this.#getTargetSlideIndices()
2769
2893
  for (const idx of targetIndices) {
2770
2894
  this.#tableManager.updateCellShape(
2771
2895
  idx,
2772
2896
  tableId,
2773
- rowIndex,
2774
- colIndex,
2775
- shapeIndex,
2776
- options,
2897
+ row,
2898
+ col,
2899
+ shpIdx,
2900
+ opts,
2777
2901
  this.#slideManager,
2778
2902
  this.#shapeManager
2779
2903
  )
@@ -2792,14 +2916,33 @@ class PPTXTemplater {
2792
2916
  */
2793
2917
  async removeCellShape(tableId, rowIndex, colIndex, shapeIndex) {
2794
2918
  this.#assertLoaded()
2919
+ let row = rowIndex
2920
+ let col = colIndex
2921
+ let shpIdx = shapeIndex
2922
+ if (rowIndex && typeof rowIndex === 'object') {
2923
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2924
+ col =
2925
+ rowIndex.column !== undefined
2926
+ ? rowIndex.column
2927
+ : rowIndex.col !== undefined
2928
+ ? rowIndex.col
2929
+ : rowIndex.colIndex
2930
+ shpIdx =
2931
+ rowIndex.shapeIndex !== undefined
2932
+ ? rowIndex.shapeIndex
2933
+ : rowIndex.shape !== undefined
2934
+ ? rowIndex.shape
2935
+ : colIndex
2936
+ }
2937
+
2795
2938
  const targetIndices = this.#getTargetSlideIndices()
2796
2939
  for (const idx of targetIndices) {
2797
2940
  this.#tableManager.removeCellShape(
2798
2941
  idx,
2799
2942
  tableId,
2800
- rowIndex,
2801
- colIndex,
2802
- shapeIndex,
2943
+ row,
2944
+ col,
2945
+ shpIdx,
2803
2946
  this.#slideManager,
2804
2947
  this.#shapeManager
2805
2948
  )
@@ -2818,14 +2961,33 @@ class PPTXTemplater {
2818
2961
  */
2819
2962
  getCellShape(tableId, rowIndex, colIndex, shapeIndex) {
2820
2963
  this.#assertLoaded()
2964
+ let row = rowIndex
2965
+ let col = colIndex
2966
+ let shpIdx = shapeIndex
2967
+ if (rowIndex && typeof rowIndex === 'object') {
2968
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2969
+ col =
2970
+ rowIndex.column !== undefined
2971
+ ? rowIndex.column
2972
+ : rowIndex.col !== undefined
2973
+ ? rowIndex.col
2974
+ : rowIndex.colIndex
2975
+ shpIdx =
2976
+ rowIndex.shapeIndex !== undefined
2977
+ ? rowIndex.shapeIndex
2978
+ : rowIndex.shape !== undefined
2979
+ ? rowIndex.shape
2980
+ : colIndex
2981
+ }
2982
+
2821
2983
  const targetIndices = this.#getTargetSlideIndices()
2822
2984
  for (const idx of targetIndices) {
2823
2985
  const shape = this.#tableManager.getCellShape(
2824
2986
  idx,
2825
2987
  tableId,
2826
- rowIndex,
2827
- colIndex,
2828
- shapeIndex,
2988
+ row,
2989
+ col,
2990
+ shpIdx,
2829
2991
  this.#slideManager,
2830
2992
  this.#shapeManager
2831
2993
  )
@@ -2848,16 +3010,22 @@ class PPTXTemplater {
2848
3010
  typeof tableIdOrObj === 'object'
2849
3011
  ? tableIdOrObj.id || tableIdOrObj.name || tableIdOrObj.tableId
2850
3012
  : tableIdOrObj
3013
+ let row = rowIndex
3014
+ let col = colIndex
3015
+ if (rowIndex && typeof rowIndex === 'object') {
3016
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
3017
+ col =
3018
+ rowIndex.column !== undefined
3019
+ ? rowIndex.column
3020
+ : rowIndex.col !== undefined
3021
+ ? rowIndex.col
3022
+ : rowIndex.colIndex
3023
+ }
3024
+
2851
3025
  const targetIndices = this.#getTargetSlideIndices()
2852
3026
  for (const idx of targetIndices) {
2853
3027
  try {
2854
- const bounds = this.#tableManager.getCellBounds(
2855
- idx,
2856
- tableId,
2857
- rowIndex,
2858
- colIndex,
2859
- this.#slideManager
2860
- )
3028
+ const bounds = this.#tableManager.getCellBounds(idx, tableId, row, col, this.#slideManager)
2861
3029
  if (bounds) return bounds
2862
3030
  } catch (err) {
2863
3031
  logger.debug(
@@ -2885,17 +3053,33 @@ class PPTXTemplater {
2885
3053
  typeof tableIdOrObj === 'object'
2886
3054
  ? tableIdOrObj.id || tableIdOrObj.name || tableIdOrObj.tableId
2887
3055
  : tableIdOrObj
3056
+ let row = rowIndex
3057
+ let col = colIndex
3058
+ let widthOrOpts = shapeWidthOrOptions
3059
+ let height = shapeHeight
3060
+ if (rowIndex && typeof rowIndex === 'object') {
3061
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
3062
+ col =
3063
+ rowIndex.column !== undefined
3064
+ ? rowIndex.column
3065
+ : rowIndex.col !== undefined
3066
+ ? rowIndex.col
3067
+ : rowIndex.colIndex
3068
+ widthOrOpts = colIndex
3069
+ height = shapeWidthOrOptions
3070
+ }
3071
+
2888
3072
  const targetIndices = this.#getTargetSlideIndices()
2889
3073
  for (const idx of targetIndices) {
2890
3074
  try {
2891
3075
  const pos = this.#tableManager.getCellPosition(
2892
3076
  idx,
2893
3077
  tableId,
2894
- rowIndex,
2895
- colIndex,
3078
+ row,
3079
+ col,
2896
3080
  this.#slideManager,
2897
- shapeWidthOrOptions,
2898
- shapeHeight
3081
+ widthOrOpts,
3082
+ height
2899
3083
  )
2900
3084
  if (pos) return pos
2901
3085
  } catch (err) {