pptx2js 0.4.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/README.html +804 -0
- package/README.md +201 -0
- package/bin/pptx2js.js +75 -0
- package/lib/chart.js +245 -0
- package/lib/codegen.js +172 -0
- package/lib/convert.js +195 -0
- package/lib/extractor.js +394 -0
- package/lib/graphic.js +63 -0
- package/lib/index.js +7 -0
- package/lib/mapper.js +98 -0
- package/lib/packager.js +72 -0
- package/lib/placeholder.js +258 -0
- package/lib/presentation.js +70 -0
- package/lib/rels.js +117 -0
- package/lib/smartart.js +96 -0
- package/lib/table.js +138 -0
- package/lib/unpacker.js +40 -0
- package/lib/utils/bounds.js +34 -0
- package/lib/utils/color.js +128 -0
- package/lib/utils/emu.js +20 -0
- package/lib/xml-parser.js +25 -0
- package/lib/xml-utils.js +68 -0
- package/package.json +41 -0
package/lib/table.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 表格提取(design.html §4.5)
|
|
3
|
+
*/
|
|
4
|
+
const { asArray, attr, child, textContent } = require('./xml-utils');
|
|
5
|
+
const { getGraphicXfrm } = require('./graphic');
|
|
6
|
+
const { boundsFromXfrm } = require('./utils/bounds');
|
|
7
|
+
const { resolveFillColor } = require('./utils/color');
|
|
8
|
+
const { emuToInch } = require('./utils/emu');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} graphicFrame
|
|
12
|
+
* @param {object} ctx
|
|
13
|
+
* @returns {import('./extractor').SlideEntity|null}
|
|
14
|
+
*/
|
|
15
|
+
function extractTable(graphicFrame, ctx) {
|
|
16
|
+
const bounds = boundsFromXfrm(getGraphicXfrm(graphicFrame), ctx.offset);
|
|
17
|
+
const graphicData = child(child(graphicFrame, 'a:graphic'), 'a:graphicData');
|
|
18
|
+
const tbl = child(graphicData, 'a:tbl');
|
|
19
|
+
|
|
20
|
+
if (!tbl) {
|
|
21
|
+
return {
|
|
22
|
+
slideIndex: ctx.slideIndex,
|
|
23
|
+
slidePath: ctx.slidePath,
|
|
24
|
+
decision: 'SKIP',
|
|
25
|
+
kind: 'skip',
|
|
26
|
+
bounds,
|
|
27
|
+
skipReason: '表格数据无法解析(无内联 a:tbl)',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rows = extractTableRows(tbl, ctx.scheme);
|
|
32
|
+
if (rows.length === 0) return null;
|
|
33
|
+
|
|
34
|
+
const colWidths = extractColWidths(tbl);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
slideIndex: ctx.slideIndex,
|
|
38
|
+
slidePath: ctx.slidePath,
|
|
39
|
+
decision: 'FULL',
|
|
40
|
+
kind: 'table',
|
|
41
|
+
bounds,
|
|
42
|
+
table: { rows, colWidths },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {object} tbl
|
|
48
|
+
* @param {Record<string, string>} scheme
|
|
49
|
+
*/
|
|
50
|
+
function extractTableRows(tbl, scheme) {
|
|
51
|
+
const rows = [];
|
|
52
|
+
for (const tr of asArray(tbl['a:tr'])) {
|
|
53
|
+
const cells = [];
|
|
54
|
+
for (const tc of asArray(tr['a:tc'])) {
|
|
55
|
+
const text = extractCellText(tc);
|
|
56
|
+
const tcPr = child(tc, 'a:tcPr');
|
|
57
|
+
const fill = tcPr ? resolveFillColor(tcPr, scheme).color : null;
|
|
58
|
+
const merge = {};
|
|
59
|
+
const rowSpan = attr(tcPr, 'rowSpan');
|
|
60
|
+
const gridSpan = attr(tcPr, 'gridSpan');
|
|
61
|
+
if (rowSpan && rowSpan !== '1') merge.rowspan = parseInt(rowSpan, 10);
|
|
62
|
+
if (gridSpan && gridSpan !== '1') merge.colspan = parseInt(gridSpan, 10);
|
|
63
|
+
|
|
64
|
+
const cell = { text, options: {} };
|
|
65
|
+
if (fill) cell.options.fill = { color: fill };
|
|
66
|
+
if (Object.keys(merge).length) Object.assign(cell.options, merge);
|
|
67
|
+
|
|
68
|
+
const border = extractCellBorder(tcPr, scheme);
|
|
69
|
+
if (border) cell.options.border = border;
|
|
70
|
+
|
|
71
|
+
cells.push(cell);
|
|
72
|
+
}
|
|
73
|
+
if (cells.length) rows.push(cells);
|
|
74
|
+
}
|
|
75
|
+
return rows;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {object|null|undefined} tcPr
|
|
80
|
+
* @param {Record<string, string>} scheme
|
|
81
|
+
*/
|
|
82
|
+
function extractCellBorder(tcPr, scheme) {
|
|
83
|
+
if (!tcPr) return null;
|
|
84
|
+
const border = {};
|
|
85
|
+
const sides = {
|
|
86
|
+
left: 'a:lnL',
|
|
87
|
+
right: 'a:lnR',
|
|
88
|
+
top: 'a:lnT',
|
|
89
|
+
bottom: 'a:lnB',
|
|
90
|
+
};
|
|
91
|
+
let hasAny = false;
|
|
92
|
+
|
|
93
|
+
for (const [side, key] of Object.entries(sides)) {
|
|
94
|
+
const ln = child(tcPr, key);
|
|
95
|
+
if (!ln) continue;
|
|
96
|
+
const { color } = resolveFillColor(ln, scheme);
|
|
97
|
+
const w = parseInt(attr(ln, 'w') ?? '0', 10);
|
|
98
|
+
if (color || w) {
|
|
99
|
+
border[side] = {
|
|
100
|
+
...(color && { color }),
|
|
101
|
+
...(w > 0 && { pt: Math.round(w / 12700) }),
|
|
102
|
+
};
|
|
103
|
+
hasAny = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return hasAny ? border : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {object} tc
|
|
111
|
+
*/
|
|
112
|
+
function extractCellText(tc) {
|
|
113
|
+
const txBody = child(tc, 'a:txBody');
|
|
114
|
+
if (!txBody) return '';
|
|
115
|
+
const parts = [];
|
|
116
|
+
for (const p of asArray(txBody['a:p'])) {
|
|
117
|
+
for (const r of asArray(p['a:r'])) {
|
|
118
|
+
parts.push(textContent(r['a:t']));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return parts.join('');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {object} tbl
|
|
126
|
+
* @returns {number[]|undefined}
|
|
127
|
+
*/
|
|
128
|
+
function extractColWidths(tbl) {
|
|
129
|
+
const grid = child(tbl, 'a:tblGrid');
|
|
130
|
+
const cols = asArray(child(grid, 'a:gridCol'));
|
|
131
|
+
if (!cols.length) return undefined;
|
|
132
|
+
return cols.map((col) => {
|
|
133
|
+
const w = parseInt(attr(col, 'w') ?? '0', 10);
|
|
134
|
+
return Math.round(emuToInch(w) * 1000) / 1000;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { extractTable };
|
package/lib/unpacker.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ① 解压与索引
|
|
3
|
+
* JSZip 解压 PPTX,建立文件清单。
|
|
4
|
+
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const JSZip = require('jszip');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} PptxArchive
|
|
10
|
+
* @property {JSZip} zip
|
|
11
|
+
* @property {Map<string, string>} files - 路径 → 文本内容或 __binary__
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} filePath
|
|
16
|
+
* @returns {Promise<PptxArchive>}
|
|
17
|
+
*/
|
|
18
|
+
async function unpack(filePath) {
|
|
19
|
+
const buffer = fs.readFileSync(filePath);
|
|
20
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
21
|
+
|
|
22
|
+
/** @type {Map<string, string>} */
|
|
23
|
+
const files = new Map();
|
|
24
|
+
for (const [entryPath, entry] of Object.entries(zip.files)) {
|
|
25
|
+
if (entry.dir) continue;
|
|
26
|
+
const isText =
|
|
27
|
+
entryPath.endsWith('.xml') ||
|
|
28
|
+
entryPath.endsWith('.rels') ||
|
|
29
|
+
entryPath.includes('[Content_Types]');
|
|
30
|
+
if (isText) {
|
|
31
|
+
files.set(entryPath, await entry.async('string'));
|
|
32
|
+
} else {
|
|
33
|
+
files.set(entryPath, '__binary__');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { zip, files };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { unpack };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 形状坐标:a:xfrm → 英寸边界
|
|
3
|
+
*/
|
|
4
|
+
const { attr, child } = require('../xml-utils');
|
|
5
|
+
const { emuToInch } = require('./emu');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {object|null|undefined} xfrm
|
|
9
|
+
* @param {{ x: number, y: number }} offset EMU
|
|
10
|
+
* @returns {{ x: number, y: number, w: number, h: number }}
|
|
11
|
+
*/
|
|
12
|
+
function boundsFromXfrm(xfrm, offset = { x: 0, y: 0 }) {
|
|
13
|
+
const off = child(xfrm, 'a:off');
|
|
14
|
+
const ext = child(xfrm, 'a:ext');
|
|
15
|
+
const x = parseInt(attr(off, 'x') ?? '0', 10) + offset.x;
|
|
16
|
+
const y = parseInt(attr(off, 'y') ?? '0', 10) + offset.y;
|
|
17
|
+
const cx = parseInt(attr(ext, 'cx') ?? '0', 10);
|
|
18
|
+
const cy = parseInt(attr(ext, 'cy') ?? '0', 10);
|
|
19
|
+
return {
|
|
20
|
+
x: round3(emuToInch(x)),
|
|
21
|
+
y: round3(emuToInch(y)),
|
|
22
|
+
w: round3(emuToInch(cx)),
|
|
23
|
+
h: round3(emuToInch(cy)),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {number} n
|
|
29
|
+
*/
|
|
30
|
+
function round3(n) {
|
|
31
|
+
return Math.round(n * 1000) / 1000;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { boundsFromXfrm };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 颜色规范化(srgbClr、schemeClr、渐变首色标)
|
|
3
|
+
*/
|
|
4
|
+
const { asArray, attr, child } = require('../xml-utils');
|
|
5
|
+
|
|
6
|
+
/** @type {Record<string, string>} */
|
|
7
|
+
const DEFAULT_SCHEME = {
|
|
8
|
+
dk1: '000000',
|
|
9
|
+
lt1: 'FFFFFF',
|
|
10
|
+
dk2: '44546A',
|
|
11
|
+
lt2: 'E7E6E6',
|
|
12
|
+
accent1: '4472C4',
|
|
13
|
+
accent2: 'ED7D31',
|
|
14
|
+
accent3: 'A5A5A5',
|
|
15
|
+
accent4: 'FFC000',
|
|
16
|
+
accent5: '5B9BD5',
|
|
17
|
+
accent6: '70AD47',
|
|
18
|
+
hlink: '0563C1',
|
|
19
|
+
folHlink: '954F72',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Record<string, object>|null} parsed
|
|
24
|
+
* @param {string|null} themePath
|
|
25
|
+
* @returns {Record<string, string>}
|
|
26
|
+
*/
|
|
27
|
+
function loadColorScheme(parsed, themePath) {
|
|
28
|
+
if (!themePath || !parsed[themePath]) return { ...DEFAULT_SCHEME };
|
|
29
|
+
|
|
30
|
+
const theme = child(parsed[themePath], 'a:theme');
|
|
31
|
+
const clrScheme = child(child(theme, 'a:themeElements'), 'a:clrScheme');
|
|
32
|
+
if (!clrScheme) return { ...DEFAULT_SCHEME };
|
|
33
|
+
|
|
34
|
+
const scheme = { ...DEFAULT_SCHEME };
|
|
35
|
+
for (const name of Object.keys(DEFAULT_SCHEME)) {
|
|
36
|
+
const slot = child(clrScheme, `a:${name}`);
|
|
37
|
+
const rgb = extractRgbFromColorNode(slot);
|
|
38
|
+
if (rgb) scheme[name] = rgb;
|
|
39
|
+
}
|
|
40
|
+
return scheme;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {object|null|undefined} colorNode a:srgbClr / a:schemeClr 的父级
|
|
45
|
+
* @returns {string|null}
|
|
46
|
+
*/
|
|
47
|
+
function extractRgbFromColorNode(colorNode) {
|
|
48
|
+
if (!colorNode) return null;
|
|
49
|
+
|
|
50
|
+
const srgb = child(colorNode, 'a:srgbClr');
|
|
51
|
+
if (srgb) {
|
|
52
|
+
const val = attr(srgb, 'val');
|
|
53
|
+
return val ? val.toUpperCase() : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sys = child(colorNode, 'a:sysClr');
|
|
57
|
+
if (sys) {
|
|
58
|
+
const last = attr(sys, 'lastClr');
|
|
59
|
+
return last ? last.toUpperCase() : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {object|null|undefined} fillNode a:solidFill | a:gradFill 等
|
|
67
|
+
* @param {Record<string, string>} scheme
|
|
68
|
+
* @returns {{ color: string|null, degraded: boolean }}
|
|
69
|
+
*/
|
|
70
|
+
function resolveFillColor(fillNode, scheme) {
|
|
71
|
+
if (!fillNode) return { color: null, degraded: false };
|
|
72
|
+
|
|
73
|
+
const solid = child(fillNode, 'a:solidFill');
|
|
74
|
+
if (solid) {
|
|
75
|
+
return { color: resolveColorFromContainer(solid, scheme), degraded: false };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const grad = child(fillNode, 'a:gradFill');
|
|
79
|
+
if (grad) {
|
|
80
|
+
const gs = asArray(child(child(grad, 'a:gsLst'), 'a:gs'))[0];
|
|
81
|
+
return {
|
|
82
|
+
color: gs ? resolveColorFromContainer(gs, scheme) : null,
|
|
83
|
+
degraded: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { color: null, degraded: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {object|null|undefined} container 含 a:srgbClr / a:schemeClr
|
|
92
|
+
* @param {Record<string, string>} scheme
|
|
93
|
+
* @returns {string|null}
|
|
94
|
+
*/
|
|
95
|
+
function resolveColorFromContainer(container, scheme) {
|
|
96
|
+
if (!container) return null;
|
|
97
|
+
|
|
98
|
+
const srgb = child(container, 'a:srgbClr');
|
|
99
|
+
if (srgb) {
|
|
100
|
+
const val = attr(srgb, 'val');
|
|
101
|
+
return val ? val.toUpperCase() : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const schemeClr = child(container, 'a:schemeClr');
|
|
105
|
+
if (schemeClr) {
|
|
106
|
+
const name = attr(schemeClr, 'val');
|
|
107
|
+
return name ? scheme[name] ?? DEFAULT_SCHEME[name] ?? null : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return extractRgbFromColorNode(container);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {Record<string, string>} scheme
|
|
115
|
+
* @param {object|null|undefined} clrNode
|
|
116
|
+
* @returns {string|null}
|
|
117
|
+
*/
|
|
118
|
+
function resolveColor(scheme, clrNode) {
|
|
119
|
+
return resolveColorFromContainer(clrNode, scheme);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
DEFAULT_SCHEME,
|
|
124
|
+
loadColorScheme,
|
|
125
|
+
resolveFillColor,
|
|
126
|
+
resolveColor,
|
|
127
|
+
resolveColorFromContainer,
|
|
128
|
+
};
|
package/lib/utils/emu.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** EMU(English Metric Units)与英寸换算,1 英寸 = 914400 EMU */
|
|
2
|
+
const EMU_PER_INCH = 914400;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {number} emu
|
|
6
|
+
* @returns {number}
|
|
7
|
+
*/
|
|
8
|
+
function emuToInch(emu) {
|
|
9
|
+
return emu / EMU_PER_INCH;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {number} inch
|
|
14
|
+
* @returns {number}
|
|
15
|
+
*/
|
|
16
|
+
function inchToEmu(inch) {
|
|
17
|
+
return inch * EMU_PER_INCH;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { EMU_PER_INCH, emuToInch, inchToEmu };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 项目唯一导出的 XML 解析器实例。
|
|
3
|
+
* 全项目必须使用此入口,禁止在模块内自行实例化解析器。
|
|
4
|
+
* @see design.html §4.6
|
|
5
|
+
*/
|
|
6
|
+
const xml2js = require('xml2js');
|
|
7
|
+
|
|
8
|
+
const PARSER_OPTIONS = {
|
|
9
|
+
explicitArray: false,
|
|
10
|
+
mergeAttrs: false,
|
|
11
|
+
explicitCharkey: false,
|
|
12
|
+
tagNameProcessors: [],
|
|
13
|
+
attrNameProcessors: [],
|
|
14
|
+
xmlns: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} str
|
|
19
|
+
* @returns {Promise<object>}
|
|
20
|
+
*/
|
|
21
|
+
function parseXml(str) {
|
|
22
|
+
return xml2js.parseStringPromise(str, PARSER_OPTIONS);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { parseXml, PARSER_OPTIONS };
|
package/lib/xml-utils.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xml2js 对象树访问工具(配合 lib/xml-parser.js 统一配置)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {*} value
|
|
7
|
+
* @returns {Array}
|
|
8
|
+
*/
|
|
9
|
+
function asArray(value) {
|
|
10
|
+
if (value == null) return [];
|
|
11
|
+
return Array.isArray(value) ? value : [value];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {object|null|undefined} node
|
|
16
|
+
* @returns {Record<string, string>}
|
|
17
|
+
*/
|
|
18
|
+
function attrs(node) {
|
|
19
|
+
if (node == null) return {};
|
|
20
|
+
if (node.$) return node.$;
|
|
21
|
+
return node;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {object|null|undefined} node
|
|
26
|
+
* @param {string} name
|
|
27
|
+
* @returns {string|undefined}
|
|
28
|
+
*/
|
|
29
|
+
function attr(node, name) {
|
|
30
|
+
return attrs(node)[name];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {object|null|undefined} node
|
|
35
|
+
* @param {string} key
|
|
36
|
+
* @returns {*}
|
|
37
|
+
*/
|
|
38
|
+
function child(node, key) {
|
|
39
|
+
if (node == null) return undefined;
|
|
40
|
+
return node[key];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {object|null|undefined} node
|
|
45
|
+
* @param {string[]} keys
|
|
46
|
+
* @returns {*}
|
|
47
|
+
*/
|
|
48
|
+
function firstChild(node, keys) {
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
const v = child(node, key);
|
|
51
|
+
if (v != null) return v;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {object|null|undefined} node
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
function textContent(node) {
|
|
61
|
+
if (node == null) return '';
|
|
62
|
+
if (typeof node === 'string') return node;
|
|
63
|
+
if (typeof node === 'number') return String(node);
|
|
64
|
+
if (node._ != null) return String(node._);
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { asArray, attrs, attr, child, firstChild, textContent };
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pptx2js",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "将 .pptx 文件转换为可运行的 PptxGenJS 生成脚本",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pptx2js": "bin/pptx2js.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"test:watch": "jest --watch"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pptx",
|
|
15
|
+
"pptxgenjs",
|
|
16
|
+
"converter",
|
|
17
|
+
"powerpoint",
|
|
18
|
+
"presentation",
|
|
19
|
+
"nodejs",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"author": "yuese12333",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"chalk": "^4.1.2",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"jszip": "^3.10.1",
|
|
31
|
+
"xml2js": "^0.6.2"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"jest": "^29.7.0"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"lib",
|
|
38
|
+
"bin",
|
|
39
|
+
"README.md"
|
|
40
|
+
]
|
|
41
|
+
}
|