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/README.html
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>pptx2js · 项目说明</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=JetBrains+Mono:wght@400;500&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
:root {
|
|
11
|
+
--ink: #1a1a1a;
|
|
12
|
+
--ink-light: #444;
|
|
13
|
+
--ink-faint: #888;
|
|
14
|
+
--paper: #f7f4ef;
|
|
15
|
+
--paper-warm: #efe9df;
|
|
16
|
+
--accent: #c0392b;
|
|
17
|
+
--accent-dim: #e8b4b0;
|
|
18
|
+
--accent-bg: #fdf0ef;
|
|
19
|
+
--full: #1a6b3c;
|
|
20
|
+
--full-bg: #eaf5ee;
|
|
21
|
+
--degrade: #8b5e00;
|
|
22
|
+
--degrade-bg: #fdf6e3;
|
|
23
|
+
--skip: #555;
|
|
24
|
+
--skip-bg: #f0f0f0;
|
|
25
|
+
--border: #d6cfc4;
|
|
26
|
+
--mono: 'JetBrains Mono', monospace;
|
|
27
|
+
--serif: 'Noto Serif SC', serif;
|
|
28
|
+
--sans: 'Noto Sans SC', sans-serif;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
32
|
+
html { scroll-behavior: smooth; }
|
|
33
|
+
|
|
34
|
+
body {
|
|
35
|
+
background: var(--paper);
|
|
36
|
+
color: var(--ink);
|
|
37
|
+
font-family: var(--sans);
|
|
38
|
+
font-size: 15px;
|
|
39
|
+
line-height: 1.9;
|
|
40
|
+
font-weight: 300;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
nav {
|
|
44
|
+
position: fixed;
|
|
45
|
+
top: 0; left: 0;
|
|
46
|
+
width: 220px;
|
|
47
|
+
height: 100vh;
|
|
48
|
+
background: var(--ink);
|
|
49
|
+
color: #ccc;
|
|
50
|
+
overflow-y: auto;
|
|
51
|
+
z-index: 100;
|
|
52
|
+
padding: 32px 0 48px;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
nav .nav-logo {
|
|
58
|
+
font-family: var(--mono);
|
|
59
|
+
font-size: 11px;
|
|
60
|
+
color: var(--accent-dim);
|
|
61
|
+
letter-spacing: 0.08em;
|
|
62
|
+
padding: 0 20px 28px;
|
|
63
|
+
border-bottom: 1px solid #333;
|
|
64
|
+
line-height: 1.6;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
nav ul { list-style: none; padding: 20px 0; flex: 1; }
|
|
68
|
+
|
|
69
|
+
nav ul li a {
|
|
70
|
+
display: block;
|
|
71
|
+
padding: 6px 20px;
|
|
72
|
+
color: #999;
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
font-size: 12.5px;
|
|
75
|
+
transition: color 0.2s, background 0.2s;
|
|
76
|
+
border-left: 2px solid transparent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
nav ul li a:hover,
|
|
80
|
+
nav ul li a.active {
|
|
81
|
+
color: #fff;
|
|
82
|
+
background: #2a2a2a;
|
|
83
|
+
border-left-color: var(--accent);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
nav ul li.section-label {
|
|
87
|
+
font-family: var(--mono);
|
|
88
|
+
font-size: 10px;
|
|
89
|
+
color: #555;
|
|
90
|
+
letter-spacing: 0.12em;
|
|
91
|
+
text-transform: uppercase;
|
|
92
|
+
padding: 16px 20px 4px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
nav .nav-footer {
|
|
96
|
+
padding: 16px 20px 0;
|
|
97
|
+
border-top: 1px solid #333;
|
|
98
|
+
font-size: 11px;
|
|
99
|
+
color: #666;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
nav .nav-footer a { color: var(--accent-dim); text-decoration: none; }
|
|
103
|
+
nav .nav-footer a:hover { color: #fff; }
|
|
104
|
+
|
|
105
|
+
main { margin-left: 220px; min-height: 100vh; }
|
|
106
|
+
|
|
107
|
+
.hero {
|
|
108
|
+
background: var(--ink);
|
|
109
|
+
color: var(--paper);
|
|
110
|
+
padding: 72px 64px 64px;
|
|
111
|
+
position: relative;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.hero::before {
|
|
116
|
+
content: '';
|
|
117
|
+
position: absolute;
|
|
118
|
+
top: -60px; right: -60px;
|
|
119
|
+
width: 400px; height: 400px;
|
|
120
|
+
border-radius: 50%;
|
|
121
|
+
background: radial-gradient(circle, rgba(192,57,43,0.18) 0%, transparent 70%);
|
|
122
|
+
pointer-events: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hero-label {
|
|
126
|
+
font-family: var(--mono);
|
|
127
|
+
font-size: 11px;
|
|
128
|
+
letter-spacing: 0.15em;
|
|
129
|
+
color: var(--accent-dim);
|
|
130
|
+
text-transform: uppercase;
|
|
131
|
+
margin-bottom: 16px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.hero h1 {
|
|
135
|
+
font-family: var(--serif);
|
|
136
|
+
font-size: 40px;
|
|
137
|
+
font-weight: 700;
|
|
138
|
+
line-height: 1.25;
|
|
139
|
+
margin-bottom: 14px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.hero-desc {
|
|
143
|
+
font-size: 14px;
|
|
144
|
+
color: #aaa;
|
|
145
|
+
max-width: 560px;
|
|
146
|
+
line-height: 1.8;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.hero-desc a { color: var(--accent-dim); }
|
|
150
|
+
|
|
151
|
+
.hero-meta {
|
|
152
|
+
margin-top: 32px;
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-wrap: wrap;
|
|
155
|
+
gap: 28px;
|
|
156
|
+
font-family: var(--mono);
|
|
157
|
+
font-size: 11px;
|
|
158
|
+
color: #666;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.hero-meta span strong {
|
|
162
|
+
color: #bbb;
|
|
163
|
+
display: block;
|
|
164
|
+
margin-bottom: 2px;
|
|
165
|
+
letter-spacing: 0.08em;
|
|
166
|
+
text-transform: uppercase;
|
|
167
|
+
font-size: 10px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.content { max-width: 820px; padding: 56px 64px 96px; }
|
|
171
|
+
|
|
172
|
+
.section { margin-bottom: 64px; }
|
|
173
|
+
|
|
174
|
+
.section-header {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: baseline;
|
|
177
|
+
gap: 14px;
|
|
178
|
+
margin-bottom: 24px;
|
|
179
|
+
padding-bottom: 10px;
|
|
180
|
+
border-bottom: 1.5px solid var(--border);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.section-title {
|
|
184
|
+
font-family: var(--serif);
|
|
185
|
+
font-size: 22px;
|
|
186
|
+
font-weight: 700;
|
|
187
|
+
color: var(--ink);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
h3 {
|
|
191
|
+
font-family: var(--serif);
|
|
192
|
+
font-size: 16px;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
margin: 28px 0 10px;
|
|
195
|
+
color: var(--ink);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
p {
|
|
199
|
+
color: var(--ink-light);
|
|
200
|
+
margin-bottom: 14px;
|
|
201
|
+
max-width: 700px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
strong { color: var(--ink); font-weight: 500; }
|
|
205
|
+
|
|
206
|
+
a { color: var(--accent); }
|
|
207
|
+
a:hover { text-decoration: underline; }
|
|
208
|
+
|
|
209
|
+
.callout {
|
|
210
|
+
background: white;
|
|
211
|
+
border-left: 3px solid var(--accent);
|
|
212
|
+
padding: 12px 16px;
|
|
213
|
+
margin: 16px 0;
|
|
214
|
+
font-size: 13.5px;
|
|
215
|
+
color: var(--ink-light);
|
|
216
|
+
border-radius: 0 4px 4px 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.callout code {
|
|
220
|
+
font-family: var(--mono);
|
|
221
|
+
font-size: 12px;
|
|
222
|
+
background: var(--paper-warm);
|
|
223
|
+
padding: 1px 5px;
|
|
224
|
+
border-radius: 2px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
ul.feature-list {
|
|
228
|
+
list-style: none;
|
|
229
|
+
margin: 12px 0 20px;
|
|
230
|
+
display: flex;
|
|
231
|
+
flex-direction: column;
|
|
232
|
+
gap: 8px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
ul.feature-list li {
|
|
236
|
+
font-size: 14px;
|
|
237
|
+
color: var(--ink-light);
|
|
238
|
+
padding-left: 18px;
|
|
239
|
+
position: relative;
|
|
240
|
+
max-width: 700px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
ul.feature-list li::before {
|
|
244
|
+
content: '·';
|
|
245
|
+
position: absolute;
|
|
246
|
+
left: 0;
|
|
247
|
+
color: var(--accent);
|
|
248
|
+
font-weight: 700;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
ul.limits-list {
|
|
252
|
+
list-style: none;
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-direction: column;
|
|
255
|
+
gap: 10px;
|
|
256
|
+
margin-top: 8px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
ul.limits-list li {
|
|
260
|
+
display: flex;
|
|
261
|
+
gap: 12px;
|
|
262
|
+
font-size: 13.5px;
|
|
263
|
+
color: var(--ink-light);
|
|
264
|
+
padding: 12px 14px;
|
|
265
|
+
background: white;
|
|
266
|
+
border: 1px solid var(--border);
|
|
267
|
+
border-radius: 4px;
|
|
268
|
+
max-width: 700px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.limit-num {
|
|
272
|
+
font-family: var(--mono);
|
|
273
|
+
font-size: 11px;
|
|
274
|
+
color: var(--ink-faint);
|
|
275
|
+
flex-shrink: 0;
|
|
276
|
+
padding-top: 2px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
table {
|
|
280
|
+
width: 100%;
|
|
281
|
+
border-collapse: collapse;
|
|
282
|
+
font-size: 13px;
|
|
283
|
+
margin: 16px 0 24px;
|
|
284
|
+
background: white;
|
|
285
|
+
border: 1px solid var(--border);
|
|
286
|
+
border-radius: 4px;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
th {
|
|
291
|
+
text-align: left;
|
|
292
|
+
padding: 11px 14px;
|
|
293
|
+
font-family: var(--mono);
|
|
294
|
+
font-size: 11px;
|
|
295
|
+
letter-spacing: 0.05em;
|
|
296
|
+
text-transform: uppercase;
|
|
297
|
+
background: var(--paper-warm);
|
|
298
|
+
color: var(--ink);
|
|
299
|
+
border-bottom: 1px solid var(--border);
|
|
300
|
+
font-weight: 500;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
td {
|
|
304
|
+
padding: 10px 14px;
|
|
305
|
+
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
306
|
+
color: var(--ink-light);
|
|
307
|
+
vertical-align: top;
|
|
308
|
+
line-height: 1.6;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
td:first-child {
|
|
312
|
+
font-family: var(--mono);
|
|
313
|
+
font-size: 12px;
|
|
314
|
+
color: var(--ink);
|
|
315
|
+
white-space: nowrap;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
tr:last-child td { border-bottom: none; }
|
|
319
|
+
|
|
320
|
+
.table-philosophy .row-full td:first-child { color: var(--full); }
|
|
321
|
+
.table-philosophy .row-degrade td:first-child { color: var(--degrade); }
|
|
322
|
+
.table-philosophy .row-skip td:first-child { color: var(--skip); }
|
|
323
|
+
|
|
324
|
+
.table-tech td:last-child a { color: var(--accent); }
|
|
325
|
+
|
|
326
|
+
pre, .code-block {
|
|
327
|
+
font-family: var(--mono);
|
|
328
|
+
font-size: 12.5px;
|
|
329
|
+
line-height: 1.7;
|
|
330
|
+
border-radius: 4px;
|
|
331
|
+
margin: 12px 0 20px;
|
|
332
|
+
overflow-x: auto;
|
|
333
|
+
max-width: 700px;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
pre.shell {
|
|
337
|
+
background: var(--ink);
|
|
338
|
+
color: #a8d8a8;
|
|
339
|
+
padding: 16px 20px;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
pre.shell .prompt { color: #666; margin-right: 8px; }
|
|
343
|
+
|
|
344
|
+
pre.tree, pre.pipeline {
|
|
345
|
+
background: white;
|
|
346
|
+
border: 1px solid var(--border);
|
|
347
|
+
color: var(--ink-light);
|
|
348
|
+
padding: 16px 20px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
pre.js {
|
|
352
|
+
background: white;
|
|
353
|
+
border: 1px solid var(--border);
|
|
354
|
+
padding: 16px 20px;
|
|
355
|
+
color: var(--ink-light);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
pre.js .kw { color: #8b5e00; }
|
|
359
|
+
pre.js .fn { color: var(--full); }
|
|
360
|
+
pre.js .str { color: var(--accent); }
|
|
361
|
+
pre.js .com { color: var(--ink-faint); }
|
|
362
|
+
|
|
363
|
+
.pipeline-flow {
|
|
364
|
+
display: flex;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
gap: 0;
|
|
367
|
+
margin: 20px 0;
|
|
368
|
+
max-width: 520px;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.pipeline-step {
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: stretch;
|
|
374
|
+
gap: 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.pipeline-connector {
|
|
378
|
+
display: flex;
|
|
379
|
+
flex-direction: column;
|
|
380
|
+
align-items: center;
|
|
381
|
+
width: 36px;
|
|
382
|
+
flex-shrink: 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.pipeline-dot {
|
|
386
|
+
width: 8px; height: 8px;
|
|
387
|
+
border-radius: 50%;
|
|
388
|
+
background: var(--accent);
|
|
389
|
+
margin-top: 14px;
|
|
390
|
+
flex-shrink: 0;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.pipeline-line {
|
|
394
|
+
width: 1px;
|
|
395
|
+
flex: 1;
|
|
396
|
+
background: var(--border);
|
|
397
|
+
margin: 2px 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.pipeline-box {
|
|
401
|
+
flex: 1;
|
|
402
|
+
border: 1px solid var(--border);
|
|
403
|
+
border-radius: 4px;
|
|
404
|
+
padding: 10px 14px;
|
|
405
|
+
margin: 3px 0;
|
|
406
|
+
background: white;
|
|
407
|
+
font-family: var(--mono);
|
|
408
|
+
font-size: 12px;
|
|
409
|
+
color: var(--ink-light);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.pipeline-box strong { font-weight: 500; color: var(--ink); }
|
|
413
|
+
|
|
414
|
+
.pipeline-io {
|
|
415
|
+
font-family: var(--mono);
|
|
416
|
+
font-size: 12px;
|
|
417
|
+
padding: 10px 14px;
|
|
418
|
+
border-radius: 3px;
|
|
419
|
+
margin: 4px 0 4px 36px;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.pipeline-io.in {
|
|
423
|
+
background: var(--ink);
|
|
424
|
+
color: var(--paper);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.pipeline-io.out {
|
|
428
|
+
background: var(--full-bg);
|
|
429
|
+
color: var(--full);
|
|
430
|
+
border: 1px solid #b2d8be;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.status-banner {
|
|
434
|
+
background: var(--accent-bg);
|
|
435
|
+
border: 1px solid var(--accent-dim);
|
|
436
|
+
border-radius: 4px;
|
|
437
|
+
padding: 16px 20px;
|
|
438
|
+
font-size: 14px;
|
|
439
|
+
color: var(--ink-light);
|
|
440
|
+
max-width: 700px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.status-banner strong { color: var(--accent); }
|
|
444
|
+
|
|
445
|
+
.license {
|
|
446
|
+
font-family: var(--mono);
|
|
447
|
+
font-size: 13px;
|
|
448
|
+
color: var(--ink-faint);
|
|
449
|
+
padding-top: 24px;
|
|
450
|
+
border-top: 1px solid var(--border);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
code {
|
|
454
|
+
font-family: var(--mono);
|
|
455
|
+
font-size: 12px;
|
|
456
|
+
background: var(--paper-warm);
|
|
457
|
+
padding: 1px 5px;
|
|
458
|
+
border-radius: 2px;
|
|
459
|
+
color: var(--ink);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.badge {
|
|
463
|
+
font-family: var(--mono);
|
|
464
|
+
font-size: 10px;
|
|
465
|
+
letter-spacing: 0.04em;
|
|
466
|
+
padding: 2px 8px;
|
|
467
|
+
border-radius: 3px;
|
|
468
|
+
white-space: nowrap;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.badge-full { background: var(--full-bg); color: var(--full); }
|
|
472
|
+
.badge-degrade { background: var(--degrade-bg); color: var(--degrade); }
|
|
473
|
+
.badge-skip { background: var(--skip-bg); color: var(--skip); }
|
|
474
|
+
.badge-plan { background: #eee; color: #666; }
|
|
475
|
+
|
|
476
|
+
.callout-note {
|
|
477
|
+
background: #fff8e1;
|
|
478
|
+
border-left: 3px solid #f0a500;
|
|
479
|
+
padding: 12px 16px;
|
|
480
|
+
margin: 16px 0;
|
|
481
|
+
font-size: 13px;
|
|
482
|
+
color: #6b4c00;
|
|
483
|
+
border-radius: 0 4px 4px 0;
|
|
484
|
+
max-width: 700px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
@media (max-width: 900px) {
|
|
488
|
+
nav { display: none; }
|
|
489
|
+
main { margin-left: 0; }
|
|
490
|
+
.content { padding: 40px 24px 64px; }
|
|
491
|
+
.hero { padding: 48px 24px; }
|
|
492
|
+
.phil-layers { grid-template-columns: 1fr; }
|
|
493
|
+
}
|
|
494
|
+
</style>
|
|
495
|
+
</head>
|
|
496
|
+
<body>
|
|
497
|
+
|
|
498
|
+
<nav>
|
|
499
|
+
<div class="nav-logo">pptx2js<br>README</div>
|
|
500
|
+
<ul>
|
|
501
|
+
<li class="section-label">概述</li>
|
|
502
|
+
<li><a href="#philosophy">核心哲学</a></li>
|
|
503
|
+
<li><a href="#features">功能特性</a></li>
|
|
504
|
+
<li><a href="#capabilities">转换能力</a></li>
|
|
505
|
+
<li class="section-label">使用</li>
|
|
506
|
+
<li><a href="#install">安装</a></li>
|
|
507
|
+
<li><a href="#usage">使用方式</a></li>
|
|
508
|
+
<li><a href="#run-output">运行脚本</a></li>
|
|
509
|
+
<li><a href="#output">输出结构</a></li>
|
|
510
|
+
<li class="section-label">技术</li>
|
|
511
|
+
<li><a href="#architecture">系统架构</a></li>
|
|
512
|
+
<li><a href="#tech">技术栈</a></li>
|
|
513
|
+
<li><a href="#dev">开发</a></li>
|
|
514
|
+
<li class="section-label">其他</li>
|
|
515
|
+
<li><a href="#limits">已知局限</a></li>
|
|
516
|
+
<li><a href="#status">项目状态</a></li>
|
|
517
|
+
</ul>
|
|
518
|
+
<div class="nav-footer">
|
|
519
|
+
<a href="./design.html">设计文档 →</a><br>
|
|
520
|
+
<a href="https://github.com/yuese12333/pptx2js">GitHub →</a>
|
|
521
|
+
</div>
|
|
522
|
+
</nav>
|
|
523
|
+
|
|
524
|
+
<main>
|
|
525
|
+
<header class="hero">
|
|
526
|
+
<div class="hero-label">PPTX → PptxGenJS · 项目说明</div>
|
|
527
|
+
<h1>pptx2js</h1>
|
|
528
|
+
<p class="hero-desc">
|
|
529
|
+
将 <strong style="color:#ddd">PowerPoint <code style="background:#333;color:#ccc;padding:2px 6px;border-radius:2px">.pptx</code></strong>
|
|
530
|
+
文件转换为可直接运行的
|
|
531
|
+
<a href="https://github.com/gitbrent/PptxGenJS" target="_blank" rel="noopener">PptxGenJS</a>
|
|
532
|
+
生成脚本,并附带媒体资源与转换报告。
|
|
533
|
+
</p>
|
|
534
|
+
<p class="hero-desc" style="margin-top:12px;font-size:13px">
|
|
535
|
+
<a href="./design.html">设计文档</a> · <a href="./README.md">Markdown 版</a>
|
|
536
|
+
</p>
|
|
537
|
+
<div class="hero-meta">
|
|
538
|
+
<span><strong>运行环境</strong>Node.js ≥ 18</span>
|
|
539
|
+
<span><strong>版本</strong>v0.4.0</span>
|
|
540
|
+
<span><strong>许可</strong>MIT</span>
|
|
541
|
+
<span><strong>仓库</strong>GitHub 开源</span>
|
|
542
|
+
</div>
|
|
543
|
+
</header>
|
|
544
|
+
|
|
545
|
+
<div class="content">
|
|
546
|
+
|
|
547
|
+
<!-- 核心哲学 -->
|
|
548
|
+
<section class="section" id="philosophy">
|
|
549
|
+
<div class="section-header">
|
|
550
|
+
<h2 class="section-title">核心哲学</h2>
|
|
551
|
+
</div>
|
|
552
|
+
<p><strong>「PptxGenJS 能力边界即转换边界」</strong></p>
|
|
553
|
+
<table class="table-philosophy">
|
|
554
|
+
<thead><tr><th>层级</th><th>策略</th><th>示例</th></tr></thead>
|
|
555
|
+
<tbody>
|
|
556
|
+
<tr class="row-full">
|
|
557
|
+
<td>精确转换</td>
|
|
558
|
+
<td>PptxGenJS 原生支持的元素一比一映射</td>
|
|
559
|
+
<td>文本框、图片、基本形状、线条、纯色背景</td>
|
|
560
|
+
</tr>
|
|
561
|
+
<tr class="row-degrade">
|
|
562
|
+
<td>退化转换</td>
|
|
563
|
+
<td>尽力保留内容,降级为近似表示</td>
|
|
564
|
+
<td>渐变→纯色、未知形状→矩形</td>
|
|
565
|
+
</tr>
|
|
566
|
+
<tr class="row-skip">
|
|
567
|
+
<td>静默跳过</td>
|
|
568
|
+
<td>记录日志,不中断流程</td>
|
|
569
|
+
<td>图表/表格框架、ActiveX、OLE、VBA</td>
|
|
570
|
+
</tr>
|
|
571
|
+
</tbody>
|
|
572
|
+
</table>
|
|
573
|
+
</section>
|
|
574
|
+
|
|
575
|
+
<!-- 功能特性 -->
|
|
576
|
+
<section class="section" id="features">
|
|
577
|
+
<div class="section-header"><h2 class="section-title">功能特性</h2></div>
|
|
578
|
+
<ul class="feature-list">
|
|
579
|
+
<li>输入 <code>.pptx</code>,输出 <code>output.js</code> + <code>media/</code> + <code>conversion.log</code> + 自动生成说明</li>
|
|
580
|
+
<li>同时提供 <strong>CLI</strong> 与 <strong>编程 API</strong>,共用同一套核心逻辑</li>
|
|
581
|
+
<li>转换报告为 JSON,含 <code>slideIndex</code>、<code>elementBounds</code>、<code>severity</code>,便于 CI 集成</li>
|
|
582
|
+
<li>仅支持 OOXML(<code>.pptx</code>),不做双向转换、不做 GUI</li>
|
|
583
|
+
</ul>
|
|
584
|
+
</section>
|
|
585
|
+
|
|
586
|
+
<!-- 转换能力 -->
|
|
587
|
+
<section class="section" id="capabilities">
|
|
588
|
+
<div class="section-header"><h2 class="section-title">当前转换能力(v0.4.0)</h2></div>
|
|
589
|
+
<table>
|
|
590
|
+
<thead><tr><th>元素</th><th>状态</th><th>说明</th></tr></thead>
|
|
591
|
+
<tbody>
|
|
592
|
+
<tr><td>文本框(段落对齐、lvl 缩进、段距、行距、列表、超链接)</td><td><span class="badge badge-full">精确</span></td><td><code>addText()</code></td></tr>
|
|
593
|
+
<tr><td>图片</td><td><span class="badge badge-full">精确</span></td><td><code>addImage()</code></td></tr>
|
|
594
|
+
<tr><td>表格(含单元格边框)</td><td><span class="badge badge-full">精确</span></td><td><code>addTable()</code></td></tr>
|
|
595
|
+
<tr><td>图表 BAR/LINE/PIE/AREA/DOUGHNUT/SCATTER/RADAR/BUBBLE</td><td><span class="badge badge-full">精确</span></td><td><code>addChart()</code></td></tr>
|
|
596
|
+
<tr><td>形状 / 背景 / 组合</td><td><span class="badge badge-full">精确</span></td><td><code>addShape()</code> 等</td></tr>
|
|
597
|
+
<tr><td>母版/版式占位符</td><td><span class="badge badge-full">精确</span></td><td><code>placeholder.js</code></td></tr>
|
|
598
|
+
<tr><td>SmartArt</td><td><span class="badge badge-degrade">退化</span></td><td>文本列表或占位;缓存 PNG 暂不实现</td></tr>
|
|
599
|
+
<tr><td>渐变 / 未知形状</td><td><span class="badge badge-degrade">退化</span></td><td>首色标、矩形 fallback</td></tr>
|
|
600
|
+
<tr><td>不支持图表</td><td><span class="badge badge-degrade">退化</span></td><td>缓存图或占位文本</td></tr>
|
|
601
|
+
<tr><td>复杂动画</td><td><span class="badge badge-plan">计划</span></td><td>design.html</td></tr>
|
|
602
|
+
</tbody>
|
|
603
|
+
</table>
|
|
604
|
+
</section>
|
|
605
|
+
|
|
606
|
+
<!-- 安装 -->
|
|
607
|
+
<section class="section" id="install">
|
|
608
|
+
<div class="section-header"><h2 class="section-title">安装</h2></div>
|
|
609
|
+
<pre class="shell"><span class="prompt">$</span>npm install pptx2js</pre>
|
|
610
|
+
<p>本地开发:</p>
|
|
611
|
+
<pre class="shell"><span class="prompt">$</span>git clone https://github.com/yuese12333/pptx2js.git
|
|
612
|
+
<span class="prompt">$</span>cd pptx2js
|
|
613
|
+
<span class="prompt">$</span>npm install
|
|
614
|
+
<span class="prompt">$</span>npm test</pre>
|
|
615
|
+
</section>
|
|
616
|
+
|
|
617
|
+
<!-- 使用方式 -->
|
|
618
|
+
<section class="section" id="usage">
|
|
619
|
+
<div class="section-header"><h2 class="section-title">使用方式</h2></div>
|
|
620
|
+
|
|
621
|
+
<h3>CLI</h3>
|
|
622
|
+
<pre class="shell"><span class="prompt">$</span>npx pptx2js input.pptx -o ./pptx2js-output</pre>
|
|
623
|
+
|
|
624
|
+
<p>常用参数:</p>
|
|
625
|
+
<table>
|
|
626
|
+
<thead><tr><th>参数</th><th>默认值</th><th>说明</th></tr></thead>
|
|
627
|
+
<tbody>
|
|
628
|
+
<tr><td>input.pptx</td><td>必填</td><td>源文件路径</td></tr>
|
|
629
|
+
<tr><td>-o, --output</td><td>./pptx2js-output</td><td>输出目录</td></tr>
|
|
630
|
+
<tr><td>--no-media</td><td>—</td><td>不提取媒体,图片引用变为占位注释</td></tr>
|
|
631
|
+
<tr><td>--strict-degrade</td><td>false</td><td>任意退化项触发非零退出码</td></tr>
|
|
632
|
+
<tr><td>--strict-skip</td><td>false</td><td><code>severity:error</code> 跳过项触发非零退出码</td></tr>
|
|
633
|
+
<tr><td>--log-level</td><td>info</td><td><code>minimal</code> / <code>info</code> / <code>verbose</code></td></tr>
|
|
634
|
+
<tr><td>--max-file-size</td><td>50MB</td><td>超过阈值切换流式解析(实现中)</td></tr>
|
|
635
|
+
</tbody>
|
|
636
|
+
</table>
|
|
637
|
+
|
|
638
|
+
<div class="callout-note">
|
|
639
|
+
Commander 的 <code>--no-media</code> 对应内部选项 <code>media</code>(默认 <code>true</code>)。传入 <code>--no-media</code> 后才会跳过媒体提取。
|
|
640
|
+
</div>
|
|
641
|
+
|
|
642
|
+
<h3>编程 API</h3>
|
|
643
|
+
<pre class="js"><span class="kw">const</span> { <span class="fn">convert</span> } = <span class="kw">require</span>(<span class="str">'pptx2js'</span>);
|
|
644
|
+
|
|
645
|
+
<span class="kw">const</span> result = <span class="kw">await</span> <span class="fn">convert</span>(<span class="str">'./input.pptx'</span>, {
|
|
646
|
+
outputDir: <span class="str">'./pptx2js-output'</span>,
|
|
647
|
+
logLevel: <span class="str">'info'</span>,
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
console.log(result.scriptPath); <span class="com">// .../output.js</span>
|
|
651
|
+
console.log(result.logPath); <span class="com">// .../conversion.log</span>
|
|
652
|
+
console.log(result.log.statistics);</pre>
|
|
653
|
+
</section>
|
|
654
|
+
|
|
655
|
+
<!-- 运行生成脚本 -->
|
|
656
|
+
<section class="section" id="run-output">
|
|
657
|
+
<div class="section-header"><h2 class="section-title">运行生成脚本</h2></div>
|
|
658
|
+
<p><code>output.js</code> 依赖 <a href="https://github.com/gitbrent/PptxGenJS" target="_blank" rel="noopener">PptxGenJS</a>,需在输出目录单独安装:</p>
|
|
659
|
+
<pre class="shell"><span class="prompt">$</span>cd pptx2js-output
|
|
660
|
+
<span class="prompt">$</span>npm init -y
|
|
661
|
+
<span class="prompt">$</span>npm install pptxgenjs
|
|
662
|
+
<span class="prompt">$</span>node output.js <span class="com" style="color:#666"># 生成 output.pptx</span></pre>
|
|
663
|
+
</section>
|
|
664
|
+
|
|
665
|
+
<!-- 输出目录结构 -->
|
|
666
|
+
<section class="section" id="output">
|
|
667
|
+
<div class="section-header"><h2 class="section-title">输出目录结构</h2></div>
|
|
668
|
+
<pre class="tree">pptx2js-output/
|
|
669
|
+
├── output.js # 主生成脚本,可直接 node 运行
|
|
670
|
+
├── media/ # 提取的图片等媒体
|
|
671
|
+
├── conversion.log # JSON 格式转换报告
|
|
672
|
+
└── README.md # 自动生成的说明</pre>
|
|
673
|
+
<p><code>conversion.log</code> 主要字段:<code>source</code>、<code>statistics</code>、<code>slides[]</code>、<code>degraded[]</code>、<code>omitted[]</code>、<code>warnings[]</code>。</p>
|
|
674
|
+
</section>
|
|
675
|
+
|
|
676
|
+
<!-- 系统架构 -->
|
|
677
|
+
<section class="section" id="architecture">
|
|
678
|
+
<div class="section-header"><h2 class="section-title">系统架构</h2></div>
|
|
679
|
+
<p>六模块流水线(顺序执行):</p>
|
|
680
|
+
<div class="pipeline-flow">
|
|
681
|
+
<div class="pipeline-io in">input.pptx</div>
|
|
682
|
+
<div class="pipeline-step">
|
|
683
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div><div class="pipeline-line"></div></div>
|
|
684
|
+
<div class="pipeline-box"><strong>① 解压与索引</strong> — lib/unpacker.js</div>
|
|
685
|
+
</div>
|
|
686
|
+
<div class="pipeline-step">
|
|
687
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div><div class="pipeline-line"></div></div>
|
|
688
|
+
<div class="pipeline-box"><strong>② XML 预解析</strong> — lib/xml-parser.js + lib/rels.js</div>
|
|
689
|
+
</div>
|
|
690
|
+
<div class="pipeline-step">
|
|
691
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div><div class="pipeline-line"></div></div>
|
|
692
|
+
<div class="pipeline-box"><strong>③ 实体提取器</strong> — extractor, placeholder, table, chart, smartart</div>
|
|
693
|
+
</div>
|
|
694
|
+
<div class="pipeline-step">
|
|
695
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div><div class="pipeline-line"></div></div>
|
|
696
|
+
<div class="pipeline-box"><strong>④ 映射引擎</strong> — lib/mapper.js</div>
|
|
697
|
+
</div>
|
|
698
|
+
<div class="pipeline-step">
|
|
699
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div><div class="pipeline-line"></div></div>
|
|
700
|
+
<div class="pipeline-box"><strong>⑤ 代码生成器</strong> — lib/codegen.js</div>
|
|
701
|
+
</div>
|
|
702
|
+
<div class="pipeline-step">
|
|
703
|
+
<div class="pipeline-connector"><div class="pipeline-dot"></div></div>
|
|
704
|
+
<div class="pipeline-box"><strong>⑥ 资源打包器</strong> — lib/packager.js</div>
|
|
705
|
+
</div>
|
|
706
|
+
<div class="pipeline-io out">output.js + media/ + conversion.log</div>
|
|
707
|
+
</div>
|
|
708
|
+
<p style="margin-top:16px;font-size:13px;color:var(--ink-faint)">辅助模块:<code>lib/presentation.js</code>、<code>lib/graphic.js</code>、<code>lib/smartart.js</code>、<code>lib/xml-utils.js</code>、<code>lib/utils/color.js</code>、<code>lib/utils/bounds.js</code></p>
|
|
709
|
+
</section>
|
|
710
|
+
|
|
711
|
+
<!-- 技术栈 -->
|
|
712
|
+
<section class="section" id="tech">
|
|
713
|
+
<div class="section-header"><h2 class="section-title">技术栈</h2></div>
|
|
714
|
+
<table class="table-tech">
|
|
715
|
+
<thead><tr><th>用途</th><th>选型</th></tr></thead>
|
|
716
|
+
<tbody>
|
|
717
|
+
<tr><td>ZIP 处理</td><td><a href="https://www.npmjs.com/package/jszip" target="_blank" rel="noopener">JSZip</a></td></tr>
|
|
718
|
+
<tr><td>XML 解析</td><td><a href="https://www.npmjs.com/package/xml2js" target="_blank" rel="noopener">xml2js</a>(统一配置,见 <code>lib/xml-parser.js</code>)</td></tr>
|
|
719
|
+
<tr><td>CLI</td><td><a href="https://www.npmjs.com/package/commander" target="_blank" rel="noopener">Commander.js</a></td></tr>
|
|
720
|
+
<tr><td>终端输出</td><td><a href="https://www.npmjs.com/package/chalk" target="_blank" rel="noopener">chalk</a></td></tr>
|
|
721
|
+
<tr><td>测试</td><td><a href="https://jestjs.io/" target="_blank" rel="noopener">Jest</a>(单元 + 集成)</td></tr>
|
|
722
|
+
</tbody>
|
|
723
|
+
</table>
|
|
724
|
+
<p>代码格式化在生成阶段自实现缩进拼接,不引入 Prettier。</p>
|
|
725
|
+
</section>
|
|
726
|
+
|
|
727
|
+
<!-- 开发 -->
|
|
728
|
+
<section class="section" id="dev">
|
|
729
|
+
<div class="section-header"><h2 class="section-title">开发</h2></div>
|
|
730
|
+
<pre class="shell"><span class="prompt">$</span>npm test <span class="com" style="color:#666"># 24 用例(单元 + 集成)</span>
|
|
731
|
+
<span class="prompt">$</span>npm run test:watch <span class="com" style="color:#666"># 监听模式</span></pre>
|
|
732
|
+
|
|
733
|
+
<h3>目录结构</h3>
|
|
734
|
+
<pre class="tree">pptx2js/
|
|
735
|
+
├── bin/pptx2js.js # CLI 入口
|
|
736
|
+
├── lib/
|
|
737
|
+
│ ├── convert.js # 流水线编排
|
|
738
|
+
│ ├── unpacker.js # ① 解压
|
|
739
|
+
│ ├── xml-parser.js # ② 统一 XML 解析
|
|
740
|
+
│ ├── rels.js # ② 关系索引
|
|
741
|
+
│ ├── presentation.js # 幻灯片列表 / 尺寸
|
|
742
|
+
│ ├── placeholder.js # 母版/版式占位符
|
|
743
|
+
│ ├── graphic.js # graphicFrame 识别
|
|
744
|
+
│ ├── table.js # 表格提取
|
|
745
|
+
│ ├── chart.js # 图表提取
|
|
746
|
+
│ ├── smartart.js # SmartArt 退化
|
|
747
|
+
│ ├── extractor.js # ③ 实体提取
|
|
748
|
+
│ ├── mapper.js # ④ IR 映射
|
|
749
|
+
│ ├── codegen.js # ⑤ 代码生成
|
|
750
|
+
│ ├── packager.js # ⑥ 资源打包
|
|
751
|
+
│ └── utils/ # EMU、颜色
|
|
752
|
+
├── test/
|
|
753
|
+
│ ├── unit/
|
|
754
|
+
│ ├── integration/
|
|
755
|
+
│ └── helpers/
|
|
756
|
+
├── design.html
|
|
757
|
+
└── README.html</pre>
|
|
758
|
+
</section>
|
|
759
|
+
|
|
760
|
+
<!-- 已知局限 -->
|
|
761
|
+
<section class="section" id="limits">
|
|
762
|
+
<div class="section-header"><h2 class="section-title">已知局限</h2></div>
|
|
763
|
+
<ul class="limits-list">
|
|
764
|
+
<li><span class="limit-num">L1</span><span><strong>字体</strong>依赖运行环境,不负责检测或打包嵌入字体</span></li>
|
|
765
|
+
<li><span class="limit-num">L2</span><span><strong>母版继承</strong>已实现 xfrm 与基础 txBody;复杂列表样式继承仍有限;<code>a:spcPct</code> 百分比段距/行距暂不处理</span></li>
|
|
766
|
+
<li><span class="limit-num">L3</span><span><strong>SmartArt</strong> 仅文本列表退化,缓存图片因 PPT 版本差异未实现</span></li>
|
|
767
|
+
<li><span class="limit-num">L4</span><span><strong>动画</strong>尚未转换(设计为退化淡入,待实现)</span></li>
|
|
768
|
+
<li><span class="limit-num">L5</span><span><strong>不保证</strong>往返 PPTX 二进制一致,追求视觉可接受</span></li>
|
|
769
|
+
<li><span class="limit-num">L6</span><span><strong>不支持</strong>密码保护或 <code>.ppt</code> 旧格式</span></li>
|
|
770
|
+
<li><span class="limit-num">L7</span><span><strong>不支持</strong>增量转换,每次全量重写</span></li>
|
|
771
|
+
<li><span class="limit-num">L8</span><span><strong>大文件</strong>流式解析仍在实现中</span></li>
|
|
772
|
+
<li><span class="limit-num">L9</span><span><strong>媒体重名</strong>同名文件可能互相覆盖(packager 待去重)</span></li>
|
|
773
|
+
</ul>
|
|
774
|
+
</section>
|
|
775
|
+
|
|
776
|
+
<!-- 项目状态 -->
|
|
777
|
+
<section class="section" id="status">
|
|
778
|
+
<div class="section-header"><h2 class="section-title">项目状态</h2></div>
|
|
779
|
+
<div class="status-banner">
|
|
780
|
+
当前为 <strong>v0.4.0</strong>:在 v0.3.0 基础上新增段落级格式、表格单元格边框、扩展图表类型、SmartArt 文本列表退化。复杂动画、SmartArt 缓存图、外部链接表格等按 <a href="./design.html" style="color:var(--accent)">design.html</a> 继续推进,欢迎贡献。
|
|
781
|
+
</div>
|
|
782
|
+
<p class="license" style="margin-top:32px;border:none;padding:0">License: MIT</p>
|
|
783
|
+
</section>
|
|
784
|
+
|
|
785
|
+
</div>
|
|
786
|
+
</main>
|
|
787
|
+
|
|
788
|
+
<script>
|
|
789
|
+
const sections = document.querySelectorAll('section[id]');
|
|
790
|
+
const navLinks = document.querySelectorAll('nav a[href^="#"]');
|
|
791
|
+
|
|
792
|
+
const observer = new IntersectionObserver((entries) => {
|
|
793
|
+
entries.forEach((entry) => {
|
|
794
|
+
if (!entry.isIntersecting) return;
|
|
795
|
+
navLinks.forEach((link) => {
|
|
796
|
+
link.classList.toggle('active', link.getAttribute('href') === '#' + entry.target.id);
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
}, { rootMargin: '-20% 0px -70% 0px' });
|
|
800
|
+
|
|
801
|
+
sections.forEach((s) => observer.observe(s));
|
|
802
|
+
</script>
|
|
803
|
+
</body>
|
|
804
|
+
</html>
|