⚠️ Browser-only. This package parses
.pptxin the browser and depends on DOM APIs (XMLHttpRequest,DOMMatrix,Path2D, via a browser-targeted pdf.js build). It is built for browser / bundler (webpack, Vite, Next client) environments — importing it in a pure Node.js process throws at load. Use it from client code, not from a Node server runtime.
一个运行在浏览器中,可以将 .pptx 文件转为可读的 JSON 数据的 JavaScript 库。
实现说明:主入口为 TypeScript 实现(src/),构建产物为 dist/;输出 JSON 格式与文档及原示例一致。原 JavaScript 实现位于 src1/,仅作输出格式与逻辑参考,不参与构建。
OpenMAIC 扩展:在 parse()(PPTX → 中间 JSON)之上新增了完整的 import pipeline —— importPptx() 直接吃 .pptx 文件、吐 OpenMAIC 画布需要的 Slide[],并可选地把所有 base64 图片 / blob 媒体转交给你提供的 OSS 上传函数。详见下方「🚀 进阶用法」。
与其他的pptx文件解析工具的最大区别在于: 1. 直接运行在浏览器端; 2. 解析结果是可读的 JSON 数据,而不仅仅是把 XML 文件内容原样翻译成难以理解的 JSON。
在线DEMO:https://pipipi-pikachu.github.io/pptxtojson/
本仓库诞生于项目 PPTist ,希望为其“导入 .pptx 文件功能”提供一个参考示例。不过就目前来说,解析出来的PPT信息与源文件在样式上还是存在差异。
但如果你只是需要提取PPT文件的文本内容、媒体资源信息、结构信息等,或者对排版/样式精准度没有特别高的要求,那么 pptxtojson 可能会对你有帮助。
输出的JSON中,所有数值长度值单位都为pt(point)
注意:在0.x版本中,所有输出的长度值单位都是px(像素)
npm install pptxtojson
<input type="file" accept="application/vnd.openxmlformats-officedocument.presentationml.presentation"/>
import { parse } from 'pptxtojson'
document.querySelector('input').addEventListener('change', evt => {
const file = evt.target.files[0]
const reader = new FileReader()
reader.onload = async e => {
const json = await parse(e.target.result)
console.log(json)
}
reader.readAsArrayBuffer(file)
})
const pptxtojson = require('pptxtojson/dist/index.cjs')
const fs = require('fs')
async function func() {
const buffer = fs.readFileSync('test.pptx')
const json = await pptxtojson.parse(buffer.buffer)
console.log(json)
}
func()
Slide[]parse() 只完成「PPTX → 可读 JSON」这一步,元素的字段还是 PPT 语义(type: 'text' | 'shape' | 'image' …、单位 pt、媒体是 base64 / blob: URL)。
如果你需要的最终产物是 OpenMAIC 画布直接可渲染的 Slide[](PPTTextElement / PPTShapeElement / PPTImageElement 等、单位 px、媒体替换为 OSS URL),用 importPptx()。
| 导出 | 类型 | 用途 |
|---|---|---|
importPptx(input, options?) |
(File \| Blob \| ArrayBuffer, ImportPptxOptions?) => Promise<Slide[]> |
一站式:.pptx → Slide[],等所有上传 settle 后再 resolve |
parsedToSlides(json, options?) |
(Output, ImportPptxOptions?) => Promise<Slide[]> |
只做「中间 JSON → Slide[]」,给已经用 parse() 拿到 JSON 的场景 |
normalizeImportedSlides(slides) |
(Slide[]) => Slide[] |
DSL 合同边界:补默认值、丢弃无法修复的元素(console.warn 上报)。parsedToSlides / importPptx 已自动应用;直接调用 transformParsedToSlides 的消费方需要自己跑一遍以获得相同的输出契约 |
OssUpload |
(blob: Blob, filename: string, dir?: string) => Promise<string> |
上传回调签名 |
ImportPptxOptions |
{ upload?: OssUpload } |
选项对象 |
CanvasSlide |
OpenMAIC Slide 类型 |
用于消费方做类型注解 |
importPptx 内部就是 parse(buffer, { mediaMode: 'base64' }) + parsedToSlides(...),两者任选其一即可。
import {
importPptx,
parsedToSlides,
normalizeImportedSlides,
type OssUpload,
type ImportPptxOptions,
type CanvasSlide,
} from '@openmaic/importer';
export type OssUpload = (
blob: Blob,
filename: string,
dir?: string,
) => Promise<string>;
export interface ImportPptxOptions {
/**
* 上传媒体(图片 / 音频 / 视频)到远程存储并返回公网 URL。
* - 提供:所有 base64 图片会先转成 Blob,再调用此函数,URL 写回 slide。
* - 不提供:图片保留 base64 data URL;音视频保留临时 `blob:` URL(仅当前 tab 有效)。
*/
upload?: OssUpload;
}
export function importPptx(
input: File | Blob | ArrayBuffer,
options?: ImportPptxOptions,
): Promise<CanvasSlide[]>;
export function parsedToSlides(
json: Output,
options?: ImportPptxOptions,
): Promise<CanvasSlide[]>;
export function normalizeImportedSlides(slides: CanvasSlide[]): CanvasSlide[];
upload —— 本地预览 / 调试媒体留在内存,slide 可以直接在当前 tab 里渲染,但刷新就失效(音视频)/ JSON 体积大(图片)。
import { importPptx } from '@openmaic/importer';
const slides = await importPptx(file);
// slides[*].elements 里的 image.src 还是 data:image/png;base64,…
// audio/video.src 是 blob:http://… URL
upload —— 生产场景把媒体上传到你自己的 OSS / classroom-media / S3 / 任意存储,slide 里只剩 URL:
import { importPptx, type OssUpload } from '@openmaic/importer';
const upload: OssUpload = async (blob, filename, dir) => {
const form = new FormData();
form.append('file', blob, filename);
form.append('dir', dir ?? 'pptx-import');
const res = await fetch('/api/upload', { method: 'POST', body: form });
if (!res.ok) throw new Error(`upload failed: ${res.status}`);
const { url } = await res.json();
return url; // ← 必须返回最终可访问 URL
};
const slides = await importPptx(file, { upload });
// 此时 slides[*].elements 里的 src 全是 OSS URL
parse() 拿到 JSON 时import { parse, parsedToSlides } from '@openmaic/importer';
const json = await parse(buffer, { mediaMode: 'base64' });
const slides = await parsedToSlides(json, { upload });
⚠️ 必须用
mediaMode: 'base64'。blob模式产出的 URL 只在当前 tab 有效,无法上传后跨页面使用。
upload 回调被调用的时机| 元素类型 | 源数据 | filename 示例 | dir |
|---|---|---|---|
| 背景图片 | base64 → Blob | background_<timestamp>.png |
a2m |
| 图片元素 | base64 → Blob | image_<timestamp>.png |
a2m |
| 数学公式渲染图 | base64 → Blob | math_<timestamp>.png |
a2m |
| 形状的图案填充 | base64 → Blob | pattern_<timestamp>.png |
a2m |
| 音频 | 直接是 Blob | audio_<timestamp>.mp3 |
a2m/audio |
| 视频 | 直接是 Blob | video_<timestamp>.mp4 |
a2m/video |
并发:内部用 6 路并发上传图片,避免一次性打满网络。
.catch 吞掉错误(控制台 console.error),该元素的 src 仍是原始 base64 / 空字符串。整体 import 不会失败。parse() 解析失败(坏文件等)→ importPptx 直接 throw,调用方自己 try/catch。Promise.allSettled 等所有上传 settle 后才 resolve,调用方拿到的 Slide[] 不需要再 await 任何东西。| 模块 | 状态 | 影响 |
|---|---|---|
字体白名单(resolveFont) |
stub,透传 | 中文字体保留原名,浏览器找不到字体时会回退到默认。后续可移植 PPTist 字体替换逻辑。 |
视频编码检测(videoCodec) |
stub,永远视为支持 | HEVC 等浏览器不支持的编码会变成坏的 <video>,而不是降级到占位图标。 |
SVG path bbox(svgPathParser) |
自实现 tokenizer | 标准命令(M L H V C S Q T A Z 大小写)都覆盖;弧线 bbox 用端点近似,可能略小。 |
maic-importer 源码依赖 pdfjs-dist,其动态 require() 模式会被 Turbopack 拒绝。OpenMAIC 的做法:
pnpm run build 把整个包(含 importPptx)打成 dist/。scripts/sync-maic-importer.mjs 把 dist/ 复制到 public/vendor/maic-importer/。import type * as PptxtojsonPro from '@openmaic/importer';
const mod = (await import(
/* webpackIgnore: true */
/* turbopackIgnore: true */
/* @vite-ignore */
'/vendor/maic-importer/index.js'
)) as typeof PptxtojsonPro;
const slides = await mod.importPptx(file, { upload });
类型仍走 workspace 包,IntelliSense 不丢。
参考:lib/import/use-import-pptx.ts。
public/vendor/maic-importer/ 是 gitignored 的构建产物,不进仓库,由 postinstall
现生成(pnpm --filter @openmaic/importer build + node scripts/sync-maic-importer.mjs)。
因此部署流水线必须执行 postinstall(或显式跑这两步),否则运行时
/vendor/maic-importer/index.js 会 404,PPTX 导入功能失效。
两道防护已就位:
build 脚本前置 node scripts/assert-vendor-maic-importer.mjs,
若 vendor 产物缺失则构建直接失败并给出修复提示,避免把必崩版本部署上线。use-import-pptx.ts 在动态 import 前先 HEAD 预检该 URL,
404 时抛出明确错误并提示 import.error.parserUnavailable,而不是把 404 HTML
当 JS 解析出诡异的 SyntaxError。另注:
git pull后若未重新pnpm install,workspace 类型会更新但 URL 加载的 仍是旧dist,二者可能静默漂移——拉取后请重新安装。
parse(),未走 import pipeline){
"slides": [
{
"fill": {
"type": "color",
"value": "#FF0000"
},
"elements": [
{
"left": 0,
"top": 0,
"width": 72,
"height": 72,
"borderColor": "#1F4E79",
"borderWidth": 1,
"borderType": "solid",
"borderStrokeDasharray": 0,
"fill": {
"type": "color",
"value": "#FF0000"
},
"content": "<p style=\"text-align: center;\"><span style=\"font-size: 18pt;font-family: Calibri;\">TEST</span></p>",
"isFlipV": false,
"isFlipH": false,
"rotate": 0,
"vAlign": "mid",
"name": "矩形 1",
"type": "shape",
"shapType": "rect"
},
// more...
],
"layoutElements": [
// more...
],
"note": "演讲者备注内容..."
},
// more...
],
"themeColors": ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#5B9BD5', '#70AD47'],
"size": {
"width": 960,
"height": 540
}
}
幻灯片主题色 themeColors
幻灯片尺寸 size
widthheight幻灯片页面 slides
页面备注 note
页面背景填充(颜色、图片、渐变、图案) fill
type='color'type='image'type='gradient'type='pattern'页面切换动画 transition
typedurationdirection页面内元素 elements / 母版元素 layoutElements
文字
type='text'lefttopwidthheightborderColorborderWidthborderTypeborderStrokeDasharrayshadowfillcontentisFlipVisFlipHrotatevAlignisVerticalnameautoFittypeshape:文本框高度会根据文本内容自动调整text:文本框大小固定,字号会自动缩放以适应文本框(注:autoFit不存在时,也会固定文本框大小,但字号不会缩放)fontScalelink图片
type='image'lefttopwidthheightborderColorborderWidthborderTypeborderStrokeDasharraygeomrectsrcrotatefilterslink形状
type='shape'lefttopwidthheightborderColorborderWidthborderTypeborderStrokeDasharrayshadowfillcontentisFlipVisFlipHrotateshapTypevAlignpathkeypointsnameautoFitlink表格
type='table'lefttopwidthheightbordersdatarowHeightscolWidths图表
type='chart'lefttopwidthheightdatacolorschartTypebarDirmarkerholeSizegroupingstyle视频
type='video'lefttopwidthheightblobsrc音频
type='audio'lefttopwidthheightblob公式
type='math'lefttopwidthheightpicBase64latextextSmart图
type='diagram'lefttopwidthheightelementstextList多元素组合
type='group'lefttopwidthheightelementshttps://github.com/pipipi-pikachu/pptxtojson/blob/master/dist/index.d.ts
本仓库大量参考了 PPTX2HTML 和 PPTXjs 的实现。
与它们不同的是:PPTX2HTML 和 PPTXjs 是将PPT文件转换为能够运行的 HTML 页面,而 pptxtojson 做的是将PPT文件转换为干净的 JSON 数据,且在原有基础上进行了大量优化补充(包括代码质量和提取信息的完整度和准确度)。
MIT License | Copyright © 2020-PRESENT pipipi-pikachu