pptx- 递归器
一个高纯度,浏览器本地化的 PPTX 渲染器,用于解析 Office Open XML ().pptx)文件并制作幻灯片作为HTML/SVG DOM.
支持形状,文本,图像,表格,图表,SmartArt,有边框的OMML等方程,组,背景,梯度,图案填充,有边框的普通-形状外影和反射,有边框的静态绘图ML 3D,和OOXML色彩继承. 渲染忠实取决于源特性和可用的预览数据;参见下方的支持边界.
渲染示例
具有图表、文本样式、形状和SmartArt的复杂幻灯片——PowerPoint地面真相与浏览器交接的输出:
| PowerPoint( 回合真理) | pptx- 投降者 (浏览器) |
|---|---|
![]() |
![]() |
视觉回归测试
可视回归套件比较所选形状,SmartArt,填充/中程,文本,表格,以及图表大小写与PowerPoint输出. 每个API评价记录其PPTX的渲染器修订,浏览器,可选字体配置,以及SHA-256指纹和地真能,从而可以将结果与同一份投入进行比较. 符合条件的单图笛卡尔幻灯片也暴露出图案捆绑,数据链接,系列-彩色,以及区域诊断,因此页面白空无法隐藏缺失或被取代的系列. 通过汇总或本地得分并不能确定语义正确性或PowerPoint完全对等;结构断言和定向浏览器/内测是对衡量标准的补充.
E2E评价仪表板:侧面地面真假对输出时使用SSIM,颜色直方图,以及IoU度量衡每张幻灯片.
地面真理二进制( PPTX/ PDF/ PNG) 保留在被忽略
test/e2e/testdata/树。 跟踪案件定义和覆盖元数据可以复制。 生成形状/ SmartArt 公司scripts/one_shot_full_ground_truth.py或有重点的案文/表/图/合并案件scripts/generate_pypptx_cases.py;同时支持macOS和Windows PowerPoint. 见docs/TESTING.md。 。 。 .
在macOS上,本地出口使用一个被忽略的固定 oracle-runtime 目录和目标
以精确的完整路径呈现。 保持交互式PowerPoint会话的可用性和检查
当自动化失败时等待对话框。 这样可以让本地甲骨文运行到其它打开处
演示文稿,但不将活动窗口作为导出目标.
安装
npm install @aiden0z/pptx-renderer
# or
pnpm add @aiden0z/pptx-renderer
# Optional: only needed for SmartArt / EMF files with embedded PDF fallback previews
pnpm add pdfjs-dist
发展需要20+节点. 运行时间仅为浏览器 .
快速启动
对捆绑者和 npm 应用程序 :
import { PptxViewer, RECOMMENDED_ZIP_LIMITS } from '@aiden0z/pptx-renderer';
const container = document.getElementById('pptx-container')!;
const resp = await fetch('/slides/demo.pptx');
// One-liner: parse, build model, and render
const viewer = await PptxViewer.open(await resp.arrayBuffer(), container, {
zipLimits: RECOMMENDED_ZIP_LIMITS,
listOptions: { windowed: true },
});
对于没有捆绑器的直接浏览器使用,导入独立的浏览器 EME 构建. 这个
条目捆绑 JSZip 和 ECharts,并替换节点样式 process.env 在建设时间检查.
PDF.js 仍是可选的, 仅供 EMF 嵌入式 PDF 后退预览使用 :
<script type="module">
import {
PptxViewer,
RECOMMENDED_ZIP_LIMITS,
} from '/vendor/pptx-renderer/aiden0z-pptx-renderer.browser.es.js';
const container = document.getElementById('pptx-container');
const resp = await fetch('/slides/demo.pptx');
await PptxViewer.open(await resp.arrayBuffer(), container, {
zipLimits: RECOMMENDED_ZIP_LIMITS,
});
</script>
将已出版的软件包中的独立文物复制到应用程序的版本 静态资产。 条目将 JSZip 和 ECHarts 图表类型捆绑在一起 渲染器; PDF.js 仍然是可选的外部资产. 也可以使用被绑定的CDN URL, 但项目并不依赖或通过CDN供应商部署.
对于大型甲板,将窗口安装与点播幻灯片解析及媒体解码相结合:
const viewer = await PptxViewer.open(buffer, container, {
zipLimits: RECOMMENDED_ZIP_LIMITS,
lazySlides: true,
lazyMedia: true,
listOptions: { windowed: true, initialSlides: 4, batchSize: 4 },
});
SmartArt/EMF 预览图像的可选 PDF.js Fallback
PowerPoint常将SmartArt或被粘贴的矢量艺术作为EMF倒置图像存储. 这个 库有 没有 实施完整的EMF/WMF向量渲染器。 它可以让共同 一个EMF包含嵌入式PDF预览或位图预览的办公倒置例.
对于带有嵌入式PDF预览的EMF文件,安装 pdfjs-dist 并传递明确资产
URLs (英语). 这保持了PDF.js的可选性,并避免了强迫每个消费者捆绑包含.
这只用于EMF-PDF倒置预览;普通的PPTX渲染不可用
需要修改代码 .
import { PptxViewer } from '@aiden0z/pptx-renderer';
const pdfjs = {
moduleUrl: new URL('pdfjs-dist/build/pdf.min.mjs', import.meta.url).toString(),
workerUrl: new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString(),
};
const viewer = await PptxViewer.open(buffer, container, {
pdfjs,
});
如果您的应用程序使用了 CDN 或预复制资产, 请将这些字段指向您主机文件 .
设定 pdfjs: false 以完全禁用EMF-PDF倒置渲染。 没有 pdfjs
配置, 渲染器只尝试最优的自动解析和
否则会轻而易举地堕落.
type PdfjsConfig =
| {
moduleUrl?: string;
workerUrl?: string;
}
| false;
对于无捆绑者部署,将两个PDF.js文件复制到您的应用程序的静态中 资产和通过绝对自托管的 URL :
const pdfjs = {
moduleUrl: '/vendor/pdfjs/pdf.min.mjs',
workerUrl: '/vendor/pdfjs/pdf.worker.min.mjs',
};
PDF倒计时使用寿命短的blob工人. 限制性内容安全政策
必须允许选定的资产来源 blob: 例如,工人:
script-src 'self';
worker-src 'self' blob:;
img-src 'self' data: blob:;
如果使用CDN,则将精确的软件包版本标出并仅将来源添加到 (c) 萨赫勒农药委员会的相关指示.
或者对每一步都有更多的控制:
import {
PptxViewer,
parseZip,
buildPresentation,
RECOMMENDED_ZIP_LIMITS,
} from '@aiden0z/pptx-renderer';
const container = document.getElementById('pptx-container')!;
const viewer = new PptxViewer(container, { fitMode: 'contain' });
const files = await parseZip(arrayBuffer, RECOMMENDED_ZIP_LIMITS);
const presentation = buildPresentation(files);
viewer.load(presentation);
await viewer.renderList({ windowed: true, batchSize: 8 });
延迟媒体解压和幻灯片节点解析,直到制作的幻灯片实际需要
它们,使用 parseZipLazyMedia() 通过 { lazySlides: true } 改为 buildPresentation()数字 :
import {
PptxViewer,
parseZipLazyMedia,
buildPresentation,
RECOMMENDED_ZIP_LIMITS,
} from '@aiden0z/pptx-renderer';
const files = await parseZipLazyMedia(arrayBuffer, RECOMMENDED_ZIP_LIMITS);
const presentation = buildPresentation(files, { lazySlides: true });
const viewer = new PptxViewer(container);
viewer.load(presentation);
await viewer.renderList({ windowed: true, initialSlides: 4 });
API 密码
PptxViewer (初级,延伸) EventTarget(中文(简体) )
PptxViewer.open(input, container, options?) - 静态工厂
解析,构建, 和渲染在一个呼叫。 回返a Promise<PptxViewer>。 。 。 .
const viewer = await PptxViewer.open(buffer, container, {
renderMode: 'list', // 'list' (default) | 'slide'
zipLimits: RECOMMENDED_ZIP_LIMITS,
listOptions: { windowed: true, batchSize: 8 },
signal: abortController.signal, // optional AbortSignal
// ...ViewerOptions
});
new PptxViewer(container, options?)
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
width | number | - 说吧 | 容器宽度提示( 自动检测的缩略语) |
fitMode | 'contain' | 'none' | 'contain' | 适配或固定大小 |
zoomPercent | number | 100 | 缩放级别( 10- 400) |
scrollContainer | HTMLElement | - 说吧 | 交叉观察员根滚动容器 |
zipLimits | ZipParseLimits | - 说吧 | ZIP 解析的安全限制( 由 .open()) (中文(简体) ). 使用 RECOMMENDED_ZIP_LIMITS 用于不可信输入. |
lazyMedia | boolean | false | ZIP解析期间, 对带有窗口列表渲染的大型甲板来说最好. |
lazySlides | boolean | false | 按要求分析幻灯片形状/表/图节点。 对带有窗口列表渲染的大型甲板来说最好. |
pdfjs | PdfjsConfig | - 说吧 | EMF-嵌入式 PDF 反向渲染的可选 PDF.js URL,或 false 来禁用它. |
embeddedFontLimits | EmbeddedFontLimits | 安全默认 | 可选的嵌入式font资源限制覆盖 。 Oinduce 字段保留了内置默认值 . |
fontFaces | readonly FontFaceConfig[] | - 说吧 | 为 PPTX 引用但并未嵌入的打字面提供主机字体面. |
onSlideChange | (index) => void | - 说吧 | 简指 slidechange 事件 |
onSlideRendered | (index, element) => void | - 说吧 | 简指 sliderendered 事件 |
onSlideError | (index, error) => void | - 说吧 | 简指 slideerror 事件 |
onSlideUnmounted | (index) => void | - 说吧 | 简指 slideunmounted 事件 |
onNodeError | (nodeId, error) => void | - 说吧 | 简指 nodeerror 事件 |
onRenderStart | () => void | - 说吧 | 简指 renderstart 事件 |
onRenderComplete | () => void | - 说吧 | 简指 rendercomplete 事件 |
所有短手回调也可用为 EventTarget 事件(例如:. viewer.addEventListener('slidechange', ...)) (中文(简体) ).
嵌入式字体解压被默认限制. 信任的应用程序可以提供部分
embeddedFontLimits 覆盖; 参见 业绩指南
用于默认、示例和软处理时间边界.
对于浏览器中找不到的参考字体的甲板,请先提供正则/粗体面相
通过布局 fontFaces。源跟踪浏览器 FontFace API 并且可以是字体字节或一个
支助科 url(...) 资料来源(受主机页面的CSP和CORS政策限制):
const [regular, bold] = await Promise.all([
fetch('/fonts/brand-sans-regular.woff2').then((response) => response.arrayBuffer()),
fetch('/fonts/brand-sans-bold.woff2').then((response) => response.arrayBuffer()),
]);
const viewer = new PptxViewer(container, {
fontFaces: [
{ family: 'Brand Sans', source: regular, descriptors: { weight: '400' } },
{ family: 'Brand Sans', source: bold, descriptors: { weight: '700' } },
],
});
实例方法
viewer.load(presentation); // Load a PresentationData model (no render)
await viewer.renderList({ windowed: true }); // Render all slides in scrollable list
await viewer.renderSlide(0); // Render a single slide (no built-in nav UI)
// Load from binary input (parse → build → render). Cleans up previous state on re-open.
await viewer.open(buffer, { renderMode: 'list', signal: abortController.signal });
await viewer.goToSlide(index); // Jump to slide (0-based), returns Promise<void>
await viewer.goToSlide(index, { behavior: 'instant' }); // Custom ScrollIntoViewOptions (list mode)
await viewer.setZoom(150); // Runtime zoom (10–400)
await viewer.setFitMode('none'); // Switch fit mode
const matches = viewer.searchText('GPU'); // Search parsed model text
const hit = await viewer.highlightSearchResult(matches[0]); // Default node overlay highlight
hit?.dispose();
viewer.clearSearchHighlights(); // Remove active search overlays
// Render a single slide into an external container (React/Vue integration, thumbnails).
// Returns a SlideHandle; caller owns it and must call handle.dispose() when done.
const handle = viewer.renderSlideToContainer(index, container, scale?);
handle.dispose(); // Clean up slide-specific resources
// Render a lightweight scaled slide preview into an external container.
// This preserves the original slide layout and uses transform scaling; it is
// not a bitmap thumbnail generator, so use lazy/windowed mounting for decks.
const thumb = viewer.renderThumbnailToContainer(index, sidebarItem, { width: 180 });
await thumb?.ready;
thumb?.dispose();
// Query which slides are currently mounted in the DOM
viewer.isSlideMounted(index); // boolean
viewer.getMountedSlides(); // number[] (sorted)
// Typed event helpers (return `this` for chaining)
viewer.on('slidechange', (e) => console.log(e.detail.index));
viewer.off('slidechange', listener);
viewer.destroy(); // Cleanup blob URLs, observers, and DOM
viewer[Symbol.dispose](); // TC39 Explicit Resource Management (calls destroy)
文本搜索
PptxViewer.searchText(query, options?) 搜索解析器 PresentationData 型号,
而不是被改造的DOM。 这将在幻灯片挂起之前或之后继续搜索
避免变异渲染器生成的文本运行.
字符串查询默认为不区分大小写 。 过 matchCase: true 需要的时候
准确的外壳。 RegExp 查询保留自己的旗帜, 所以 /GPU/ 仍然对大小写敏感
财务报告和审定财务报表 /GPU/i 仍然对案件不敏感.
import type { TextSearchResult } from '@aiden0z/pptx-renderer';
const matches: TextSearchResult[] = viewer.searchText('GPU', {
matchCase: false,
wholeWord: true,
snippetRadius: 48,
});
const exactMatches = viewer.searchText('GPU', { matchCase: true });
const regexMatches = viewer.searchText(/GPU|CPU/i);
for (const match of matches) {
await viewer.goToSlide(match.slideIndex, { behavior: 'smooth', block: 'center' });
// Use match.bounds for node-level highlight overlays in your own UI.
}
每个 TextSearchResult 包含 slideIndex, (中文(简体) ), nodeId, (中文(简体) ), nodePath, (中文(简体) ), nodeType, (中文(简体) ),
textKind满 text, (中文(简体) ), matchStart, (中文(简体) ), matchEnd, (中文(简体) ), snippet,以及 bounds。 。 。 .
bounds 是内在幻灯片坐标中匹配的形状或表格边框,所以
应用程序代码可以在已渲染的幻灯片上方绘制节点级别突出显示.
渲染器故意不为字符级文本加亮重写文本节点. 突出字符级文本需要将匹配结果映射回形状的办公室 文本运行和行布局,这是一个单独的,风险更高的渲染器特性. 今天 稳定的 API 边界是模型级搜索加节点级边界.
对于常见的UI案件, highlightSearchResult(result, options?) 绘制节点级别
使用默认突出显示样式覆盖 。 过 SearchHighlightOptions 为自定义颜色,
间隔、 阴影和类名 :
const hit = await viewer.highlightSearchResult(matches[0], {
className: 'my-search-hit',
borderColor: '#22c55e',
backgroundColor: 'rgba(34, 197, 94, 0.18)',
borderRadius: 6,
borderWidth: 2,
boxShadow: '0 0 0 2px rgba(15, 23, 42, 0.35)',
padding: 3,
});
// The caller owns returned highlight handles.
hit?.dispose();
viewer.clearSearchHighlights();
缩放幻灯片预览
PptxViewer.renderThumbnailToContainer(index, container, options?) 使幻灯片在
它的内在布局大小,并在被剪接的包装器内应用 CSS 转换缩放.
这样可以避免如果将 PPTX 幻灯片直接渲染成一个
小拇指大小的容器.
const thumb = viewer.renderThumbnailToContainer(slideIndex, thumbnailEl, { width: 96 });
await thumb?.ready;
// The caller owns externally rendered previews.
thumb?.dispose();
这不是一个位图缩略图生成器: 它仍然创建一个缩放的 DOM/ SVG 幻灯片
预览,所以大型甲板应该以懒散的预览方式挂载 IntersectionObserver 或一个
窗口列表,然后在卷出视图时处理控件.
ListRenderOptions
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
windowed | boolean | false | 使用交互分区观察员窗口 |
batchSize | number | 12 | 每个渲染批次的幻灯片 |
initialSlides | number | 4 | 要挂载的初始幻灯片( 窗口) |
overscanViewport | number | 1.5 | 查看端口超扫描乘数 |
ZipParseLimits 和资源安全
parseZip(buffer) 默认后向兼容性为无 ZIP 限制。 对于来自用户或其他不可信来源的文件,通过 RECOMMENDED_ZIP_LIMITS 或更严格的数值:
| 限制 | 建议价值 | 保护什么 |
|---|---|---|
maxEntries | 4000 | 文件计数过多的档案 |
maxEntryUncompressedBytes | 32 MiB | 单个超大小 XML/ 媒体条目 |
maxTotalUncompressedBytes | 256 MiB | 已解压档案总大小 |
maxMediaBytes | 192 MiB | 媒体有效载荷总数 ppt/media/ |
maxConcurrency | 8 | 并行的 ZIP 条目为 |
当ZIP元数据不暴露一个可靠的未压缩大小时,解析器在接受该条目之前会回落到实际解码的条目大小. 一直这样 maxEntryUncompressedBytes, (中文(简体) ), maxTotalUncompressedBytes,以及 maxMediaBytes 适用于 XML/文本条目和媒体条目.
在 ZIP 解析后, Render 级的警卫也适用 :
- 图表缓存不从超大小分配
c:ptCount; 图表点索引上限为10,000每个缓存. - 解码大小超过时拒绝 EMF 位图预览
16,777,216像素, 尺寸超过8192x8192,或像素有效载荷比宣布的位图短. - 外部音频/视频关系只能保证安全
http编号 :https带 URL 的TargetMode="External"和以preload="none"。 。 。 . - 外部图像和图片填充可以在渲染过程中请求其HTTP(S)目标. 对于不信任
设甲板
externalMedia: 'block'打开PptxViewer编号 :PptxViewer.open()遗产PptxRenderer,或 独立renderSlide()以跳过所有外部媒体关系,包括图像、背景, 形状/文本图片填充、音频/视频和海报。 嵌入式软件包媒体仍然可用. 默认是'allow'用于兼容性。 该政策也适用于列表、单滑、外部 容器和缩略图渲染;点击驱动的超链接保留了它们现有的行为.
活动PptxViewerEventMap(中文(简体) )
viewer.addEventListener('renderstart', () => {
/* render cycle began */
});
viewer.addEventListener('rendercomplete', () => {
/* render cycle finished (fires even on error) */
});
viewer.addEventListener('slidechange', (e) => console.log(e.detail.index));
viewer.addEventListener('sliderendered', (e) => console.log(e.detail.index, e.detail.element));
viewer.addEventListener('slideerror', (e) => console.error(e.detail.index, e.detail.error));
viewer.addEventListener('slideunmounted', (e) => console.log(e.detail.index));
viewer.addEventListener('nodeerror', (e) => console.warn(e.detail.nodeId, e.detail.error));
slidechange 都烧了 goToSlide() (a) 导航和每个成像周期后(包括初始成像). renderstart编号 :rendercomplete 括号中每个渲染周期(提交列表、提交Slide、设定Zoom、设定FitMode)。 当调用重叠时, 更新的调用请求会取代更旧的队列或分批的工作, 因此在附加更多的 DOM 之前, stale list 批次停止 .
实例属性( 只读)
viewer.presentationData; // PresentationData | null — the parsed model, null before load()
viewer.slideCount; // number — total slides (0 if not loaded)
viewer.slideWidth; // number — intrinsic slide width in px
viewer.slideHeight; // number — intrinsic slide height in px
viewer.currentSlideIndex; // number — currently active slide (0-based)
viewer.isRendering; // boolean — true between renderstart and rendercomplete
viewer.zoomPercent; // number — current zoom level (e.g. 100, 200)
viewer.fitMode; // FitMode — current fit mode ('contain' | 'none')
PptxRenderer (减值的v1相容)
PptxRenderer 扩展 PptxViewer 并提供遗产 preview(input) 在幻灯片模式下带有内置导航按钮的API. 迁移到 PptxViewer 新代码.
import { PptxRenderer } from '@aiden0z/pptx-renderer';
const renderer = new PptxRenderer(container, { mode: 'list', listMountStrategy: 'windowed' });
await renderer.preview(buffer); // deprecated — use PptxViewer.open() instead
PptxRenderer 接受相同的选项 lazyMedia, (中文(简体) ), lazySlides,以及 pdfjs
配置为 PptxViewer,因此遗留用户可以启用性能选项或
EMF-PDF倒置渲染而无需更改API.
工具出口
import {
parseZip,
parseZipLazyMedia,
buildPresentation,
materializeAllSlideNodes,
serializePresentation,
buildTextIndex,
searchText,
searchPresentation,
RECOMMENDED_ZIP_LIMITS,
} from '@aiden0z/pptx-renderer';
const files = await parseZip(arrayBuffer, RECOMMENDED_ZIP_LIMITS); // PptxFiles
const lazyFiles = await parseZipLazyMedia(arrayBuffer, RECOMMENDED_ZIP_LIMITS); // media resolves on demand
const presentation = buildPresentation(files); // PresentationData
const lazyPresentation = buildPresentation(lazyFiles, { lazySlides: true }); // slide nodes parse on demand
materializeAllSlideNodes(lazyPresentation); // optional: force full model materialization
const json = serializePresentation(presentation); // SerializedPresentation (JSON-safe)
// json.layouts / json.masters carry each template's non-placeholder shapes as typed
// nodes; a slide names its own via layoutPath / masterPath. Draw order is master,
// then layout, then the slide's own nodes, composed as the renderer does:
// - slide nodes: always
// - layout nodes: only when slide.showMasterSp is not false
// - master nodes: only when slide.showMasterSp and layout.showMasterSp are both not false
// So slide.showMasterSp === false hides both layout and master shapes, while
// layout.showMasterSp === false hides only the master's.
const index = buildTextIndex(presentation); // TextIndexEntry[]
const matches = searchText(index, '算力'); // TextSearchResult[]
const directMatches = searchPresentation(presentation, /GPU|CPU/i); // TextSearchResult[]
无头幻灯片渲染
对于高级使用案例(服务器侧截图,自定义渲染管道):
import { renderSlide } from '@aiden0z/pptx-renderer';
import type { SlideHandle } from '@aiden0z/pptx-renderer';
const handle = renderSlide(presentation, presentation.slides[0], {
onNodeError: (nodeId, err) => console.warn(nodeId, err),
mediaUrlCache: new Map(), // optional shared cache for blob URLs
pdfjs, // optional, only for EMF-embedded PDF fallback rendering
fontFaces, // optional host-provided FontFaceConfig[]
});
document.body.appendChild(handle.element);
// Await async media such as EMF-PDF fallback previews before screenshots/exports.
await handle.ready;
// Cancels pending PDF fallbacks and disposes charts + owned blob URLs.
handle.dispose();
模型类型
所有型号都输出给消费者 建自定义工具:
import type {
PresentationData,
BuildPresentationOptions,
SlideData,
SlideNode,
ThemeData,
BaseNodeData,
ShapeNodeData,
PicNodeData,
TableNodeData,
GroupNodeData,
ChartNodeData,
TextBody,
TextParagraph,
TextRun,
Position,
Size,
NodeType,
SerializedPresentation,
SerializedSlide,
SerializedTemplate,
SerializedNode,
PptxFiles,
ZipParseLimits,
FitMode,
PreviewInput,
ViewerOptions,
FontFaceConfig,
ListRenderOptions,
ThumbnailRenderOptions,
SearchHighlightHandle,
SearchHighlightOptions,
PptxViewerEventMap,
SlideHandle,
PdfjsOptions,
PdfjsConfig,
TextBounds,
TextIndexEntry,
TextIndexOptions,
TextSearchOptions,
TextSearchResult,
} from '@aiden0z/pptx-renderer';
渲染能力
形状和几何
渲染器覆盖常用的 DigingML 预设家族和数字自定义几何,
包括多路径形状,连接器,箭头,呼号,动作按钮,和流程图.
光谱编译的运行时间目前包含29个定义: 所有28个零调整流程图加
donut,其 OOXML 默认值为 adj=25000 带有支持的 0..50000 调整范围。 其它
预设继续使用手写执行. 习惯上的任意象征性指南
未支持几何学 .
文本
文本渲染遵循主、布局、占位符、形状、段落和运行级联。 这个
支持主题字体、 CJK 文本、 符号、 超链接、 垂直文本、 上标/ 下标、 办公室
百分比间距,在水平左到右文本中明确左/中/右/小/小数分页,
并用垂直的东亚文本清晰地左标签站,
固态/渐变/平面/伸缩-相图充值,常见的包/流组合,并选定
spAutoFit 增长. 字体可用性仍然是视觉测试出处的一部分.
表格
常见的表格路径支持内置和文档表格样式,第一个/最后一个/带宽选项, 变量行和列大小、水平和垂直合并、单元格边距和锁定、CJK/混合 文本、有条件和合并的边框、明确的边界清除和直接单元格覆盖。 这个 权利要求以八种本地PowerPoint矩阵为界限; 不寻常的生产者怪异, 对角 边框,任意样式组合可能仍然不同.
图表
欧共体 使栏/栏、行、区、派、甜甜圈、雷达、散开, 气泡,股票/烛台,支持的组合图,以及二级轴. 原始和文字来源, 支持明显的零,常见的标签,传说,标记,和数字格式. 图表渲染 由于Office的地块区域、轴线、标签和传说的布局可能不同,所以仍然大致相同。 OOXML 三维 如果可能,图表可追溯到二维表示.
方程式
受限的 OMML 子集作为浏览器- 本地演示文稿 MathML: 运行, 栏/ 无栏/ skewed/ linear 分数、 基数、 下标/ 上标、 分隔符、 n- ary 运算符、 矩阵和函数. 直接路径仅在确认完整的数学子树时选择;否则 使用软件包作者的MCE倒置形状或图形框架。 富豪配方样式 未知的 OMML 构造仍然位于直接子集之外。 没有公式特定的运行时间依赖性 所需经费.
效果和静态3D
普通形状外影和反射有本地验证的道用于组合 跟踪效应矩阵。 其他效果组合使用已存在的近似或平面 倒计时.
静态的 DigingML 3D 路径剖析场景,相机,照明,贝子,轮廓,以及材料属性. 本地验证的渲染覆盖了选中的上层紫外线,零深相机平面,已裁剪 图片、活的文字、一个自定义几何家族、一个二相图组和选定的边上底部 贝氏物质价值. 它使用SVG,Canvas,以及投影数学而不是网格引擎. 常规 挤压、任意相机和照明、任意群体场景、3D图表和嵌入式办公室3D 不支持模型.
智能艺术、媒体、团体和兼容内容
- 智能艺术: 使可用的图回落数据;布局忠贞度不同.
- 图像:光栅和SVG预览,裁剪和几何剪接,常见的图像效果,和浏览器支持的音频/视频.
- OLE/EMF预览: 可用时使用软件包提供的位图或嵌入式PDF预览; PDF预览需要可选
pdfjs-dist。 。 。 . - 组: 递归地重映射出被支持的变换子.
- 兼容内容: 选择一个被支持
mc:Choice,否则mc:Fallback,同时保留分支顺序. - 背景和颜色: 解决幻灯片/放出/主机背景,主题颜色,颜色图,以及常见的修饰符.
建筑
三层管线: 解析 - > 型号 - > 渲染器
ArrayBuffer (.pptx)
-> ZipParser (jszip extraction)
-> XmlParser (DOMParser + SafeXmlNode null-safe wrapper)
-> buildPresentation() (assembles slides/layouts/masters/themes with relationship chains)
-> SlideRenderer (background -> master shapes -> layout shapes -> slide shapes -> DOM)
关键设计决定:
- 安全Xml 节点: Null-safe XML traversal – 返回空节点而不是空节点,允许不作无效检查而进行深链.
- 懒惰的幻灯片解析: 可选
lazySlides模式将每个滑行节点推迟到渲染、搜索、序列化或明确实现. - 懒群解析:分组被存储为生XML的儿童,在渲染过程中被解析,以避免在模型层中发生深刻的重复.
- 错误隔离: 目标试验/捕获。 一个失败的形状会变成一个被折叠的红色占位符;幻灯片会继续.
- 无外部 CSS:所有样式都内含. 库输出自成一体的HTML碎片.
- Blob URL 寿命周期: 为图像/媒体所创建,在
mediaUrlCache,在destroy()。 。 。 .
业绩
默认行为仍然渴望兼容性:分析软件包,构建完整 模型,并按所选模式渲染。 对于大型或相干甲板,选择 进入懒惰/被窗口的路径,所以第一个可见的幻灯片可以在不实现的情况下渲染 每张幻灯片 和每条媒体入口 在前面.
使用此预设进行交互式查看 :
const viewer = await PptxViewer.open(buffer, container, {
zipLimits: RECOMMENDED_ZIP_LIMITS,
lazySlides: true,
lazyMedia: true,
listOptions: {
windowed: true,
batchSize: 8,
initialSlides: 4,
overscanViewport: 1.5,
},
});
建议的选择:
| 假设 | 建议的备选方案 | 主要福利 |
|---|---|---|
| 用户加载的 PPTX | zipLimits: RECOMMENDED_ZIP_LIMITS | ZIP 解析工作和解码有效载荷 |
| 可滚动长的查看器 | listOptions.windowed: true | 保持屏幕外的幻灯片退出 DOM |
| 具有许多幻灯片元素的大型甲板 | lazySlides: true 加窗口列表渲染 | 在需要之前分析每个滑坡节点的阻力 |
| 媒体重型甲板 | lazyMedia: true 加窗口列表渲染 | Defers 图像/音频/视频字节解码 |
| 导出、打印或完整比较任务 | Eager 默认,或在导出前明确实现 | 确保所有幻灯片在一个通道内准备好 |
在地方基准中, lazySlides 中型模型制造时间减少约52-66%
和大甲板, 并降低第一窗口 剖析+建设 + 渲染时间大约 16-22%.
对于媒体重窗观众, lazyMedia 初始减压媒体字节
约72-97%,取决于甲板内容. 这些选项保留了渲染语义;
它们主要将工作从初始负载移动到幻灯片或媒体项目实际存在的时刻
需要帮助.
手动管线可以使用相同的构件:
const files = await parseZipLazyMedia(buffer, RECOMMENDED_ZIP_LIMITS);
const presentation = buildPresentation(files, { lazySlides: true });
const viewer = new PptxViewer(container);
viewer.load(presentation);
await viewer.renderList({ windowed: true, initialSlides: 4 });
细节 : docs/PERFORMANCE.md
警卫
- 将 PPTX 输入视为不信任 。 开始
RECOMMENDED_ZIP_LIMITS,然后收紧你的部署. - 外部超链接采用协议过滤( 无)
javascript:, (中文(简体) ),data:(见a/c.5/49/70,附件). - 报告:
docs/SECURITY.md
发展
pnpm install
pnpm dev # Vite dev server
pnpm test # Unit tests (vitest)
pnpm test:coverage # Coverage report → coverage/
pnpm build # Production build
pnpm test:package # Verify package entries, packlist boundaries, and notice links
pnpm test:browser # Real Chromium checks for standalone, charts, and PDF.js
pnpm dev:e2e # Dev server + Python E2E API server
pnpm geometry:check # Verify pinned OOXML geometry compile/evaluate/emit output
pnpm lint # ESLint
pnpm typecheck # tsc --noEmit
pnpm knip # Dead code / unused exports detection
开发页面 http://127.0.0.1:5173数字 :
| 页次 | 目的 |
|---|---|
/test/pages/index.html | 以搜索和缩略图上传预览 |
/test/pages/render-slide.html | 本地分辨率单张幻灯片 |
/test/pages/e2e-compare.html | PDF 对 HTML 有 SSIM 分数 |
/test/pages/export.html | JSON 模型树查看器 |
文档
| 医生 | 内容 |
|---|---|
ARCHITECTURE.md | 管道设计/模型 |
PERFORMANCE.md | 培训选项和预设 |
TESTING.md | 单位/E2E战略、两层计量系统、可视回归工作流程 |
CONTRIBUTING.md | 公关清单、代码质量工具和工作流程 |
SECURITY.md | 脆弱性报告 |
何为不支持
渲染器不提供一般 DigingML 外观, true 3D 图表, 嵌入式 Office 3D
模型,动画回放或过渡,一个完整的OMML引擎,任意的EMF/WMF向量
渲染、可执行/可编辑的 OLE 对象,或幻灯片注释渲染。 不支持的 三维效果
组合保留平面或近似渲染路径。 包提供的OLE、等式和EMF
当兼容的倒计时,预览仍可能渲染出. 精确的限定范围被记录
内 test/e2e/oracle/capabilities.json。 。 。 .
财务问题
这在Node.js上运行吗? 没有。 渲染依赖于浏览器 DOM API.
为什么我的PPTX不完全? OOXML是一个巨大的光谱. 请用一个最小的PPTX样本打开一个相容性问题——视觉回归管道使得增加新病例的覆盖是直截了当的.
我如何高效制造出100+滑行甲板?
使用 windowed: true 内 listOptions,并启用 lazySlides: true。用于媒体重度
甲板,也启用 lazyMedia: true。 。 。 .
许可证
阿帕奇许可证2.0. 见 LICENSE。 。 。 .

