@openmaic/importer
v0.1.0
Published
A javascript tool for parsing .pptx file
Readme
🎨 pptxtojson
⚠️ 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文件解析工具的最大区别在于:
- 直接运行在浏览器端;
- 解析结果是可读的 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)
})Node.js(实验性,1.5.0以上版本)
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()🚀 进阶用法:PPTX → OpenMAIC 画布 Slide[]
parse() 只完成「PPTX → 可读 JSON」这一步,元素的字段还是 PPT 语义(type: 'text' | 'shape' | 'image' …、单位 pt、媒体是 base64 / blob: URL)。
如果你需要的最终产物是 OpenMAIC 画布直接可渲染的 Slide[](PPTTextElement / PPTShapeElement / PPTImageElement 等、单位 px、媒体替换为 OSS URL),用 importPptx()。
🔑 API 一览
| 导出 | 类型 | 用途 |
|------|------|------|
| 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[];📦 用法
1. 不传 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://… URL2. 传 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 URL3. 已经用 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 路并发上传图片,避免一次性打满网络。
💥 错误处理
- 单个媒体上传失败 → transform 内部
.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 用端点近似,可能略小。 |
🧪 在 Next.js (Turbopack) 里用
maic-importer 源码依赖 pdfjs-dist,其动态 require() 模式会被 Turbopack 拒绝。OpenMAIC 的做法:
pnpm run build把整个包(含 importPptx)打成dist/。scripts/sync-maic-importer.mjs把dist/复制到public/vendor/maic-importer/。- 在客户端组件里用静态 URL 动态 import,bundler 完全看不到:
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- 宽度
width - 高度
height
- 宽度
幻灯片页面
slides页面备注
note页面背景填充(颜色、图片、渐变、图案)
fill- 纯色填充
type='color' - 图片填充
type='image' - 渐变填充
type='gradient' - 图案填充
type='pattern'
- 纯色填充
页面切换动画
transition- 类型
type - 持续时间
duration - 方向
direction
- 类型
页面内元素
elements/ 母版元素layoutElements文字
- 类型
type='text' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 边框颜色
borderColor - 边框宽度
borderWidth - 边框类型(实线、点线、虚线)
borderType - 非实线边框样式
borderStrokeDasharray - 阴影
shadow - 填充(颜色、图片、渐变、图案)
fill - 内容文字(HTML富文本:字体、字号、颜色、渐变、下划线、删除线、斜体、加粗、阴影、角标、超链接)
content - 垂直翻转
isFlipV - 水平翻转
isFlipH - 旋转角度
rotate - 垂直对齐方向
vAlign - 是否为竖向文本
isVertical - 元素名
name - 自动调整大小
autoFit- 类型
typeshape:文本框高度会根据文本内容自动调整text:文本框大小固定,字号会自动缩放以适应文本框(注:autoFit不存在时,也会固定文本框大小,但字号不会缩放)
- 字体缩放比例(type='text'专有,默认为1)
fontScale
- 类型
- 超链接
link
- 类型
图片
- 类型
type='image' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 边框颜色
borderColor - 边框宽度
borderWidth - 边框类型(实线、点线、虚线)
borderType - 非实线边框样式
borderStrokeDasharray - 裁剪形状
geom - 裁剪范围
rect - 图片地址(base64)
src - 旋转角度
rotate - 滤镜
filters - 超链接
link
- 类型
形状
- 类型
type='shape' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 边框颜色
borderColor - 边框宽度
borderWidth - 边框类型(实线、点线、虚线)
borderType - 非实线边框样式
borderStrokeDasharray - 阴影
shadow - 填充(颜色、图片、渐变、图案)
fill - 内容文字(HTML富文本,与文字元素一致)
content - 垂直翻转
isFlipV - 水平翻转
isFlipH - 旋转角度
rotate - 形状类型
shapType - 垂直对齐方向
vAlign - 形状路径
path - 形状调整参数
keypoints - 元素名
name - 自动调整大小
autoFit - 超链接
link
- 类型
表格
- 类型
type='table' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 边框(4边)
borders - 表格数据
data - 行高
rowHeights - 列宽
colWidths
- 类型
图表
- 类型
type='chart' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 图表数据
data - 图表主题色
colors - 图表类型
chartType - 柱状图方向
barDir - 是否带数据标记
marker - 环形图尺寸
holeSize - 分组模式
grouping - 图表样式
style
- 类型
视频
- 类型
type='video' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 视频blob
blob - 视频src
src
- 类型
音频
- 类型
type='audio' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 音频blob
blob
- 类型
公式
- 类型
type='math' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 公式图片
picBase64 - LaTeX表达式(仅支持常见结构)
latex - 文本(文本和公式混排时存在)
text
- 类型
Smart图
- 类型
type='diagram' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 子元素集合
elements - 文本列表(Smart图中的文字内容清单,仅在elements无法解析时存在)
textList
- 类型
多元素组合
- 类型
type='group' - 水平坐标
left - 垂直坐标
top - 宽度
width - 高度
height - 子元素集合
elements
- 类型
更多类型请参考 👇
https://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
