返回 aippt
ppt2json.html
根目录 / ppt2json.html
1 <!DOCTYPE html>
2 <html lang="zh">
3 <head>
4 <meta charset="UTF-8">
5 <title data-i18n="ppt2json_title">ppt2json</title>
6 <link rel="stylesheet" href="static/jsoneditor.min.css">
7 <script src="static/jsoneditor.min.js"></script>
8 <style>
9 ::-webkit-scrollbar {
10 width: 5px;
11 height: 5px;
12 background-color: #fff;
13 }
14 ::-webkit-scrollbar-thumb {
15 background-color: #c1c1c1;
16 }
17 .docmee {
18 z-index: 9999;
19 background-image: linear-gradient(to left, rgb(107, 90, 196), rgb(96, 84, 189));
20 background-clip: text;
21 color: transparent;
22 font-weight: bold;
23 display: inline-block;
24 font-size: 1.125rem;
25 line-height: 1.75rem;
26 user-select: none;
27 cursor: pointer;
28 position: fixed;
29 top: 10px;
30 left: 15px;
31 }
32 #json {
33 padding: 10px;
34 }
35 .jsoneditor-mode-view {
36 border: 2px solid #ddd;
37 }
38 .jsoneditor-menu {
39 display: none;
40 }
41 .jsoneditor-navigation-bar {
42 display: none;
43 }
44 .jsoneditor-outer.has-nav-bar.has-main-menu-bar {
45 margin-top: 0 !important;
46 padding-top: 0 !important;
47 }
48 #pageList {
49 list-style-type: none;
50 }
51 #pageList li {
52 display: inline;
53 padding: 1px 4px;
54 margin-left: 8px;
55 cursor: pointer;
56 }
57 .selected {
58 color: green;
59 border: 1px solid green;
60 }
61 #container > svg {
62 border: 1px solid #ccc;
63 }
64 .lang-switch {
65 position: fixed;
66 top: 10px;
67 right: 15px;
68 z-index: 9999;
69 cursor: pointer;
70 font-size: 14px;
71 color: #666;
72 user-select: none;
73 }
74 .lang-switch:hover {
75 color: #491ff8;
76 }
77 </style>
78 </head>
79 <body style="margin: 20px">
80 <a href="https://docmee.cn" target="_blank" class="docmee" data-i18n="brand_title" title="进入文多多AiPPT官网">文多多 AiPPT</a>
81 <div class="lang-switch" onclick="toggleLanguage()">
82 <span id="lang-text">EN</span>
83 </div>
84 <div style="margin-top: 50px;">
85 <input id="file" type="file" accept="application/vnd.openxmlformats-officedocument.presentationml.presentation, application/json" onclick="document.getElementById('file').value = ''" onchange="ppt2json()" />
86 <span id="refresh_json" style="color: #999;cursor: pointer;margin-right: 15px;" data-i18n-title="refresh_json_title" data-i18n="refresh_json" title="刷新JSON数据" onclick="refreshJson()">刷新</span>
87 <span id="download_json" style="color: #317ef3;display: none;cursor: pointer;margin-right: 15px;" data-i18n-title="download_json_title" data-i18n="download_json" title="下载json数据" onclick="downloadJson()">下载json文件</span>
88 <span id="download_pptx" style="color: #317ef3;display: none;cursor: pointer;" data-i18n-title="render_download_pptx_title" data-i18n="render_download_pptx" title="渲染pptx并下载" onclick="json2ppt()">渲染pptx并下载</span>
89 </div>
90 <div id="json" style="margin-top: 20px"></div>
91 <div>
92 <ul id="pageList"></ul>
93 <span data-i18n="edit_mode_label">编辑模式:</span>
94 <input id="mode_checkbox" type="checkbox" checked onchange="changeMode()" />
95 <select id="insert_element" style="display: none;margin-left: 10px" onchange="insertElement(this.value.split(',')[0], this.value.split(',')[1])">
96 <option value="" data-i18n="insert_element_title">-- 插入元素 --</option>
97 <option value="text,title1" data-i18n="insert_title1">插入大标题</option>
98 <option value="text,title2" data-i18n="insert_title2">插入副标题</option>
99 <option value="text,content" data-i18n="insert_content">插入正文文本</option>
100 <option value="image" data-i18n="insert_image">插入图片</option>
101 <option value="geometry" data-i18n="insert_geometry">插入随机形状</option>
102 <option value="table" data-i18n="insert_table">插入表格</option>
103 <option value="chart,bar" data-i18n="insert_bar_chart">插入柱状图</option>
104 <option value="chart,pie" data-i18n="insert_pie_chart">插入饼图</option>
105 <option value="chart,doughnut" data-i18n="insert_doughnut_chart">插入环形图</option>
106 <option value="chart,line" data-i18n="insert_line_chart">插入折线图</option>
107 </select>
108 </div>
109 <div id="container" style="margin-top: 10px;">
110 <svg id="ppt_svg"></svg>
111 </div>
112 </body>
113 <script src="static/i18n.js"></script>
114 <script src="static/chart.js"></script>
115 <script src="static/geometry.js"></script>
116 <script src="static/ppt2svg.js"></script>
117 <script src="static/element.js"></script>
118 <script>
119 const apiKey = '4u2Fo50Alk1ym2Os'
120 var editor = new JSONEditor(document.querySelector('#json'), {}, {})
121
122 var pptxObj = null
123 var selectIdx = 0
124 var mTimer = null
125 var ppt2svg = new Ppt2Svg('ppt_svg', 960, 540)
126
127 function ppt2json() {
128 let file = document.getElementById('file').files[0]
129 if (file.name.endsWith('.json')) {
130 let reader = new FileReader()
131 reader.onload = function(e) {
132 let json = e.target.result
133 let pptxObj = JSON.parse(json)
134 editor.set(pptxObj)
135 drawPageList(pptxObj)
136 }
137 reader.onerror = function(e) {
138 editor.set({ message: t('read_error') + e.target.error })
139 }
140 reader.readAsText(file)
141 return
142 }
143 if (file.size > 1048576 * 50) {
144 alert(t('file_too_large'))
145 return
146 }
147 if (file) {
148 let formData = new FormData()
149 formData.append('file', file)
150 let xhr = new XMLHttpRequest()
151 xhr.open('POST', 'https://docmee.cn/api/public/ppt/ppt2json?apiKey=' + apiKey)
152 editor.set({ message: t('parsing_msg') })
153 xhr.onreadystatechange = function () {
154 if (xhr.readyState === 4) {
155 if (xhr.status === 200) {
156 // 解析
157 let pptxObj = JSON.parse(xhr.responseText)
158 if (pptxObj.code == -1 && pptxObj.message) {
159 alert(pptxObj.message)
160 return
161 }
162 editor.set(pptxObj)
163 drawPageList(pptxObj)
164 } else {
165 editor.set({ message: t('http_status') + xhr.status })
166 }
167 }
168 }
169 xhr.send(formData)
170 } else {
171 editor.set({ message: t('please_select_file') })
172 }
173 }
174
175 function json2ppt() {
176 if (!pptxObj) {
177 return
178 }
179 // 渲染pptx并下载
180 const downloadBtnText = t('render_download_pptx')
181 if (download_pptx.innerText !== downloadBtnText) {
182 return
183 }
184 download_pptx.innerText = t('rendering_pptx')
185 let xhr = new XMLHttpRequest()
186 xhr.responseType = 'blob'
187 xhr.open('POST', 'https://docmee.cn/api/public/ppt/json2ppt?apiKey=' + apiKey)
188 xhr.setRequestHeader('Content-Type', 'application/json')
189 xhr.onload = function() {
190 if (this.status == 200) {
191 download_pptx.innerText = t('render_download_pptx')
192 let blob = this.response
193 if (blob.type == 'application/json') {
194 const reader = new FileReader()
195 reader.onload = function(e) {
196 let json = JSON.parse(e.target.result)
197 alert(t('alert_download_error') + json.message)
198 }
199 reader.readAsText(blob)
200 return
201 }
202 let a = document.createElement('a')
203 a.href = window.URL.createObjectURL(blob)
204 let name = 'download'
205 let file = document.getElementById('file').files[0]
206 if (file) {
207 name = file.name.replace('.pptx', '').replace('.json', '')
208 }
209 a.download = name + '.pptx'
210 a.click()
211 }
212 }
213 xhr.send(JSON.stringify(pptxObj))
214 }
215
216 function downloadJson() {
217 if (!pptxObj) {
218 return
219 }
220 let urlObject = window.URL || window.webkitURL || window
221 let export_blob = new Blob([JSON.stringify(pptxObj)])
222 let save_link = document.createElement('a')
223 save_link.href = urlObject.createObjectURL(export_blob)
224 let name = 'download'
225 let file = document.getElementById('file').files[0]
226 if (file) {
227 name = file.name.replace('.pptx', '').replace('.json', '')
228 }
229 save_link.download = name + '.json'
230 save_link.click()
231 }
232
233 function drawPageList(_pptxObj) {
234 counter = 0
235 selectIdx = 0
236 imageCache = {}
237 pptxObj = _pptxObj
238 document.getElementById('download_json').style.display = 'inline'
239 document.getElementById('download_pptx').style.display = 'inline'
240 document.getElementById('insert_element').style.display = document.getElementById('mode_checkbox').checked ? 'inline-block' : 'none'
241 let pageList = document.getElementById('pageList')
242 pageList.innerHTML = '';
243 for (let i = 0; i < pptxObj.pages.length; i++) {
244 let page = pptxObj.pages[i]
245 let li = document.createElement('li')
246 li.id = 'li_' + i
247 li.innerText = i + 1 + ''
248 li.addEventListener('click', () => {
249 document.getElementById('li_' + selectIdx).classList.remove('selected')
250 selectIdx = i
251 li.classList.add('selected')
252 ppt2svg.drawPptx(pptxObj, selectIdx)
253 })
254 if (i == selectIdx) {
255 li.classList.add('selected')
256 }
257 pageList.appendChild(li)
258 }
259 ppt2svg.drawPptx(pptxObj, selectIdx)
260 }
261
262 function refreshJson() {
263 pptxObj = editor.get()
264 ppt2svg.drawPptx(pptxObj, selectIdx)
265 }
266
267 function changeMode() {
268 ppt2svg.setMode(document.getElementById('mode_checkbox').checked ? 'edit' : 'view')
269 document.getElementById('insert_element').style.display = pptxObj && document.getElementById('mode_checkbox').checked ? 'inline-block' : 'none'
270 }
271
272 ppt2svg.onchange = (elementObj) => {
273 editor.set(pptxObj)
274 }
275
276 changeMode()
277
278 function insertElement(type, subType) {
279 let element = null
280 if (type == 'text') {
281 // 文本
282 element = createTextBox(subType)
283 } else if (type == 'image') {
284 // 图片
285 let input = document.createElement('input')
286 input.type = 'file'
287 input.accept = '.jpg,.png'
288 input.onchange = function(e1) {
289 const file = e1.target.files[0]
290 if (!file) return
291 const reader = new FileReader()
292 reader.onload = function(e2) {
293 const base64 = e2.target.result
294 const img = new Image()
295 img.src = base64
296 img.onload = function() {
297 const width = img.width > 300 ? 300 : img.width
298 const height = img.height * (width / img.width)
299 element = createImage(base64, width, height)
300 pptxObj.pages[selectIdx].children.push(element)
301 ppt2svg.drawPptx(pptxObj, selectIdx)
302 }
303 }
304 reader.readAsDataURL(file)
305 }
306 input.click()
307 } else if (type == 'geometry') {
308 // 形状
309 let geometryName = subType
310 if (!geometryName) {
311 // 随机形状
312 let keys = Object.keys(geometryMap)
313 geometryName = keys[Math.floor(Math.random() * keys.length)]
314 console.log('geometry', geometryName)
315 }
316 element = createGeometry(geometryName)
317 } else if (type == 'table') {
318 // 表格
319 let rowColumnDataList = [
320 [t('table_cell').replace('{row}', '1').replace('{col}', '1'), t('table_cell').replace('{row}', '1').replace('{col}', '2'), t('table_cell').replace('{row}', '1').replace('{col}', '3')],
321 [t('table_cell').replace('{row}', '2').replace('{col}', '1'), t('table_cell').replace('{row}', '2').replace('{col}', '2'), t('table_cell').replace('{row}', '2').replace('{col}', '3')],
322 [t('table_cell').replace('{row}', '3').replace('{col}', '1'), t('table_cell').replace('{row}', '3').replace('{col}', '2'), t('table_cell').replace('{row}', '3').replace('{col}', '3')]
323 ]
324 element = createTable(rowColumnDataList)
325 } else if (type == 'chart') {
326 // 图表
327 let rowColumnDataList = []
328 if (subType == 'pie' || subType == 'doughnut') {
329 rowColumnDataList = [[' ', t('chart_sales')], [t('chart_q1'), '8.2'], [t('chart_q2'), '3.2'], [t('chart_q3'), '1.4'], [t('chart_q4'), '1.2']]
330 } else {
331 rowColumnDataList = [
332 [' ', t('chart_series').replace('{n}', '1'), t('chart_series').replace('{n}', '2'), t('chart_series').replace('{n}', '3')],
333 [t('chart_category').replace('{n}', '1'), '4.3', '2.4', '2'],
334 [t('chart_category').replace('{n}', '2'), '2.5', '4.4', '2'],
335 [t('chart_category').replace('{n}', '3'), '3.5', '1.8', '3'],
336 [t('chart_category').replace('{n}', '4'), '4.5', '2.8', '5']
337 ]
338 }
339 element = createChart(t('chart_title'), subType, rowColumnDataList)
340 }
341 if (element) {
342 pptxObj.pages[selectIdx].children.push(element)
343 ppt2svg.drawPptx(pptxObj, selectIdx)
344 }
345 document.getElementById('insert_element').value = ''
346 }
347
348 // 初始化国际化
349 applyLanguage()
350 </script>
351 </html>
352
352 lines HTML