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/CHANGELOG.md +39 -0
- package/README.md +169 -2230
- package/package.json +12 -2
- package/src/core/PPTXTemplater.js +992 -42
- package/src/index.js +3 -1
- package/src/managers/TableManager.js +81 -12
- package/src/utils/logger.js +76 -15
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(
|
|
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
|
|
854
|
-
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
|
-
|
|
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
|
-
|
|
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
|
|
2107
|
-
const rowHeights = trsArr.map(row =>
|
|
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
|
|
package/src/utils/logger.js
CHANGED
|
@@ -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
|
|
19
|
-
* PPTX_LOG_LEVEL=info
|
|
20
|
-
* PPTX_LOG_LEVEL=warn
|
|
21
|
-
* PPTX_LOG_LEVEL=error
|
|
22
|
-
* PPTX_LOG_LEVEL=silent
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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 <
|
|
122
|
+
if (levelNum < getEffectiveLevel()) return
|
|
73
123
|
|
|
74
124
|
const prefix = isTTY
|
|
75
|
-
? `${COLORS.dim}${timestamp()}${COLORS.reset} ${color}[${level.toUpperCase().padEnd(
|
|
76
|
-
: `${timestamp()} [${level.toUpperCase().padEnd(
|
|
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
|
|
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
|
}
|