node-pptx-templater 1.0.21 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -51,7 +51,7 @@ const {
51
51
  analyzeXmlFile,
52
52
  reportXmlComplexity,
53
53
  } = require('./utils/xmlUtils.js')
54
- const { createLogger } = require('./utils/logger.js')
54
+ const { createLogger, setGlobalLogLevel, resetLogLevel } = require('./utils/logger.js')
55
55
  const {
56
56
  PPTXError,
57
57
  SlideNotFoundError,
@@ -87,6 +87,8 @@ module.exports = {
87
87
  analyzeXmlFile,
88
88
  reportXmlComplexity,
89
89
  createLogger,
90
+ setGlobalLogLevel,
91
+ resetLogLevel,
90
92
  PPTXError,
91
93
  SlideNotFoundError,
92
94
  ChartNotFoundError,
@@ -845,13 +845,50 @@ class TableManager {
845
845
  }
846
846
  }
847
847
 
848
- getCellPosition(slideIndex, tableId, rowIndex, colIndex, slideManager) {
848
+ getCellPosition(
849
+ slideIndex,
850
+ tableId,
851
+ rowIndex,
852
+ colIndex,
853
+ slideManager,
854
+ shapeWidthOrOptions,
855
+ shapeHeight
856
+ ) {
849
857
  const bounds = this.getCellBounds(slideIndex, tableId, rowIndex, colIndex, slideManager)
858
+ if (!bounds) return null
859
+
860
+ let shapeWidth
861
+ let shapeHeightVal
862
+
863
+ if (shapeWidthOrOptions && typeof shapeWidthOrOptions === 'object') {
864
+ shapeWidth =
865
+ shapeWidthOrOptions.width !== undefined
866
+ ? shapeWidthOrOptions.width
867
+ : shapeWidthOrOptions.shapeWidth
868
+ shapeHeightVal =
869
+ shapeWidthOrOptions.height !== undefined
870
+ ? shapeWidthOrOptions.height
871
+ : shapeWidthOrOptions.shapeHeight
872
+ } else {
873
+ shapeWidth = shapeWidthOrOptions
874
+ shapeHeightVal = shapeHeight
875
+ }
876
+
877
+ let x = bounds.x
878
+ let y = bounds.y
879
+
880
+ if (shapeWidth !== undefined && shapeHeightVal !== undefined) {
881
+ x = Math.round(bounds.x + (bounds.width - shapeWidth) / 2)
882
+ y = Math.round(bounds.y + (bounds.height - shapeHeightVal) / 2)
883
+ }
884
+
850
885
  return {
851
886
  row: rowIndex,
852
887
  column: colIndex,
853
- x: bounds.x,
854
- y: bounds.y,
888
+ x,
889
+ y,
890
+ width: bounds.width,
891
+ height: bounds.height,
855
892
  }
856
893
  }
857
894
 
@@ -1343,9 +1380,28 @@ class TableManager {
1343
1380
  }
1344
1381
  }
1345
1382
 
1383
+ // Scale shape down proportionally to fit inside the cell if it exceeds the cell dimensions
1384
+ if (shapeWidth > cellWidth_px || shapeHeight > cellHeight_px) {
1385
+ logger.warn(
1386
+ `Shape width (${shapeWidth}px) or height (${shapeHeight}px) exceeds cell dimensions (${cellWidth_px}px x ${cellHeight_px}px). Scaling shape to fit.`
1387
+ )
1388
+ const scale = Math.min(cellWidth_px / shapeWidth, cellHeight_px / shapeHeight)
1389
+ shapeWidth = Math.max(1, Math.floor(shapeWidth * scale))
1390
+ shapeHeight = Math.max(1, Math.floor(shapeHeight * scale))
1391
+ }
1392
+
1346
1393
  // 2. Determine alignment settings
1347
- let alignX = config.alignX
1348
- let alignY = config.alignY
1394
+ let alignX = config.alignX || config.horizontal
1395
+ let alignY = config.alignY || config.vertical
1396
+
1397
+ if (alignX) {
1398
+ alignX = String(alignX).toLowerCase()
1399
+ if (alignX === 'middle') alignX = 'center'
1400
+ }
1401
+ if (alignY) {
1402
+ alignY = String(alignY).toLowerCase()
1403
+ if (alignY === 'center') alignY = 'middle'
1404
+ }
1349
1405
 
1350
1406
  if (config.position) {
1351
1407
  switch (config.position) {
@@ -1436,7 +1492,13 @@ class TableManager {
1436
1492
 
1437
1493
  // 4. Boundary Constraints Validation/Enforcement
1438
1494
  if (shapeWidth > cellWidth_px) {
1439
- shapeLeft = cellLeft_px
1495
+ if (alignX === 'center') {
1496
+ shapeLeft = cellLeft_px + (cellWidth_px - shapeWidth) / 2
1497
+ } else if (alignX === 'right') {
1498
+ shapeLeft = cellLeft_px + cellWidth_px - shapeWidth
1499
+ } else {
1500
+ shapeLeft = cellLeft_px
1501
+ }
1440
1502
  } else {
1441
1503
  shapeLeft = Math.max(
1442
1504
  cellLeft_px,
@@ -1445,7 +1507,13 @@ class TableManager {
1445
1507
  }
1446
1508
 
1447
1509
  if (shapeHeight > cellHeight_px) {
1448
- shapeTop = cellTop_px
1510
+ if (alignY === 'middle') {
1511
+ shapeTop = cellTop_px + (cellHeight_px - shapeHeight) / 2
1512
+ } else if (alignY === 'bottom') {
1513
+ shapeTop = cellTop_px + cellHeight_px - shapeHeight
1514
+ } else {
1515
+ shapeTop = cellTop_px
1516
+ }
1449
1517
  } else {
1450
1518
  shapeTop = Math.max(
1451
1519
  cellTop_px,
@@ -1695,8 +1763,6 @@ class TableManager {
1695
1763
  }
1696
1764
  }
1697
1765
 
1698
- this.#calculateRowHeights(slideIndex, tableId, slideManager, tblObj)
1699
-
1700
1766
  const xfrm = frameObj['p:xfrm']
1701
1767
  const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
1702
1768
  const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
@@ -2103,8 +2169,11 @@ class TableManager {
2103
2169
  const numRows = trsArr.length
2104
2170
  const numCols = colWidths.length
2105
2171
 
2106
- // Initialize rowHeights with original height or 0
2107
- const rowHeights = trsArr.map(row => parseInt(row['@_h'] || 0, 10))
2172
+ // Initialize rowHeights with original height or a safe minimum floor of 228600 EMUs (~24px/pt)
2173
+ const rowHeights = trsArr.map(row => {
2174
+ const h = parseInt(row['@_h'] || 0, 10)
2175
+ return Math.max(h, 228600)
2176
+ })
2108
2177
 
2109
2178
  // Helper to get paragraph font size
2110
2179
  const getParagraphFontSize = p => {
@@ -2226,7 +2295,7 @@ class TableManager {
2226
2295
  }
2227
2296
 
2228
2297
  const totalCellHeight_emu = marT + marB + textHeight_emu
2229
- cellHeights[r][c] = totalCellHeight_emu
2298
+ cellHeights[r][c] = Math.max(totalCellHeight_emu, 228600)
2230
2299
  }
2231
2300
  }
2232
2301
 
@@ -2,10 +2,11 @@
2
2
  * @fileoverview Logger utility - lightweight structured logging.
3
3
  *
4
4
  * Provides contextual logging with module names.
5
- * Respects the PPTX_LOG_LEVEL environment variable.
5
+ * Respects the PPTX_LOG_LEVEL environment variable, or can be
6
+ * configured at runtime via setGlobalLogLevel().
6
7
  *
7
8
  * Log levels (lowest to highest severity):
8
- * debug → info → warn → error
9
+ * verbose → debug → info → warn → error → silent
9
10
  *
10
11
  * Usage:
11
12
  * const logger = createLogger('MyModule');
@@ -14,21 +15,67 @@
14
15
  * logger.warn('Watch out');
15
16
  * logger.error('Something failed');
16
17
  *
17
- * Environment:
18
- * PPTX_LOG_LEVEL=debug → show all logs
19
- * PPTX_LOG_LEVEL=info → show info, warn, error (default)
20
- * PPTX_LOG_LEVEL=warn → show warn and error only
21
- * PPTX_LOG_LEVEL=error → show only errors
22
- * PPTX_LOG_LEVEL=silent → suppress all logs
18
+ * Environment variable (set before process starts):
19
+ * PPTX_LOG_LEVEL=debug → show debug, info, warn, error
20
+ * PPTX_LOG_LEVEL=info → show info, warn, error
21
+ * PPTX_LOG_LEVEL=warn → show warn and error only (default)
22
+ * PPTX_LOG_LEVEL=error → show only errors
23
+ * PPTX_LOG_LEVEL=silent → suppress all output
24
+ *
25
+ * Runtime control (overrides env var):
26
+ * const { setGlobalLogLevel } = require('./logger');
27
+ * setGlobalLogLevel('debug');
23
28
  */
24
29
 
25
- const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 }
30
+ /** @type {Object.<string, number>} */
31
+ const LOG_LEVELS = { verbose: -1, debug: 0, info: 1, warn: 2, error: 3, silent: 4 }
32
+
33
+ /** @type {number} Initial level from environment variable */
34
+ const envLevel = LOG_LEVELS[(process.env.PPTX_LOG_LEVEL || 'warn').toLowerCase()] ?? LOG_LEVELS.warn
35
+
36
+ /** @type {number|null} Runtime override — null means use envLevel */
37
+ let runtimeLevel = null
26
38
 
27
- const currentLevel =
28
- LOG_LEVELS[(process.env.PPTX_LOG_LEVEL || 'warn').toLowerCase()] ?? LOG_LEVELS.warn
39
+ /**
40
+ * Gets the current effective log level.
41
+ * @returns {number}
42
+ */
43
+ function getEffectiveLevel() {
44
+ return runtimeLevel !== null ? runtimeLevel : envLevel
45
+ }
46
+
47
+ /**
48
+ * Sets the global log level at runtime, overriding the environment variable.
49
+ * This affects all logger instances immediately.
50
+ *
51
+ * @param {string} level - One of: 'verbose', 'debug', 'info', 'warn', 'error', 'silent'
52
+ * @throws {Error} If an invalid level is provided.
53
+ *
54
+ * @example
55
+ * const { setGlobalLogLevel } = require('node-pptx-templater');
56
+ * setGlobalLogLevel('debug'); // Enable verbose output
57
+ * setGlobalLogLevel('silent'); // Suppress everything
58
+ */
59
+ function setGlobalLogLevel(level) {
60
+ const normalized = String(level).toLowerCase()
61
+ if (LOG_LEVELS[normalized] === undefined) {
62
+ throw new Error(
63
+ `Invalid log level: "${level}". Valid levels: verbose, debug, info, warn, error, silent`
64
+ )
65
+ }
66
+ runtimeLevel = LOG_LEVELS[normalized]
67
+ }
68
+
69
+ /**
70
+ * Resets the log level back to the environment variable default.
71
+ */
72
+ function resetLogLevel() {
73
+ runtimeLevel = null
74
+ }
29
75
 
30
76
  /**
31
77
  * ANSI color codes for terminal output.
78
+ * @private
32
79
  */
33
80
  const COLORS = {
34
81
  reset: '\x1b[0m',
@@ -37,6 +84,7 @@ const COLORS = {
37
84
  green: '\x1b[32m',
38
85
  yellow: '\x1b[33m',
39
86
  red: '\x1b[31m',
87
+ magenta: '\x1b[35m',
40
88
  }
41
89
 
42
90
  /**
@@ -49,6 +97,7 @@ function timestamp() {
49
97
 
50
98
  /**
51
99
  * @typedef {Object} Logger
100
+ * @property {Function} verbose - Log verbose message (most detailed).
52
101
  * @property {Function} debug - Log debug message.
53
102
  * @property {Function} info - Log info message.
54
103
  * @property {Function} warn - Log warning.
@@ -64,16 +113,17 @@ function timestamp() {
64
113
  * @example
65
114
  * const logger = createLogger('SlideManager');
66
115
  * logger.info('Loaded 5 slides');
116
+ * logger.debug('Processing slide XML...');
67
117
  */
68
118
  function createLogger(moduleName) {
69
119
  const isTTY = process.stdout.isTTY
70
120
 
71
121
  const log = (level, levelNum, color, message, ...args) => {
72
- if (levelNum < currentLevel) return
122
+ if (levelNum < getEffectiveLevel()) return
73
123
 
74
124
  const prefix = isTTY
75
- ? `${COLORS.dim}${timestamp()}${COLORS.reset} ${color}[${level.toUpperCase().padEnd(5)}]${COLORS.reset} ${COLORS.cyan}[${moduleName}]${COLORS.reset}`
76
- : `${timestamp()} [${level.toUpperCase().padEnd(5)}] [${moduleName}]`
125
+ ? `${COLORS.dim}${timestamp()}${COLORS.reset} ${color}[${level.toUpperCase().padEnd(7)}]${COLORS.reset} ${COLORS.cyan}[${moduleName}]${COLORS.reset}`
126
+ : `${timestamp()} [${level.toUpperCase().padEnd(7)}] [${moduleName}]`
77
127
 
78
128
  const output =
79
129
  args.length > 0
@@ -85,7 +135,15 @@ function createLogger(moduleName) {
85
135
 
86
136
  return {
87
137
  /**
88
- * Logs a debug-level message. Only shown when PPTX_LOG_LEVEL=debug.
138
+ * Logs a verbose-level message. Only shown when level=verbose.
139
+ * @param {string} message
140
+ * @param {...*} args
141
+ */
142
+ verbose: (message, ...args) =>
143
+ log('verbose', LOG_LEVELS.verbose, COLORS.magenta, message, ...args),
144
+
145
+ /**
146
+ * Logs a debug-level message. Only shown when level=debug or lower.
89
147
  * @param {string} message
90
148
  * @param {...*} args
91
149
  */
@@ -116,4 +174,7 @@ function createLogger(moduleName) {
116
174
 
117
175
  module.exports = {
118
176
  createLogger,
177
+ setGlobalLogLevel,
178
+ resetLogLevel,
179
+ LOG_LEVELS,
119
180
  }