← Về thư mục
📄 / / projects / tech / OpenMAIC / app / packages / @openmaic / importer / README.md

🎨 pptxtojson

⚠️ Browser-only. This package parses .pptx in 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.

stars forks license issue gitee gitcode

一个运行在浏览器中,可以将 .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/

国内镜像(定期同步):Gitee、GitCode

🎯 注意事项

⚒️ 使用场景

本仓库诞生于项目 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://… URL

2. 传 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

3. 已经用 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 路并发上传图片,避免一次性打满网络。

💥 错误处理

⚠️ 当前限制

模块 状态 影响
字体白名单(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 的做法:

  1. pnpm run build 把整个包(含 importPptx)打成 dist/。
  2. scripts/sync-maic-importer.mjs 把 dist/ 复制到 public/vendor/maic-importer/。
  3. 在客户端组件里用静态 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 导入功能失效。

两道防护已就位:

另注: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
    }
}

📕 完整功能支持

更多类型请参考 👇

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