pptxtojson 1.5.1 → 1.6.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.md +145 -136
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/favicon.ico +0 -0
- package/index.html +437 -93
- package/package.json +1 -1
- package/src/animation.js +81 -0
- package/src/pptxtojson.js +8 -0
- package/src/shape.js +1 -1
package/favicon.ico
CHANGED
|
Binary file
|
package/index.html
CHANGED
|
@@ -5,52 +5,248 @@
|
|
|
5
5
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
7
|
<meta name="description" content="Office PowerPoint(.pptx) file to JSON | 将 PPTX 文件转为可读的 JSON 数据" />
|
|
8
|
-
<meta name="keywords" content="pptx2json,pptxtojson,ppt,powerpoint,json,
|
|
8
|
+
<meta name="keywords" content="pptx2json,pptxtojson,ppt,powerpoint,json,PPT解析,PPT转JSON" />
|
|
9
9
|
<link rel="icon" href="favicon.ico">
|
|
10
10
|
<title>pptxtojson - PPTX转JSON</title>
|
|
11
11
|
|
|
12
12
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsoneditor@9.9.2/dist/jsoneditor.min.css">
|
|
13
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
|
|
13
14
|
<script src="https://cdn.jsdelivr.net/npm/jsoneditor@9.9.2/dist/jsoneditor.min.js"></script>
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
|
14
16
|
<script src="dist/index.umd.js"></script>
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
html, body {
|
|
23
|
-
height: 100%;
|
|
24
|
-
}
|
|
25
|
-
::-webkit-scrollbar {
|
|
26
|
-
width: 5px;
|
|
27
|
-
height: 5px;
|
|
28
|
-
background-color: #fff;
|
|
29
|
-
}
|
|
30
|
-
::-webkit-scrollbar-thumb {
|
|
31
|
-
background-color: #c1c1c1;
|
|
32
|
-
}
|
|
18
|
+
<style>
|
|
19
|
+
:root {
|
|
20
|
+
--primary-color: #33bcfc;
|
|
21
|
+
--secondary-color: #d897fd;
|
|
22
|
+
}
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
* {
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0;
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
font-family: 'Segoe UI', 'Roboto', sans-serif;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body {
|
|
32
|
+
color: #1e293b;
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
background-color: #f3f4f6;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.container {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex: 1;
|
|
42
|
+
padding: 32px;
|
|
43
|
+
padding-bottom: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.left-panel {
|
|
47
|
+
flex: 1;
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.header {
|
|
53
|
+
margin-bottom: 16px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.logo {
|
|
57
|
+
font-size: 32px;
|
|
58
|
+
font-weight: 700;
|
|
59
|
+
color: #1e293b;
|
|
60
|
+
margin-bottom: 8px;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.logo span {
|
|
66
|
+
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
|
|
67
|
+
-webkit-background-clip: text;
|
|
68
|
+
background-clip: text;
|
|
69
|
+
color: transparent;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.tagline {
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
color: #64748b;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.upload-section {
|
|
78
|
+
background-color: #fff;
|
|
79
|
+
border-radius: 12px;
|
|
80
|
+
padding: 32px;
|
|
81
|
+
margin-bottom: 32px;
|
|
82
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
83
|
+
border: 1px dashed #d1d5db;
|
|
84
|
+
text-align: center;
|
|
85
|
+
transition: all 0.3s ease;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.upload-section.highlight {
|
|
89
|
+
border-color: var(--primary-color);
|
|
90
|
+
background-color: #f0f8ff;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.upload-section:hover {
|
|
94
|
+
border-color: var(--primary-color);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.upload-icon {
|
|
98
|
+
font-size: 48px;
|
|
99
|
+
margin-bottom: 16px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.upload-title {
|
|
103
|
+
font-size: 20px;
|
|
104
|
+
font-weight: 700;
|
|
105
|
+
margin-bottom: 8px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.upload-desc {
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
color: #64748b;
|
|
111
|
+
margin-bottom: 24px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.upload-btn {
|
|
115
|
+
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
|
|
116
|
+
color: #fff;
|
|
117
|
+
border: none;
|
|
118
|
+
padding: 12px 32px;
|
|
119
|
+
border-radius: 50px;
|
|
120
|
+
font-weight: 700;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
transition: all 0.3s ease;
|
|
123
|
+
box-shadow: 0 4px 15px rgba(58, 134, 255, 0.2);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.upload-btn:hover {
|
|
127
|
+
box-shadow: 0 6px 20px rgba(58, 134, 255, 0.3);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.features {
|
|
131
|
+
background-color: #fff;
|
|
132
|
+
border-radius: 12px;
|
|
133
|
+
padding: 24px;
|
|
134
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.features + .features {
|
|
138
|
+
margin-top: 32px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.features-title {
|
|
142
|
+
font-size: 20px;
|
|
143
|
+
font-weight: 700;
|
|
144
|
+
margin-bottom: 20px;
|
|
145
|
+
position: relative;
|
|
146
|
+
padding-bottom: 8px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.features-title::after {
|
|
150
|
+
content: '';
|
|
151
|
+
position: absolute;
|
|
152
|
+
bottom: 0;
|
|
153
|
+
left: 0;
|
|
154
|
+
width: 40px;
|
|
155
|
+
height: 3px;
|
|
156
|
+
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
|
|
157
|
+
border-radius: 3px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.feature-item {
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.feature-item.link {
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.feature-item.link:hover .feature-text {
|
|
170
|
+
color: var(--primary-color);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.feature-item + .feature-item {
|
|
174
|
+
margin-top: 16px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.feature-icon {
|
|
178
|
+
min-width: 24px;
|
|
179
|
+
color: var(--primary-color);
|
|
180
|
+
margin-right: 12px;
|
|
181
|
+
font-size: 20px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.feature-text {
|
|
185
|
+
font-size: 15px;
|
|
186
|
+
color: #334155;
|
|
187
|
+
transition: all 0.3s ease;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
a {
|
|
191
|
+
color: unset;
|
|
192
|
+
text-decoration: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.footer {
|
|
196
|
+
font-size: 14px;
|
|
197
|
+
color: #64748b;
|
|
198
|
+
text-align: center;
|
|
199
|
+
padding: 24px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.right-panel {
|
|
203
|
+
flex: 1;
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: column;
|
|
206
|
+
margin-left: 32px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.result-header {
|
|
210
|
+
display: flex;
|
|
211
|
+
justify-content: space-between;
|
|
212
|
+
align-items: center;
|
|
213
|
+
margin-bottom: 16px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.result-title {
|
|
217
|
+
font-size: 18px;
|
|
218
|
+
font-weight: 700;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.result-actions button {
|
|
222
|
+
background-color: transparent;
|
|
223
|
+
border: 1px solid #e2e8f0;
|
|
224
|
+
padding: 4px 16px;
|
|
225
|
+
border-radius: 4px;
|
|
226
|
+
margin-left: 8px;
|
|
227
|
+
color: #475569;
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
transition: all 0.3s ease;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.result-actions button:hover {
|
|
233
|
+
background-color: #f8fafc;
|
|
234
|
+
color: var(--primary-color);
|
|
235
|
+
border-color: var(--primary-color);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.json-preview {
|
|
239
|
+
flex: 1;
|
|
240
|
+
background-color: #fff;
|
|
241
|
+
border-radius: 12px;
|
|
242
|
+
padding: 24px;
|
|
243
|
+
overflow: auto;
|
|
244
|
+
font-family: 'Courier New', monospace;
|
|
245
|
+
position: relative;
|
|
246
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
247
|
+
}
|
|
52
248
|
.jsoneditor-mode-view {
|
|
53
|
-
border:
|
|
249
|
+
border: none;
|
|
54
250
|
}
|
|
55
251
|
.jsoneditor-menu {
|
|
56
252
|
display: none;
|
|
@@ -63,70 +259,218 @@
|
|
|
63
259
|
padding-top: 0 !important;
|
|
64
260
|
}
|
|
65
261
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
.link a {
|
|
87
|
-
padding: 5px 10px;
|
|
88
|
-
color: #d14424;
|
|
89
|
-
}
|
|
90
|
-
</style>
|
|
262
|
+
/* 响应式调整 */
|
|
263
|
+
@media screen and (max-width: 768px) {
|
|
264
|
+
.container {
|
|
265
|
+
flex-direction: column;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.left-panel,
|
|
269
|
+
.right-panel {
|
|
270
|
+
width: 100%;
|
|
271
|
+
flex: none;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.right-panel {
|
|
275
|
+
min-height: 50vh;
|
|
276
|
+
margin-left: 0;
|
|
277
|
+
margin-top: 32px;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
</style>
|
|
91
281
|
</head>
|
|
92
282
|
|
|
93
283
|
<body>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
284
|
+
<div class="container">
|
|
285
|
+
<!-- 左侧面板 -->
|
|
286
|
+
<div class="left-panel">
|
|
287
|
+
<div class="header">
|
|
288
|
+
<div class="logo">PPTX<span>TO</span>JSON</div>
|
|
289
|
+
<p class="tagline">将 PPTX 文件转为可读的 JSON 数据</p>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<div class="upload-section">
|
|
293
|
+
<div class="upload-icon">📤</div>
|
|
294
|
+
<h3 class="upload-title">上传PPTX文件</h3>
|
|
295
|
+
<p class="upload-desc">「点击选择文件」或「将文件拖放到区域内」上传</p>
|
|
296
|
+
<button class="upload-btn">选择文件</button>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<div class="features">
|
|
300
|
+
<h3 class="features-title">功能介绍</h3>
|
|
301
|
+
<div class="feature-item">
|
|
302
|
+
<span class="feature-icon">✓</span>
|
|
303
|
+
<p class="feature-text">解析PPTX数据,包括幻灯片主题、尺寸、背景、备注、母版、切页动画、页内元素及布局信息等</p>
|
|
304
|
+
</div>
|
|
305
|
+
<div class="feature-item">
|
|
306
|
+
<span class="feature-icon">✓</span>
|
|
307
|
+
<p class="feature-text">支持页内元素包括:文字(HTML富文本)、图片、形状、线条、表格、图表、音视频、公式等复杂元素</p>
|
|
308
|
+
</div>
|
|
309
|
+
<div class="feature-item">
|
|
310
|
+
<span class="feature-icon">✓</span>
|
|
311
|
+
<p class="feature-text">易读易写的结构化 JSON 数据格式</p>
|
|
312
|
+
</div>
|
|
313
|
+
<div class="feature-item">
|
|
314
|
+
<span class="feature-icon">✓</span>
|
|
315
|
+
<p class="feature-text">不依赖任何服务器端环境,直接在浏览器端运行</p>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="features">
|
|
320
|
+
<h3 class="features-title">相关链接</h3>
|
|
321
|
+
<div class="feature-item link">
|
|
322
|
+
<span class="feature-icon">📦</span>
|
|
323
|
+
<p class="feature-text"><a href="https://github.com/pipipi-pikachu/pptx2json" target="_blank">GitHub 仓库</a></p>
|
|
324
|
+
</div>
|
|
325
|
+
<div class="feature-item link">
|
|
326
|
+
<span class="feature-icon">🧩</span>
|
|
327
|
+
<p class="feature-text"><a href="https://pipipi-pikachu.github.io/PPTist/" target="_blank">在 PPTist 中测试</a></p>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<!-- 右侧面板 -->
|
|
333
|
+
<div class="right-panel">
|
|
334
|
+
<div class="result-header">
|
|
335
|
+
<h2 class="result-title">解析结果</h2>
|
|
336
|
+
<div class="result-actions">
|
|
337
|
+
<button class="copy-btn">复制</button>
|
|
338
|
+
<button class="download-btn">下载</button>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<div class="json-preview">
|
|
343
|
+
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<div class="footer">
|
|
349
|
+
<p>MIT License | Copyright © 2020-PRESENT <a href="https://github.com/pipipi-pikachu" target="_blank">pipipi-pikachu</a></p>
|
|
350
|
+
</div>
|
|
106
351
|
|
|
107
352
|
<script>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
353
|
+
document.ondragleave = e => e.preventDefault()
|
|
354
|
+
document.ondrop = e => e.preventDefault()
|
|
355
|
+
document.ondragenter = e => e.preventDefault()
|
|
356
|
+
document.ondragover = e => e.preventDefault()
|
|
111
357
|
|
|
112
|
-
const
|
|
358
|
+
const jsonPreviewRef = document.querySelector('.json-preview')
|
|
359
|
+
const uploadBtnRef = document.querySelector('.upload-btn')
|
|
360
|
+
const uploadSectionRef = document.querySelector('.upload-section')
|
|
361
|
+
const copyBtnRef = document.querySelector('.copy-btn')
|
|
362
|
+
const downloadBtnRef = document.querySelector('.download-btn')
|
|
113
363
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
364
|
+
let jsonString = ''
|
|
365
|
+
|
|
366
|
+
const editor = new JSONEditor(jsonPreviewRef, { mode: 'view' }, {})
|
|
367
|
+
|
|
368
|
+
uploadBtnRef.addEventListener('click', () => {
|
|
369
|
+
const input = document.createElement('input')
|
|
370
|
+
input.type = 'file'
|
|
371
|
+
input.accept = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|
372
|
+
input.click()
|
|
373
|
+
|
|
374
|
+
input.onchange = e => {
|
|
375
|
+
const files = e.target.files
|
|
376
|
+
if (files && files[0]) {
|
|
377
|
+
const reader = new FileReader()
|
|
378
|
+
reader.onload = async e => {
|
|
379
|
+
const json = await pptxtojson.parse(e.target.result)
|
|
380
|
+
editor.set(json)
|
|
381
|
+
console.log(json)
|
|
382
|
+
jsonString = JSON.stringify(json, null, 2)
|
|
383
|
+
}
|
|
384
|
+
reader.readAsArrayBuffer(files[0])
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
uploadSectionRef.addEventListener('drop', async e => {
|
|
390
|
+
uploadSectionRef.classList.remove('highlight')
|
|
391
|
+
|
|
392
|
+
const file = e.dataTransfer.files[0]
|
|
393
|
+
|
|
394
|
+
if (!file) return
|
|
395
|
+
if (file.type === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
|
|
396
|
+
const json = await pptxtojson.parse(file)
|
|
397
|
+
editor.set(json)
|
|
398
|
+
console.log(json)
|
|
399
|
+
jsonString = JSON.stringify(json, null, 2)
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
uploadSectionRef.addEventListener('dragenter', e => {
|
|
404
|
+
e.preventDefault()
|
|
405
|
+
uploadSectionRef.classList.add('highlight')
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
uploadSectionRef.addEventListener('dragleave', e => {
|
|
409
|
+
e.preventDefault()
|
|
410
|
+
if (!uploadSectionRef.contains(e.relatedTarget)) {
|
|
411
|
+
uploadSectionRef.classList.remove('highlight')
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
copyBtnRef.addEventListener('click', () => {
|
|
416
|
+
if (!jsonString) {
|
|
417
|
+
Toastify({
|
|
418
|
+
text: '请先上传文件',
|
|
419
|
+
duration: 1000,
|
|
420
|
+
gravity: 'top',
|
|
421
|
+
position: 'center',
|
|
422
|
+
style: {
|
|
423
|
+
background: 'linear-gradient(to right, #33bcfc, #d897fd)',
|
|
424
|
+
padding: '10px 20px',
|
|
425
|
+
fontSize: '14px',
|
|
426
|
+
},
|
|
427
|
+
}).showToast()
|
|
428
|
+
return
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
navigator.clipboard.writeText(jsonString)
|
|
432
|
+
Toastify({
|
|
433
|
+
text: '已复制到剪贴板',
|
|
434
|
+
duration: 1000,
|
|
435
|
+
gravity: 'top',
|
|
436
|
+
position: 'center',
|
|
437
|
+
style: {
|
|
438
|
+
background: 'linear-gradient(to right, #33bcfc, #d897fd)',
|
|
439
|
+
padding: '10px 20px',
|
|
440
|
+
fontSize: '14px',
|
|
441
|
+
},
|
|
442
|
+
}).showToast()
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
downloadBtnRef.addEventListener('click', () => {
|
|
446
|
+
if (!jsonString) {
|
|
447
|
+
Toastify({
|
|
448
|
+
text: '请先上传文件',
|
|
449
|
+
duration: 1000,
|
|
450
|
+
gravity: 'top',
|
|
451
|
+
position: 'center',
|
|
452
|
+
style: {
|
|
453
|
+
background: 'linear-gradient(to right, #33bcfc, #d897fd)',
|
|
454
|
+
padding: '10px 20px',
|
|
455
|
+
fontSize: '14px',
|
|
456
|
+
},
|
|
457
|
+
}).showToast()
|
|
458
|
+
return
|
|
122
459
|
}
|
|
123
|
-
reader.readAsArrayBuffer(fileName)
|
|
124
|
-
})
|
|
125
460
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
461
|
+
const blob = new Blob([jsonString], { type: 'application/json' })
|
|
462
|
+
const url = URL.createObjectURL(blob)
|
|
463
|
+
|
|
464
|
+
const a = document.createElement('a')
|
|
465
|
+
a.href = url
|
|
466
|
+
a.download = 'slides.json'
|
|
467
|
+
|
|
468
|
+
document.body.appendChild(a)
|
|
469
|
+
a.click()
|
|
470
|
+
|
|
471
|
+
document.body.removeChild(a)
|
|
472
|
+
URL.revokeObjectURL(url)
|
|
473
|
+
})
|
|
130
474
|
</script>
|
|
131
475
|
</body>
|
|
132
|
-
</html>
|
|
476
|
+
</html>
|
package/package.json
CHANGED
package/src/animation.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getTextByPathList } from './utils'
|
|
2
|
+
|
|
3
|
+
export function findTransitionNode(content, rootElement) {
|
|
4
|
+
if (!content || !rootElement) return null
|
|
5
|
+
|
|
6
|
+
const path1 = [rootElement, 'p:transition']
|
|
7
|
+
let transitionNode = getTextByPathList(content, path1)
|
|
8
|
+
if (transitionNode) return transitionNode
|
|
9
|
+
|
|
10
|
+
const path2 = [rootElement, 'mc:AlternateContent', 'mc:Choice', 'p:transition']
|
|
11
|
+
transitionNode = getTextByPathList(content, path2)
|
|
12
|
+
if (transitionNode) return transitionNode
|
|
13
|
+
|
|
14
|
+
const path3 = [rootElement, 'mc:AlternateContent', 'mc:Fallback', 'p:transition']
|
|
15
|
+
transitionNode = getTextByPathList(content, path3)
|
|
16
|
+
|
|
17
|
+
return transitionNode
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseTransition(transitionNode) {
|
|
21
|
+
if (!transitionNode) return null
|
|
22
|
+
|
|
23
|
+
const transition = {
|
|
24
|
+
type: 'none',
|
|
25
|
+
duration: 1000,
|
|
26
|
+
direction: null,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const attrs = transitionNode.attrs || {}
|
|
30
|
+
|
|
31
|
+
let durationFound = false
|
|
32
|
+
const durRegex = /^p\d{2}:dur$/
|
|
33
|
+
for (const key in attrs) {
|
|
34
|
+
if (durRegex.test(key) && !isNaN(parseInt(attrs[key], 10))) {
|
|
35
|
+
transition.duration = parseInt(attrs[key], 10)
|
|
36
|
+
durationFound = true
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!durationFound && attrs.spd) {
|
|
42
|
+
switch (attrs.spd) {
|
|
43
|
+
case 'slow':
|
|
44
|
+
transition.duration = 1000
|
|
45
|
+
break
|
|
46
|
+
case 'med':
|
|
47
|
+
transition.duration = 800
|
|
48
|
+
break
|
|
49
|
+
case 'fast':
|
|
50
|
+
transition.duration = 500
|
|
51
|
+
break
|
|
52
|
+
default:
|
|
53
|
+
transition.duration = 1000
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (attrs.advClick === '0' && attrs.advTm) {
|
|
59
|
+
transition.autoNextAfter = parseInt(attrs.advTm, 10)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const effectRegex = /^(p|p\d{2}):/
|
|
63
|
+
for (const key in transitionNode) {
|
|
64
|
+
if (key !== 'attrs' && effectRegex.test(key)) {
|
|
65
|
+
const effectNode = transitionNode[key]
|
|
66
|
+
transition.type = key.substring(key.indexOf(':') + 1)
|
|
67
|
+
|
|
68
|
+
if (effectNode && effectNode.attrs) {
|
|
69
|
+
const effectAttrs = effectNode.attrs
|
|
70
|
+
|
|
71
|
+
if (effectAttrs.dur && !isNaN(parseInt(effectAttrs.dur, 10))) {
|
|
72
|
+
if (!durationFound) transition.duration = parseInt(effectAttrs.dur, 10)
|
|
73
|
+
}
|
|
74
|
+
if (effectAttrs.dir) transition.direction = effectAttrs.dir
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return transition
|
|
81
|
+
}
|
package/src/pptxtojson.js
CHANGED
|
@@ -13,6 +13,7 @@ import { getTableBorders, getTableCellParams, getTableRowParams } from './table'
|
|
|
13
13
|
import { RATIO_EMUs_Points } from './constants'
|
|
14
14
|
import { findOMath, latexFormart, parseOMath } from './math'
|
|
15
15
|
import { getShapePath } from './shapePath'
|
|
16
|
+
import { parseTransition, findTransitionNode } from './animation'
|
|
16
17
|
|
|
17
18
|
export async function parse(file) {
|
|
18
19
|
const slides = []
|
|
@@ -274,11 +275,18 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
|
|
|
274
275
|
}
|
|
275
276
|
}
|
|
276
277
|
|
|
278
|
+
let transitionNode = findTransitionNode(slideContent, 'p:sld')
|
|
279
|
+
if (!transitionNode) transitionNode = findTransitionNode(slideLayoutContent, 'p:sldLayout')
|
|
280
|
+
if (!transitionNode) transitionNode = findTransitionNode(slideMasterContent, 'p:sldMaster')
|
|
281
|
+
|
|
282
|
+
const transition = parseTransition(transitionNode)
|
|
283
|
+
|
|
277
284
|
return {
|
|
278
285
|
fill,
|
|
279
286
|
elements,
|
|
280
287
|
layoutElements,
|
|
281
288
|
note,
|
|
289
|
+
transition,
|
|
282
290
|
}
|
|
283
291
|
}
|
|
284
292
|
|
package/src/shape.js
CHANGED
|
@@ -50,7 +50,6 @@ export function getCustomShapePath(custShapType, w, h) {
|
|
|
50
50
|
const arcToNodes = pathNodes['a:arcTo']
|
|
51
51
|
let closeNode = getTextByPathList(pathNodes, ['a:close'])
|
|
52
52
|
if (!Array.isArray(moveToNode)) moveToNode = [moveToNode]
|
|
53
|
-
if (!Array.isArray(lnToNodes)) lnToNodes = [lnToNodes]
|
|
54
53
|
|
|
55
54
|
const multiSapeAry = []
|
|
56
55
|
if (moveToNode.length > 0) {
|
|
@@ -72,6 +71,7 @@ export function getCustomShapePath(custShapType, w, h) {
|
|
|
72
71
|
}
|
|
73
72
|
})
|
|
74
73
|
if (lnToNodes) {
|
|
74
|
+
if (!Array.isArray(lnToNodes)) lnToNodes = [lnToNodes]
|
|
75
75
|
Object.keys(lnToNodes).forEach(key => {
|
|
76
76
|
const lnToPtNode = lnToNodes[key]['a:pt']
|
|
77
77
|
if (lnToPtNode) {
|