Docmee protocol reference

docmee-pptxProperty 结构协议

面向第三方开发者的 PPTX 结构数据说明:从顶层字段、母版版式,到页面对象树、文本、图片、表格、分组和媒体扩展。

12篇文档
100段代码
455张表格
980字段标记

Recommended route

建议阅读路径

  1. 核心协议确认顶层结构、坐标、颜色与通用类型。
  2. 母版协议理解主题、版式、颜色映射和占位符继承。
  3. 页面协议处理页面与母版/版式之间的索引关系。
  4. 对象协议按对象类型解析文本、图片、表格、分组和媒体。

README.md

项目概览

项目用途、阅读方式和兼容性说明。

源 Markdown

#docmee-pptxProperty 结构协议

Docmee pptxProperty JSON 结构协议文档,用于帮助第三方开发者读取、生成、转换和校验 PPTX 结构数据。

在线阅读文档 »

在线文档 · 协议入口 · 参考 JSON

#Table of Contents

#About The Project

docmee-pptxProperty-structural-protocol 维护 Docmee pptxProperty 的结构协议说明。

pptxProperty 描述一个演示文稿的页面尺寸、页面内容、幻灯片母版、版式、主题、背景和页面对象。它不是 PPTX 文件本身,而是 Docmee 系统在编辑、解析和生成 PPTX 时使用的结构化数据格式。

本仓库的目标是让开发者可以:

  • 理解 pptxProperty 的顶层结构、索引关系和兼容性规则。
  • 按对象类型解析文本、图片、表格、分组、音频和视频等页面内容。
  • 生成符合 Docmee 预期的数据结构,并在读写过程中保留未知字段。
  • 通过在线文档快速检索字段、对象类型和示例。

#Built With

本项目以 Markdown 协议文档为源文件,并使用零运行时依赖的静态站点生成脚本发布在线文档。

  • Markdown
  • Node.js
  • GitHub Pages

#Getting Started

可以直接访问线上文档:

https://docmee.github.io/docmee-pptxproperty-structural-protocol/

也可以在本地克隆仓库后阅读 Markdown 源文档,或重新生成静态文档站点。

#Prerequisites

  • Node.js 18 或更高版本

#Local Preview

  1. Clone the repo

``sh git clone https://github.com/docmee/docmee-pptxproperty-structural-protocol.git cd docmee-pptxproperty-structural-protocol ``

  1. Build the documentation site

``sh npm run build:site ``

  1. Preview the generated site

``sh cd docs && npx serve ``

  1. Open the local URL

``text http://127.0.0.1:3000/ ``

#Usage

建议按以下顺序阅读协议:

  1. 阅读 protocol/core.md,理解顶层字段、坐标、颜色、填充、边框等通用结构。
  2. 阅读 protocol/slideMaster.md,理解母版、版式、主题与占位符的组织方式。
  3. 阅读 protocol/page.md,理解页面如何引用母版和版式,以及页面内容如何挂载。
  4. 阅读 protocol/object/README.md,理解对象树和各对象类型的文档入口。
  5. 对照 example/example.json,验证实际 JSON 与协议字段的对应关系。

开发集成时,推荐的处理流程是:

  1. 读取顶层 widthheight,建立页面画布。
  2. 遍历 pages,读取每个页面的 extInfo.slideMasterIdxextInfo.slideLayoutIdx
  3. 根据索引解析对应的 slideMasters[]slideMasters[].slideLayouts[]
  4. 合成母版、版式、页面三层背景和占位符信息。
  5. 遍历 pages[].children,按对象协议解析并渲染对象树。

重要: slideMasterIdxslideLayoutIdx 是数组下标引用。处理过程中不应重新排序相关数组,除非同步更新所有页面引用。

#Documentation

路径用途
在线文档可在线观看和搜索的完整协议文档。
protocol/README.md协议文档入口和阅读顺序。
protocol/core.md顶层结构、索引关系、坐标约定和通用类型。
protocol/slideMaster.mdslideMastersslideLayouts、主题和母版结构。
protocol/page.mdpages、页面扩展信息和页面内容组织方式。
protocol/object/README.md页面对象协议入口。
protocol/object/common-property.md对象公共属性,例如位置、填充、阴影、超链接。
protocol/object/text.md文本对象、段落、文本片段和富文本组合。
protocol/object/image.md图片对象、图片资源、裁剪和透明度。
protocol/object/table.md表格、行、单元格、合并单元格。
protocol/object/container.md分组对象、位置、缩放和嵌套。
protocol/object/other-objects.md音频、视频和媒体播放扩展。
example/example.json参考 pptxProperty JSON。
example/audio_shadow.json含音频和阴影对象的参考 JSON。

#Roadmap

  • [x] 梳理顶层结构、页面、母版和对象协议。
  • [x] 补充文本、图片、表格、分组和媒体对象说明。
  • [x] 生成可通过 GitHub Pages 访问的在线文档站点。
  • [ ] 补充 JSON Schema。
  • [ ] 补充最小对象样例。
  • [ ] 补充自动校验脚本。

#Contributing

欢迎通过 issue 或 pull request 补充字段说明、示例和兼容性经验。

建议贡献流程:

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

提交协议变更时,请尽量同时补充:

  • 字段路径和类型。
  • 是否必填,以及缺失或为 null 时的处理方式。
  • 与 PPTX、Docmee 编辑器或导入导出流程相关的兼容性说明。
  • 可复现的最小 JSON 示例。

#License

Distributed under the Apache-2.0 License. See LICENSE for more information.

#Contact

Project Link: https://github.com/docmee/docmee-pptxproperty-structural-protocol

Documentation: https://docmee.github.io/docmee-pptxproperty-structural-protocol/

#Acknowledgments

protocol/README.md

协议入口

pptxProperty 的整体结构和推荐阅读顺序。

源 Markdown

#pptxProperty 协议

本目录存放 pptxProperty 的完整结构协议说明。协议面向需要读取、生成或转换 Docmee PPTX 结构数据的开发者。

#阅读顺序

文档主题
core.md顶层数据结构、通用类型、坐标、颜色、填充、边框。
slideMaster.md幻灯片母版、版式、主题、颜色映射、母版占位符。
page.md页面结构、页面扩展信息、页面与母版/版式的索引关系。
object/README.md页面对象树、文本、图片、表格、分组以及每类对象的属性。

#结构概览

text
pptxProperty
├── version
├── width
├── height
├── font
├── pages[]
│   ├── extInfo
│   │   ├── slideMasterIdx
│   │   ├── slideLayoutIdx
│   │   └── background
│   └── children[]
└── slideMasters[]
    ├── theme
    ├── clrMap
    ├── background
    ├── children[]
    └── slideLayouts[]
        ├── type
        ├── name
        ├── background
        └── children[]

pages 保存实际页面。slideMasters 保存母版。每个 slideMaster 内部的 slideLayouts 保存版式。页面通过 slideMasterIdxslideLayoutIdx 引用对应的母版和版式。

#数据合成

渲染或生成页面时,推荐按以下层级合成数据:

  1. 读取 slideMasters[slideMasterIdx],获得主题、母版背景和母版占位符。
  2. 读取 slideMasters[slideMasterIdx].slideLayouts[slideLayoutIdx],获得版式背景和版式占位符。
  3. 读取 pages[].extInfo.background,获得页面级背景。
  4. 读取 pages[].children,获得页面实际对象。

页面内容对象遵循统一的树结构,详见 object/README.md

protocol/core.md

核心协议

顶层结构、通用类型、坐标、颜色、填充和边框。

源 Markdown

#核心协议

core 描述 pptxProperty 的顶层结构、全局约定和跨模块复用的通用类型。

#顶层结构

ts
type PptxProperty = {
  version: string
  width: number
  height: number
  font: FontResource[]
  pages: Page[]
  slideMasters: SlideMaster[]
}
字段类型是否必填说明
versionstring协议版本。
widthnumber页面画布宽度。
heightnumber页面画布高度。
fontFontResource[]全局字体资源列表。没有字体资源时使用空数组。
pagesPage[]实际页面数组。
slideMastersSlideMaster[]幻灯片母版数组。每个母版可以包含多个 slideLayouts
ts
type FontResource = Record<string, unknown>

#索引关系

页面通过下标引用母版和版式:

text
pages[n].extInfo.slideMasterIdx
  -> slideMasters[slideMasterIdx]

pages[n].extInfo.slideLayoutIdx
  -> slideMasters[slideMasterIdx].slideLayouts[slideLayoutIdx]

实现方应保持 slideMastersslideLayouts 的顺序稳定。若需要过滤、排序或重建数组,应同步更新所有页面上的 slideMasterIdxslideLayoutIdx

#坐标约定

ts
type Box = [x: number, y: number, width: number, height: number]
字段格式说明
pointBox对象在页面坐标系中的外框。
anchorBox属性内的几何锚点。顶层对象通常与 point 一致。
interiorAnchorBox分组内部坐标系区域。
textInsets[top, right, bottom, left]文本内边距。

页面坐标原点位于左上角,x 向右增加,y 向下增加。widthheight 使用与顶层画布一致的单位。

分组对象内部可以出现相对坐标或归一化坐标。渲染分组对象时,应先建立分组坐标系,再解析其子对象位置。

#通用节点约定

所有对象节点都使用 children 表达树形关系。没有子节点时,children 使用空数组。

ts
type ElementNode = {
  id: string
  pid?: string
  type: string
  depth: number
  point?: Box
  noDraw?: boolean
  text?: string
  extInfo: Record<string, unknown>
  children: ElementNode[]
}
字段类型说明
idstring节点唯一标识。
pidstring父节点 id。顶层页面对象可以没有该字段。
typestring节点类型。
depthnumber节点深度。页面直接子对象通常为 1
pointBox对象外框。结构性节点可以没有该字段。
noDrawbooleantrue 时表示该节点不直接绘制,常用于母版或版式占位符。
textstring文本内容。主要用于段落节点和文本片段节点。
extInfoobject扩展信息。对象样式和行为主要位于 extInfo.property
childrenElementNode[]子节点数组。

#背景

ts
type Background = {
  realType: "Background"
  anchor: Box
  fillStyle: FillStyle
  strokeStyle: StrokeStyle
}
字段类型说明
realType"Background"标识背景属性。
anchorBox背景覆盖区域,通常覆盖完整画布。
fillStyleFillStyle背景填充。
strokeStyleStrokeStyle背景边框。没有边框时可为空对象。

#颜色

ts
type ColorValue = {
  scheme?: string
  realColor: number
  color: number
  lumMod?: number
  lumOff?: number
  shade?: number
  tint?: number
}
字段类型说明
schemestring主题色引用,例如 accent1tx1bg1
realColornumber计算后的实际颜色值。
colornumber原始颜色值或主题基础颜色值。
lumModnumber亮度乘数。
lumOffnumber亮度偏移。
shadenumber阴影或变暗比例。
tintnumber着色或变亮比例。

颜色使用 32 位有符号整数表示。协议中的 realColorcolor 存储的是 ARGB 整数,即高 8 位为透明通道,后 24 位依次为红、绿、蓝通道。常见不透明颜色可以理解为 0xFFRRGGBB 转成有符号十进制后的结果。

换算步骤如下:

  1. 从输入颜色得到 RGB,每个通道范围为 0..255
  2. 从输入透明度得到 A,范围也是 0..255。没有透明度时使用 255
  3. 组合无符号整数:argb = (A << 24) | (R << 16) | (G << 8) | B
  4. 如果 argb >= 2147483648,写入协议时使用 argb - 4294967296;否则直接写入 argb

从十六进制颜色转换时:

  • #RRGGBBA = 255,再按 0xFFRRGGBB 组合。
  • #RGB:先展开为 #RRGGBB,例如 #0AF 等同于 #00AAFF
  • #RRGGBBAA:若来源明确使用 CSS 八位十六进制格式,则最后两位是透明通道,需要转换成协议使用的 AARRGGBB 再写入十进制值。
  • 不建议混用未标明来源的八位十六进制颜色,因为有些系统使用 #AARRGGBB,有些系统使用 #RRGGBBAA

rgba(r,g,b,a) 转换时:

  • rgb 直接作为 RGB
  • CSS 中的 a 通常为 0..1,换算为 A = round(a * 255)
  • 协议中的独立 alpha 字段使用 0..100000,可写为 round(a * 100000)100000 表示完全不透明,0 表示完全透明。

常见示例:

输入ARGB 十六进制协议十进制
#FFFFFF0xFFFFFFFF-1
#0000000xFF000000-16777216
#4874CB0xFF4874CB-12028725
rgba(0,0,0,0.17)0x2B000000721420288

实现方可以按自身语言的颜色工具转换为 RGB 或 ARGB,但应保留原始数值以便回写。

#填充样式

ts
type FillStyle =
  | { type: "noFill" }
  | { type: "color"; color: ColorValue }
  | { type: "gradient"; gradient: GradientFill }
  | { type: "texture"; texture: TextureFill }
type说明
noFill无填充。
color纯色填充。
gradient渐变填充。
texture图片或纹理填充。

#渐变填充

ts
type GradientFill = {
  angle: number
  colors: ColorValue[]
  fractions: number[]
  gradientType: string
  insets: number[] | null
  flip: string | null
  tileRect: number[] | null
  rotWithShape: boolean | null
}
字段类型说明
anglenumber渐变角度。
colorsColorValue[]渐变色标。
fractionsnumber[]色标位置,顺序与 colors 对应。
gradientTypestring渐变类型,例如 linear
insetsnumber[] | null渐变区域内缩。
flipstring | null翻转模式。
tileRectnumber[] | null平铺区域。
rotWithShapeboolean | null是否随形状旋转。

#纹理填充

ts
type TextureFill = {
  imageData: string
  contentType: string
  alpha: number
  flipMode: string
  stretch?: number[]
  insets?: number[]
  offset?: [number, number]
  scale?: [number, number]
  alignment?: string
  alphaModFix?: number
  rotWithShape?: boolean
}
字段类型说明
imageDatastring图片资源,可以是 URL 或 data:image/...;base64,...
contentTypestringMIME 类型,例如 image/pngimage/jpeg
alphanumber纹理透明度。100000 表示完全不透明。
flipModestring翻转模式。
stretchnumber[]拉伸或裁切参数。
insetsnumber[]图片裁切内缩参数。
offset[number, number]纹理偏移。
scale[number, number]纹理缩放。
alignmentstring对齐方式。
alphaModFixnumber透明度修正值。
rotWithShapeboolean是否随形状旋转。

#边框样式

ts
type StrokeStyle = {
  paint?: FillStyle
  lineWidth?: number
  lineCap?: string
  lineJoin?: string
  lineDash?: string
  lineCompound?: string
}
字段类型说明
paintFillStyle线条填充。
lineWidthnumber线宽。
lineCapstring线端样式。
lineJoinstring连接样式。
lineDashstring虚线样式。
lineCompoundstring复合线样式。

#几何

ts
type Geometry = {
  name: string
  data: unknown | null
  avLst: unknown | null
  textBounds: unknown | null
}
字段类型说明
namestring几何类型名称,例如 recttableColumn
dataunknown | null自定义几何数据。
avLstunknown | null可调参数列表。
textBoundsunknown | null文本边界信息。

#占位符

ts
type Placeholder = {
  type: string
  idx?: number
  size?: string
  visible: boolean
}
字段类型说明
typestring占位符类型,例如 TITLEBODYCENTERED_TITLESUBTITLECONTENTPICTUREDATETIMEFOOTERSLIDE_NUMBER
idxnumber占位符索引。
sizestring占位符尺寸级别,例如 halfquarter
visibleboolean占位符是否可见。

#兼容性要求

  • 字段名必须按协议原样读取和写入。
  • 未识别字段应保留透传。
  • 数组顺序具有语义,尤其是 pagesslideMastersslideLayouts 和对象 children
  • 对象渲染应优先基于 type 分派,再解析对应的 extInfo.property
  • null 与字段缺失应区别处理。null 通常表示该结构被显式保留但当前没有值。

protocol/slideMaster.md

幻灯片母版协议

母版、版式、主题、颜色映射和占位符。

源 Markdown

#幻灯片母版协议

slideMaster 描述幻灯片母版、版式、主题、颜色映射和母版占位对象。页面通过索引引用母版和版式。

#母版结构

ts
type SlideMaster = {
  background: Background
  clrMap: ColorMap
  theme: Theme
  children: ElementNode[]
  slideLayouts: SlideLayout[]
  animation: unknown | null
  transition: unknown | null
}
字段类型是否必填说明
backgroundBackground母版默认背景。
clrMapColorMap主题颜色映射。
themeTheme主题名称、主题颜色和字体映射。
childrenElementNode[]母版级占位对象。
slideLayoutsSlideLayout[]母版下的版式集合。
animationunknown | null母版动画信息。没有动画时为 null
transitionunknown | null母版切换信息。没有切换时为 null

#颜色映射

ts
type ColorMap = Record<string, string>

clrMap 用于把文档语义色映射到主题色槽位。例如:

json
{
  "tx1": "dk1",
  "bg2": "lt2",
  "bg1": "lt1",
  "tx2": "dk2"
}
说明
tx1主文本色映射。
tx2次文本色映射。
bg1主背景色映射。
bg2次背景色映射。

#主题

ts
type Theme = {
  name: string
  colors: Record<string, number>
  majorFontMap: FontMap
  minorFontMap: FontMap
}

type FontMap = {
  latin: string
  ea: string
  cs: string
}
字段类型说明
namestring主题名称。
colorsRecord<string, number>主题色表。常见键包括 accent1accent6lt1lt2dk1dk2tx1tx2bg1bg2hlinkfolHlink
majorFontMapFontMap主字体映射,通常用于标题。
minorFontMapFontMap次字体映射,通常用于正文。
字体映射字段说明
latin西文字体。
ea东亚字体。
cs复杂文字字体。

#版式结构

ts
type SlideLayout = {
  type: string
  name: string
  noMaster: boolean | null
  clrMap: ColorMap | null
  background: Background
  children: ElementNode[]
  animation: unknown | null
  transition: unknown | null
}
字段类型是否必填说明
typestring版式类型。
namestring版式名称。
noMasterboolean | null是否不继承母版。为 null 时按默认继承规则处理。
clrMapColorMap | null版式级颜色映射。为 null 时使用母版颜色映射。
backgroundBackground版式背景。
childrenElementNode[]版式占位对象。
animationunknown | null版式动画信息。没有动画时为 null
transitionunknown | null版式切换信息。没有切换时为 null

#版式类型

常见 slideLayout.type 包括:

类型说明
TITLE标题页。
TITLE_AND_CONTENT标题和内容。
SECTION_HEADER章节标题页。
TWO_OBJ双内容区。
TWO_TX_TWO_OBJ对比版式。
TITLE_ONLY仅标题。
BLANK空白页。
VERT_TITLE_AND_TX竖排标题与文本。
CUST自定义版式。

#母版和版式对象

slideMasters[].childrenslideMasters[].slideLayouts[].children 使用与页面对象相同的 ElementNode 结构。它们通常用于表示占位符、页脚、日期、页码和默认文本样式。

占位对象通常具有以下特征:

  • type 多为 text
  • noDraw 可以为 true,表示该节点作为模板或占位定义存在。
  • extInfo.property.placeholder 描述占位符语义。
  • 文本样式可通过 pr 子节点提供默认段落样式和字符样式。

#占位符解析

页面生成或渲染时,可以按以下顺序处理占位符:

  1. 读取母版 children,建立全局占位符集合。
  2. 读取版式 children,覆盖或补充母版占位符集合。
  3. 读取页面 children,用真实页面对象替换对应占位符。
  4. 对没有被页面对象替换的占位符,根据 visiblenoDraw 决定是否渲染。

占位符匹配时可结合 placeholder.typeplaceholder.idx 和对象层级判断。

protocol/page.md

页面协议

页面结构、页面扩展信息和对象挂载方式。

源 Markdown

#页面协议

page 描述实际幻灯片页面。页面负责引用母版与版式,并承载当前页的真实对象树。

#页面结构

ts
type Page = {
  page: number
  extInfo: PageExtInfo
  children: ElementNode[]
}
字段类型是否必填说明
pagenumber页码。推荐使用从 1 开始的正整数。
extInfoPageExtInfo页面扩展信息。
childrenElementNode[]当前页的真实对象数组。

#页面扩展信息

ts
type PageExtInfo = {
  slideMasterIdx: number
  slideLayoutIdx: number
  background: Background
}
字段类型是否必填说明
slideMasterIdxnumber引用 slideMasters 的数组下标。
slideLayoutIdxnumber引用当前母版下 slideLayouts 的数组下标。
backgroundBackground页面级背景。

#页面解析

处理页面时,应按以下顺序解析:

  1. 读取顶层 widthheight,建立页面画布。
  2. 根据 extInfo.slideMasterIdx 读取对应 slideMaster
  3. 根据 extInfo.slideLayoutIdx 读取该母版下的 slideLayout
  4. 合成母版背景、版式背景和页面背景。
  5. 合成母版占位符、版式占位符和页面真实对象。
  6. 遍历 children 渲染页面对象树。

#背景优先级

背景可出现在三个层级:

层级字段优先级
母版slideMasters[].background最低
版式slideMasters[].slideLayouts[].background中等
页面pages[].extInfo.background最高

当多个层级都提供背景时,页面级背景优先。实现方也可以根据业务需要保留全部层级信息,用于编辑器展示或回写。

#页面对象

pages[].children 是页面实际对象列表。每个对象都遵循 object/README.md 中的 ElementNode 结构。

页面对象可以包含:

类型说明
text文本框、占位文本、形状文本。
image图片对象。
table表格对象。
container分组对象。

对象的绘制顺序通常与 children 数组顺序相关。实现方若需要调整层级,应明确维护对象顺序或引入额外层级字段。

#校验清单

处理页面前建议验证:

  • slideMasterIdx 指向存在的 slideMasters 元素。
  • slideLayoutIdx 指向对应母版下存在的 slideLayouts 元素。
  • background.anchor 与画布尺寸匹配或能被目标渲染器正确处理。
  • children 是数组。
  • children 中每个对象都有 idtypedepthextInfochildren

protocol/object/README.md

对象协议

页面对象树、对象类型和解析规则。

源 Markdown

#对象协议

object 描述 pptxProperty 中的页面对象树。页面、母版、版式里的可视内容都使用同一套节点结构,并通过 type 区分对象类型。

#文档入口

文档主题
common-property.md对象公共属性:位置、尺寸、旋转、填充、边框、阴影、占位符、超链接。
text.md文本对象、段落 p、文本片段 r、富文本组合方式。
image.md图片对象、图片资源、透明度、裁剪、图片填充。
table.md表格、行、单元格、合并单元格、边框和单元格文本。
container.md分组对象、坐标系、位置、缩放和子对象组合。
other-objects.md音频、视频等媒体类对象和轻量对象说明。

#对象节点

ts
type ElementNode = {
  id: string
  pid?: string
  type: ElementType
  depth: number
  point?: Box
  noDraw?: boolean
  text?: string
  extInfo: ElementExtInfo
  children: ElementNode[]
}

type ElementType =
  | "text"
  | "image"
  | "table"
  | "tableRow"
  | "tableColumn"
  | "p"
  | "r"
  | "container"
字段类型说明
idstring对象唯一标识。应在同一文档内保持唯一。
pidstring父对象 id。页面顶层对象通常不设置。
typeElementType对象类型。解析时先根据该字段分派到对应协议。
depthnumber节点深度。页面直接子对象通常为 1,子节点逐级递增。
pointBox对象在父坐标系中的外框。文本段落、文本片段、表格行等结构节点可以没有该字段。
noDrawbooleantrue 时表示该对象作为模板或占位定义存在,不应直接作为页面实物绘制。
textstring文本内容。主要用于 p 空段落和 r 文本片段。
extInfoElementExtInfo对象扩展信息。样式、资源和行为通常在 extInfo.property
childrenElementNode[]子对象数组。叶子节点使用空数组。
ts
type ElementExtInfo = {
  property?: Record<string, unknown>
  rowSpan?: number
  gridSpan?: number
  [key: string]: unknown
}

#树结构规则

text
pages[].children
├── text -> p[] -> r[]
├── image
├── table -> tableRow[] -> tableColumn[] -> p[] -> r[]
└── container -> (text | image | table | container)[]

slideMasters[].children
└── text 占位符

slideMasters[].slideLayouts[].children
└── text 占位符

#解析规则

  1. 读取 type,选择对应对象协议。
  2. 读取 point,建立对象外框。没有 point 的结构节点继承父对象布局上下文。
  3. 读取 extInfo.property,解析该对象的真实类型、几何、样式、资源和行为。
  4. 递归处理 children
  5. 未识别字段应保留透传,避免破坏向前兼容。

#对象类别

类别类型说明
文本text, p, r文本框、段落、富文本片段。
图片image图片,也可作为音频或视频的封面对象。
表格table, tableRow, tableColumn表格、行、单元格。
分组container分组和嵌套分组。
媒体image 上的 audiovideo 字段媒体资源通常挂载在图片对象的 property 上。

#公共属性概览

常见对象属性可分为:

分组属性
几何realType, anchor, shapeType, geometry, rotation
布局point, anchor, interiorAnchor, textInsets
外观fillStyle, strokeStyle, shadow, effectLst, imageAlpha, fillAlpha
占位placeholder, noDraw
交互hyperlink、媒体动画字段

公共属性的详细说明见 common-property.md

protocol/object/common-property.md

对象公共属性

对象共享的几何、填充、边框、阴影和交互属性。

源 Markdown

#对象公共属性

本文档描述多个对象类型共享的 extInfo.property 字段。具体对象文档只说明对象专有字段;遇到公共字段时应以本文为准。

#属性位置

对象样式通常位于:

text
ElementNode.extInfo.property

例如:

json
{
  "type": "text",
  "point": [80, 60, 320, 120],
  "extInfo": {
    "property": {
      "realType": "TextBox",
      "anchor": [80, 60, 320, 120],
      "rotation": 15,
      "fillStyle": { "type": "color", "color": { "realColor": -1, "color": -1 } }
    }
  },
  "children": []
}

#realType

ts
type RealType =
  | "Auto"
  | "TextBox"
  | "Picture"
  | "table"
  | "TableCell"
  | "Group"
  | "Background"
适用对象说明
Autotext自动形状文本或占位符文本。
TextBoxtext显式文本框。
Pictureimage图片对象。音频/视频封面也可以使用该类型。
tabletable表格对象。
TableCelltableColumn表格单元格。
Groupcontainer分组对象。
Background背景背景。

type 决定节点协议,realType 决定该节点在 PPTX 语义中的真实形态。实现方不应只依赖 realType 判断对象类型,因为文本、形状、占位符都可能使用相近的真实类型。

#anchor

ts
type Box = [x: number, y: number, width: number, height: number]

anchor 描述对象的几何区域。它与节点级 point 都是 [x, y, width, height],但用途不同:

字段作用
point对象在当前坐标系中的外框,主要用于布局和快速定位。
property.anchor对象真实几何锚点,生成 PPTX 或精确渲染时应使用该字段。

顶层对象的 pointanchor 通常一致。分组内部对象的 anchor 可以是相对坐标或归一化坐标,需要结合父分组的 interiorAnchor 转换。

#设置位置和尺寸

json
{
  "point": [100, 80, 240, 120],
  "extInfo": {
    "property": {
      "anchor": [100, 80, 240, 120]
    }
  }
}
  • xy 决定对象左上角。
  • widthheight 决定对象尺寸。
  • 宽高应大于 0。宽高为 0 时对象不可见或无法被正确选择。
  • 负坐标可以表示对象部分位于页面外,但第三方渲染器应明确是否支持。

#rotation

ts
type Rotation = number

rotation 表示对象绕自身中心旋转的角度,单位为度。正值通常表示顺时针旋转。

json
{
  "rotation": 45
}

设置效果:

效果
0 或缺失不旋转。
45顺时针旋转 45 度。
90旋转为竖向。
-30逆时针旋转 30 度。

建议将角度规范化到 -360..3600..360,避免不同渲染器对大角度值处理不一致。

#shapeType

shapeType 描述对象外框形状。当前常用值为 rect。当对象需要以圆角矩形、椭圆或其他形状裁剪时,可扩展为对应形状名称。

json
{
  "shapeType": "rect"
}

shapeType 应与 geometry.name 保持一致。若两者冲突,建议以 geometry.name 作为精确几何,以 shapeType 作为快速分类。

#geometry

ts
type Geometry = {
  name: string
  data: unknown | null
  avLst: unknown | null
  textBounds: unknown | null
}
字段说明
name几何类型,例如 recttableColumn
data自定义路径或几何数据。没有自定义几何时为 null
avLst形状调节点列表。没有调节点时为 null
textBounds文本边界信息。没有额外文本边界时为 null

geometry 决定对象实际轮廓。对于图片对象,几何会影响图片裁剪范围;对于文本对象,几何会影响背景填充、边框和文本排版区域。

#placeholder

ts
type Placeholder = {
  type: string
  idx?: number
  size?: string
  visible: boolean
}
字段说明
type占位符语义,例如 TITLEBODYCENTERED_TITLESUBTITLECONTENTPICTUREDATETIMEFOOTERSLIDE_NUMBER
idx占位符索引。同一版式内多个相同类型占位符用它区分。
size占位符尺寸级别,例如 halfquarter
visible占位符是否可见。

设置方式:

json
{
  "placeholder": {
    "type": "TITLE",
    "visible": false
  }
}

使用限制:

  • placeholder 主要用于母版、版式和页面中的占位文本或占位内容对象。
  • idx 应在同一版式范围内稳定,便于页面对象与占位符匹配。
  • visible: false 表示占位符默认不作为实物内容展示;编辑器仍可使用它提供插入位置和默认样式。

#fillStyle

ts
type FillStyle =
  | { type: "noFill" }
  | { type: "color"; color: ColorValue }
  | { type: "gradient"; gradient: GradientFill }
  | { type: "texture"; texture: TextureFill }

fillStyle 控制对象内部填充。文本框、形状、表格单元格、背景和图片都可以使用填充。图片对象通常使用 texture 填充。

#无填充

json
{
  "fillStyle": { "type": "noFill" }
}

效果:对象内部透明,只显示文本、边框或子内容。适用于无背景文本框。

限制:

  • noFill 不应同时带 colorgradienttexture
  • 若对象没有边框也没有文本,noFill 会让对象不可见,但对象仍可能可被选中。

#纯色填充

json
{
  "fillStyle": {
    "type": "color",
    "color": {
      "scheme": "accent1",
      "realColor": -12028725,
      "color": -12028725
    }
  }
}
字段说明
scheme可选。引用主题色槽位。
realColor实际渲染颜色。
color原始颜色或主题基础颜色。
alpha可选。透明度,范围建议 0..100000,值越大越不透明。
lumMod可选。亮度乘数。
lumOff可选。亮度偏移。
shade可选。变暗比例。
tint可选。变亮比例。

设置效果:

  • 只设置 realColor / color:使用纯色。
  • 设置 scheme:颜色可随主题变化。
  • 设置 alpha:颜色半透明,能看到底层内容。
  • 设置 lumMod / lumOff:在主题色基础上变亮或变暗。

realColorcolor 都使用核心协议中的 32 位有符号 ARGB 十进制颜色。常用转换示例:

  • #FFFFFF 写为 -1
  • #000000 写为 -16777216
  • #4874CB 写为 -12028725
  • rgba(0,0,0,0.17) 可写为 realColor: 721420288,并同时写入 alpha: 17000 保留透明度语义。

具体换算公式、十六进制和 rgba() 的处理规则见 ../core.md 的“颜色”章节。

#渐变填充

json
{
  "fillStyle": {
    "type": "gradient",
    "gradient": {
      "angle": 90,
      "colors": [
        { "realColor": -1, "color": -1 },
        { "realColor": -12028725, "color": -12028725 }
      ],
      "fractions": [0, 1],
      "gradientType": "linear",
      "insets": null,
      "flip": null,
      "tileRect": null,
      "rotWithShape": null
    }
  }
}
字段说明
angle渐变角度。常用于线性渐变方向。
colors渐变色标数组。
fractions色标位置数组,长度应与 colors 一致。通常使用 0..1
gradientType渐变类型,例如 linear
insets渐变区域内缩。没有内缩时为 null
flip翻转模式。没有翻转时为 null
tileRect平铺区域。没有平铺时为 null
rotWithShape是否随形状旋转。

限制:

  • colors.lengthfractions.length 应一致。
  • fractions 应按从小到大排列。
  • 只有当目标渲染器支持渐变时才应使用复杂渐变;否则建议降级为首个色标或平均色。

#纹理填充

ts
type TextureFill = {
  imageData: string
  contentType: string
  alpha: number
  flipMode: string
  stretch?: [left: number, top: number, right: number, bottom: number]
  insets?: [left: number, top: number, right: number, bottom: number]
  offset?: [x: number, y: number]
  scale?: [x: number, y: number]
  alignment?: string
  alphaModFix?: number
  rotWithShape?: boolean
}
字段说明
imageData图片资源。可以是 URL,也可以是 data:image/...;base64,...
contentTypeMIME 类型,例如 image/pngimage/jpeg
alpha图片透明度,100000 表示完全不透明。
flipMode翻转模式。常用 NONE
stretch拉伸填充参数。通常 [0,0,0,0] 表示完整图片拉伸到对象区域。
insets裁剪内缩参数。常用于图片裁剪。
offset平铺或缩放时的偏移。
scale平铺或缩放时的比例。
alignment对齐方式,例如 CENTER
alphaModFix透明度修正值。
rotWithShape是否随对象旋转。

#拉伸图片填充

json
{
  "type": "texture",
  "texture": {
    "imageData": "data:image/png;base64,...",
    "contentType": "image/png",
    "alpha": 100000,
    "flipMode": "NONE",
    "stretch": [0, 0, 0, 0]
  }
}

效果:图片被拉伸填满对象区域。对象宽高比与图片宽高比不一致时,图片会被压缩或拉伸变形。

#裁剪图片填充

json
{
  "type": "texture",
  "texture": {
    "imageData": "data:image/png;base64,...",
    "contentType": "image/png",
    "alpha": 100000,
    "flipMode": "NONE",
    "insets": [71667, 0, 0, 48134]
  }
}

效果:先按 insets 裁剪图片,再填充到对象区域。裁剪值通常使用 PPTX 百分比单位,正值表示从对应边向内裁剪。

#平铺或缩放图片填充

json
{
  "type": "texture",
  "texture": {
    "imageData": "https://example.com/image.jpg",
    "contentType": "image/jpeg",
    "alpha": 100000,
    "flipMode": "NONE",
    "offset": [0, 0],
    "scale": [0.1, 0.1],
    "alignment": "CENTER",
    "rotWithShape": true
  }
}

效果:图片按 scale 缩放后填充对象,可结合 offsetalignment 控制位置。适用于纹理、图案或保持图片原始比例的场景。

限制:

  • stretchscale / offset 不建议同时使用。若同时出现,应明确一个优先级。
  • imageData 为远程 URL 时,渲染端需要具备下载和缓存能力。
  • alphaalphaModFix 都会影响透明度,合成时应避免重复应用。

#strokeStyle

ts
type StrokeStyle = {
  paint?: FillStyle
  lineWidth?: number
  lineCap?: string
  lineJoin?: string
  lineDash?: string
  lineCompound?: string
}
字段说明
paint线条填充。使用 { "type": "noFill" } 表示无边框。
lineWidth线宽。值越大边框越粗。
lineCap线端样式,例如 FLAT
lineJoin转角连接样式,例如 ROUND
lineDash虚线样式,例如 SOLID
lineCompound复合线样式,例如 SINGLEDOUBLE

示例:

json
{
  "strokeStyle": {
    "paint": {
      "type": "color",
      "color": { "realColor": -16777216, "color": -16777216 }
    },
    "lineWidth": 2,
    "lineDash": "SOLID",
    "lineCompound": "SINGLE"
  }
}

#shadow

shadow 表示对象阴影。文本框、图片、形状、表格单元格等可视对象都可以支持阴影。

ts
type Shadow = {
  algn?: string
  blur?: number
  angle?: number
  distance?: number
  rotWithShape?: boolean
  scaleX?: number
  scaleY?: number
  fillStyle: FillStyle
}
字段说明
algn阴影相对对象的对齐位置。例如 t 表示顶部方向的对齐基准。
blur模糊半径。值越大,阴影边缘越柔和。
angle阴影方向角度。
distance阴影相对对象的偏移距离。
rotWithShape对象旋转时阴影是否一起旋转。
scaleX阴影横向缩放比例。大于 1 时阴影比对象更宽。
scaleY阴影纵向缩放比例。大于 1 时阴影比对象更高。
fillStyle阴影颜色和透明度。通常使用半透明颜色填充。

示例:

json
{
  "shadow": {
    "algn": "t",
    "blur": 47,
    "angle": 90,
    "distance": 4,
    "rotWithShape": false,
    "scaleX": 1.08,
    "scaleY": 1.08,
    "fillStyle": {
      "type": "color",
      "color": {
        "realColor": 721420288,
        "color": -16777216,
        "alpha": 17000
      }
    }
  }
}

设置效果:

  • 增大 blur:阴影更柔和,扩散范围更大。
  • 增大 distance:阴影离对象更远,方向由 angle 决定。
  • 设置较低 alpha:阴影更淡。
  • 设置 scaleX / scaleY 大于 1:阴影面积略大于对象,类似外阴影。

限制:

  • fillStyle 应使用颜色填充。渐变或图片阴影可能无法被所有渲染器支持。
  • blurdistance 不建议使用负数。
  • 如果对象本身透明或无填充,阴影仍应按对象几何轮廓计算。

#effectLst

ts
type EffectList = Record<string, unknown>

effectLst 用于保留额外效果列表。当前可为空对象。第三方实现如果不能识别其中的效果,应保留原值并透传。

ts
type Hyperlink = {
  type: string
  label?: string
  address: string
  action?: string
}
字段说明
type链接类型,例如 URL
label链接显示名称。可以为空字符串。
address链接地址或动作地址。
action动作指令。媒体播放可使用 ppaction://media

示例:

json
{
  "hyperlink": {
    "type": "URL",
    "label": "",
    "address": "ppaction://media",
    "action": "ppaction://media"
  }
}

媒体对象通常使用 ppaction://media 触发播放行为。普通网页跳转可把 address 设置为 URL。

protocol/object/text.md

文本对象

文本框、段落、文本片段和富文本组合。

源 Markdown

#文本对象

文本由三层节点组成:

text
text
└── p[]
    └── r[]
  • text 表示文本框或文本形状,负责位置、形状、背景、边框、阴影和文本容器设置。
  • p 表示段落,负责段落对齐、项目符号、缩进、行距和段间距。
  • r 表示文本片段,负责实际字符串和字符级样式。

#text

ts
type TextElement = ElementNode & {
  type: "text"
  point: Box
  extInfo: {
    property: TextProperty
  }
  children: ParagraphNode[]
}
ts
type TextProperty = {
  realType: "Auto" | "TextBox"
  anchor: Box
  placeholder?: Placeholder
  shapeType?: string
  fillStyle?: FillStyle
  strokeStyle?: StrokeStyle
  geometry?: Geometry
  shadow?: Shadow
  effectLst?: Record<string, unknown>
  rotation?: number
  textAutofit?: "NONE" | "NORMAL" | "SHAPE"
  textDirection?: "HORIZONTAL" | "EA_VERTICAL"
  textVerticalAlignment?: "TOP" | "MIDDLE" | "BOTTOM"
  textWordWrap?: boolean
  textInsets?: [number, number, number, number]
}
字段设置方式效果
realType显式文本框使用 TextBox;占位符或自动形状文本使用 Auto控制 PPTX 语义类型。
anchor[x, y, width, height]定义文本容器区域。
placeholder文本表示版式占位符时设置。用于版式匹配和默认样式继承。
shapeType通常为 rect决定外框形状类别。
fillStyle使用 noFillcolorgradienttexture设置文本框背景。
strokeStyle设置线条填充、线宽和虚线样式。设置文本框边框。
geometry通常为 { "name": "rect" }定义形状轮廓和文本边界。
shadow设置公共阴影对象。给文本框形状添加阴影。
rotation角度数值。旋转整个文本框。
textAutofitNONENORMALSHAPE控制文本如何适应框大小。
textDirectionHORIZONTALEA_VERTICAL横排或东亚竖排文本。
textVerticalAlignmentTOPMIDDLEBOTTOM控制文本在框内的垂直位置。
textWordWraptruefalse启用或禁用自动换行。
textInsets[top, right, bottom, left]设置边框和文本之间的内边距。

#文本自适应

行为
NONE文本保持原字号,可能溢出文本框。
NORMAL文本可以缩小以适配可用区域。
SHAPE形状或文本框可以调整,使内容更容易完整显示。

需要精确保留字号时使用 NONE。导入 PPTX 内容且希望尽量保持文字可见时使用 SHAPE

#自动换行和内边距

json
{
  "textWordWrap": true,
  "textInsets": [3.6, 7.2, 3.6, 7.2]
}
  • textWordWrap: true 让长文本在 anchor.width 范围内自动换行。
  • textWordWrap: false 允许长行横向溢出。
  • 左右内边距越大,可用文本宽度越小。
  • 上下内边距越大,可用文本高度越小。

#p

ts
type ParagraphNode = ElementNode & {
  type: "p"
  text?: string
  extInfo: {
    property: ParagraphProperty
  }
  children: RunNode[]
}
ts
type ParagraphProperty = {
  fontAlign?: "AUTO"
  textAlign?: "LEFT" | "CENTER" | "RIGHT"
  indent?: number
  indentLevel?: number
  bulletStyle?: BulletStyle
  leftMargin?: number
  lineSpacing?: number
  spaceBefore?: number
  spaceAfter?: number
}
字段设置方式效果
fontAlignAUTO使用自动字体对齐。
textAlignLEFTCENTERRIGHT控制段落水平对齐。
indent数值,可为负数。控制首行或项目符号缩进。
indentLevel非负整数。控制段落嵌套层级。
bulletStyle{ "buNone": true } 或项目符号字符配置。禁用或启用项目符号。
leftMargin数值。让段落内容离左边缘更远。
lineSpacing数值。控制行间距。值越大行距越松。
spaceBefore数值。增加段前间距。
spaceAfter数值。增加段后间距。可使用负数压缩段落间距。

#项目符号样式

ts
type BulletStyle =
  | { buNone: true }
  | { bulletCharacter: string; bulletFont: string }

无项目符号:

json
{
  "bulletStyle": { "buNone": true }
}

项目符号列表:

json
{
  "indent": -22.5,
  "leftMargin": 22.5,
  "bulletStyle": {
    "bulletCharacter": "•",
    "bulletFont": "Arial"
  }
}

设置项目符号时,通常同时设置:

  • leftMargin:正文整体左移距离。
  • indent:项目符号和正文的相对位置。负数会让项目符号位于正文左侧。
  • bulletCharacter:项目符号字符。
  • bulletFont:项目符号字体。

#r

ts
type RunNode = ElementNode & {
  type: "r"
  text: string
  extInfo: {
    property: RunProperty
  }
  children: []
}
ts
type RunProperty = {
  fontSize?: number
  bold?: boolean
  characterSpacing?: number
  fontFamily?: string
  fontColor?: { type: "color"; color: ColorValue }
  highlightColor?: number
  slideNum?: boolean
  lang?: string
}
字段设置方式效果
fontSize数值。设置文本字号。
boldtrue 或缺失。让该文本片段加粗。
characterSpacing数值。增加字符间距。
fontFamily字体名称。设置文本片段字体。
fontColor颜色填充对象。设置文本颜色。
highlightColor颜色数值。在文字字形后添加高亮。
slideNumtrue标记该文本片段为页码占位文本。
lang类似 BCP-47 的语言标签,例如 zh-CNen-US帮助字体回退和文本排版。

#富文本组合

一段富文本应该用一个 p 包住多个 r。每个 r 只负责一段连续样式一致的文本。

json
{
  "id": "text-1",
  "type": "text",
  "depth": 1,
  "point": [80, 80, 360, 120],
  "extInfo": {
    "property": {
      "realType": "TextBox",
      "shapeType": "rect",
      "anchor": [80, 80, 360, 120],
      "fillStyle": { "type": "noFill" },
      "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
      "textAutofit": "SHAPE",
      "textDirection": "HORIZONTAL",
      "textVerticalAlignment": "TOP",
      "textWordWrap": true,
      "textInsets": [3.6, 7.2, 3.6, 7.2]
    }
  },
  "children": [
    {
      "id": "p-1",
      "pid": "text-1",
      "type": "p",
      "depth": 2,
      "extInfo": {
        "property": {
          "textAlign": "LEFT",
          "leftMargin": 0,
          "lineSpacing": 120
        }
      },
      "children": [
        {
          "id": "r-1",
          "pid": "p-1",
          "type": "r",
          "text": "你好,",
          "depth": 3,
          "extInfo": {
            "property": {
              "fontSize": 18,
              "bold": true,
              "fontFamily": "苹方-简",
              "fontColor": {
                "type": "color",
                "color": { "realColor": -16777216, "color": -16777216 }
              },
              "lang": "zh-CN"
            }
          },
          "children": []
        },
        {
          "id": "r-2",
          "pid": "p-1",
          "type": "r",
          "text": "世界",
          "depth": 3,
          "extInfo": {
            "property": {
              "fontSize": 18,
              "fontFamily": "苹方-简",
              "fontColor": {
                "type": "color",
                "color": { "scheme": "accent1", "realColor": -12028725, "color": -12028725 }
              },
              "highlightColor": -256,
              "lang": "zh-CN"
            }
          },
          "children": []
        }
      ]
    }
  ]
}

渲染结果:

  • 你好, 加粗。
  • 世界 使用主题色并带高亮。
  • 两段文字在同一段落中连续排版。

#多段文本

多段文本用多个 p 表示。每个段落可以有不同对齐、项目符号和行距。

json
{
  "children": [
    {
      "id": "p-title",
      "type": "p",
      "depth": 2,
      "extInfo": { "property": { "textAlign": "CENTER", "spaceAfter": 8 } },
      "children": [
        { "id": "r-title", "type": "r", "text": "标题", "depth": 3, "extInfo": { "property": { "fontSize": 28, "bold": true } }, "children": [] }
      ]
    },
    {
      "id": "p-body",
      "type": "p",
      "depth": 2,
      "extInfo": {
        "property": {
          "textAlign": "LEFT",
          "leftMargin": 22.5,
          "indent": -22.5,
          "bulletStyle": { "bulletCharacter": "•", "bulletFont": "Arial" }
        }
      },
      "children": [
        { "id": "r-body", "type": "r", "text": "项目内容", "depth": 3, "extInfo": { "property": { "fontSize": 18 } }, "children": [] }
      ]
    }
  ]
}

#空段落

空段落可以使用:

json
{
  "id": "p-empty",
  "type": "p",
  "text": "",
  "depth": 2,
  "extInfo": {
    "property": {
      "textAlign": "LEFT",
      "leftMargin": 0
    }
  },
  "children": []
}

空段落用于保留换行、占位内容或编辑器光标位置。

protocol/object/image.md

图片对象

图片资源、不透明度、裁剪和媒体封面。

源 Markdown

#图片对象

image 表示图片对象。它也可以作为音频或视频媒体的封面对象,媒体资源通过额外字段挂在 extInfo.property 上。

#结构

ts
type ImageElement = ElementNode & {
  type: "image"
  point: Box
  extInfo: {
    property: ImageProperty
  }
  children: []
}
ts
type ImageProperty = {
  realType: "Picture"
  shapeType: string
  anchor: Box
  rotation?: number
  fillStyle: { type: "texture"; texture: TextureFill }
  geometry: Geometry
  shadow?: Shadow
  hyperlink?: Hyperlink
  fileName?: string
  image: string
  clipping?: [left: number, top: number, right: number, bottom: number]
  extension?: string
  contentType?: string
  imageAlpha?: number
  fillAlpha?: number
  audio?: string
  video?: string
  extContentType?: string
}
字段设置方式效果
realTypePicture标记该对象为图片形状。
shapeType通常为 rect定义外部裁剪形状。
anchor[x, y, width, height]设置图片位置和尺寸。
rotation角度数值。旋转图片对象。
fillStyle纹理填充。提供实际显示的图片像素。
geometry通常为 rect定义图片轮廓。
shadow公共阴影对象。添加图片阴影。
hyperlink超链接对象。添加点击动作;媒体对象使用 ppaction://media
fileName文件名。保留源文件身份。
imageURL 或 data:image/...主图片资源。
clipping四数值裁剪数组。显示前裁剪图片。
extension文件扩展名。辅助导出和 MIME 推断。
contentTypeMIME 类型。描述图片类型。
imageAlpha0..100000控制图片不透明度。
fillAlpha0..100000控制纹理填充不透明度。
audiodata:audio/... 或 URL。让该图片表示音频对象。
videodata:video/... 或 URL。让该图片表示视频对象。
extContentTypeMIME 类型。描述外部媒体类型,例如 audio/mp3

#基础图片

json
{
  "id": "image-1",
  "type": "image",
  "depth": 1,
  "point": [100, 80, 200, 120],
  "extInfo": {
    "property": {
      "realType": "Picture",
      "shapeType": "rect",
      "anchor": [100, 80, 200, 120],
      "fillStyle": {
        "type": "texture",
        "texture": {
          "imageData": "https://example.com/image.png",
          "contentType": "image/png",
          "alpha": 100000,
          "flipMode": "NONE",
          "stretch": [0, 0, 0, 0]
        }
      },
      "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
      "fileName": "image.png",
      "image": "https://example.com/image.png",
      "extension": ".png",
      "contentType": "image/png",
      "imageAlpha": 100000,
      "fillAlpha": 100000
    }
  },
  "children": []
}

#图片资源字段

除非有意区分缩略图和原图,否则 imagefillStyle.texture.imageData 应指向同一份视觉资源。

字段推荐用途
image需要读取图片内容的消费端使用的主资源。
fillStyle.texture.imageData形状填充或渲染逻辑使用的资源。
fileName可获得时保留原始文件名。
extension文件后缀,例如 .png.jpg
contentTypeMIME 类型,例如 image/pngimage/jpeg

使用 Base64 时,应包含完整的数据 URI 前缀:

text
data:image/png;base64,...

#不透明度

json
{
  "imageAlpha": 50000,
  "fillAlpha": 50000,
  "fillStyle": {
    "type": "texture",
    "texture": {
      "alpha": 50000
    }
  }
}

不透明度字段使用 0..100000

效果
100000完全不透明。
50000约 50% 不透明。
0完全透明。

除非渲染器明确需要分开处理,否则建议 imageAlphafillAlphatexture.alpha 使用相同值。

#裁剪

图片裁剪可以通过两个字段表达:

ts
type Crop = [left: number, top: number, right: number, bottom: number]
字段位置说明
clippingImageProperty对图片资源的裁剪参数。
fillStyle.texture.insetsTextureFill纹理填充裁剪参数。

推荐同时设置两者,并保持值一致:

json
{
  "clipping": [71667, 0, 0, 48134],
  "fillStyle": {
    "type": "texture",
    "texture": {
      "imageData": "data:image/png;base64,...",
      "contentType": "image/png",
      "alpha": 100000,
      "flipMode": "NONE",
      "insets": [71667, 0, 0, 48134]
    }
  }
}

裁剪效果:

  • left 增大:从图片左侧向内裁剪。
  • top 增大:从图片顶部向内裁剪。
  • right 增大:从图片右侧向内裁剪。
  • bottom 增大:从图片底部向内裁剪。

限制:

  • 裁剪值不应让可见区域宽度或高度变为 0 或负数。
  • 裁剪后图片仍会被映射到 anchor 区域。
  • 如果只设置 clipping 而不设置 texture.insets,部分渲染器可能只在导出阶段裁剪,预览不裁剪。

#拉伸、裁剪和平铺

模式字段效果
拉伸texture.stretch图片拉伸填满对象,可能改变比例。
裁剪clipping + texture.insets裁剪图片后填充对象。
平铺/缩放texture.scale, texture.offset, texture.alignment缩放或平铺图片,适合纹理图案。

详见 common-property.mdTextureFill

#图片作为媒体封面

音频或视频对象可以使用 image 作为封面/播放按钮。媒体资源放在 audiovideo 字段中。

json
{
  "type": "image",
  "point": [447.5, 237.5, 65, 65],
  "extInfo": {
    "property": {
      "realType": "Picture",
      "shapeType": "rect",
      "anchor": [447.5, 237.5, 65, 65],
      "fillStyle": {
        "type": "texture",
        "texture": {
          "imageData": "data:image/png;base64,...",
          "contentType": "image/png",
          "alpha": 100000,
          "flipMode": "NONE",
          "stretch": [0, 0, 0, 0]
        }
      },
      "hyperlink": {
        "type": "URL",
        "label": "",
        "address": "ppaction://media",
        "action": "ppaction://media"
      },
      "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
      "image": "data:image/png;base64,...",
      "audio": "data:audio/mp3;base64,...",
      "extContentType": "audio/mp3"
    }
  },
  "children": []
}

媒体播放相关信息还需要在页面 extInfo.animationExtextInfo.animation 中描述。详见 other-objects.md

protocol/object/table.md

表格对象

表格、行、单元格、合并单元格和单元格文本。

源 Markdown

#表格对象

表格由三层节点组成:

text
table
└── tableRow[]
    └── tableColumn[]
        └── p[]
            └── r[]

tableColumn 表示单元格,而不是物理列。单元格文本使用与普通文本相同的 p -> r 结构。

#table

ts
type TableElement = ElementNode & {
  type: "table"
  point: Box
  extInfo: {
    property: TableProperty
  }
  children: TableRowNode[]
}
ts
type TableProperty = {
  anchor: Box
  realType: "table"
  numberOfColumns: number
  numberOfRows: number
}
字段设置方式效果
anchor[x, y, width, height]定义完整表格区域。
realTypetable标记该节点为表格。
numberOfColumns正整数。定义逻辑列数。
numberOfRows正整数。定义逻辑行数。

numberOfColumnsnumberOfRows 描述逻辑网格大小。由于存在合并单元格,实际 tableRow.children.length 可以小于 numberOfColumns

#tableRow

ts
type TableRowNode = ElementNode & {
  type: "tableRow"
  extInfo: {
    property: TableRowProperty
  }
  children: TableColumnNode[]
}

type TableRowProperty = {
  rowHeight: number
}
字段设置方式效果
rowHeight大于 0 的数值。设置行高。除垂直合并单元格外,同一行内所有单元格都应对齐到该高度。

#tableColumn

ts
type TableColumnNode = ElementNode & {
  type: "tableColumn"
  extInfo: {
    rowSpan?: number
    gridSpan?: number
    property: TableCellProperty
  }
  children: ParagraphNode[]
}
ts
type TableCellProperty = {
  realType: "TableCell"
  anchor: Box
  fillStyle?: FillStyle
  strokeStyle?: StrokeStyle
  geometry?: Geometry
  textDirection?: "HORIZONTAL" | "EA_VERTICAL"
  textVerticalAlignment?: "TOP" | "MIDDLE" | "BOTTOM"
  textInsets?: [number, number, number, number]
  columnWidth?: number
  borders?: BorderStyle[]
}
字段设置方式效果
rowSpanextInfo 中的整数。将该单元格向下合并多行。
gridSpanextInfo 中的整数。将该单元格向右合并多列。
realTypeTableCell标记该节点为表格单元格。
anchor[x, y, width, height]定义实际单元格区域。合并单元格应有更大的宽度或高度。
fillStyle填充样式。设置单元格背景。
strokeStyle边框样式。设置默认单元格边框样式。
geometry通常为 tableColumn定义单元格几何。
textDirectionHORIZONTALEA_VERTICAL设置单元格文本方向。
textVerticalAlignmentTOPMIDDLEBOTTOM设置文本在单元格内的垂直对齐。
textInsets[top, right, bottom, left]设置单元格文本内边距。
columnWidth数值。一个逻辑列的基础宽度。
bordersBorderStyle[]设置每条边的边框。

#边框

ts
type BorderStyle = {
  color: ColorValue | null
  lineWidth: number | null
  lineCap: string | null
  lineDash: string | null
  lineCompound: string | null
}

borders 是四边边框数组。实现方应保持数组顺序稳定,并在自身系统中明确边顺序映射。若边框项字段为 null,表示该边使用默认样式或无显式设置。

示例:

json
{
  "borders": [
    { "color": null, "lineWidth": null, "lineCap": null, "lineDash": null, "lineCompound": null },
    { "color": null, "lineWidth": null, "lineCap": null, "lineDash": null, "lineCompound": null },
    { "color": null, "lineWidth": null, "lineCap": null, "lineDash": null, "lineCompound": null },
    { "color": null, "lineWidth": null, "lineCap": null, "lineDash": null, "lineCompound": null }
  ]
}

#合并单元格

#水平合并

水平合并使用 gridSpan。被合并的单元格只保留左上角主单元格,后续被覆盖的逻辑单元格不再单独出现。

json
{
  "id": "cell-1",
  "type": "tableColumn",
  "depth": 3,
  "extInfo": {
    "gridSpan": 2,
    "rowSpan": 1,
    "property": {
      "realType": "TableCell",
      "anchor": [100, 80, 200, 40],
      "columnWidth": 100,
      "geometry": { "name": "tableColumn", "data": null, "avLst": null, "textBounds": null },
      "textVerticalAlignment": "TOP",
      "textInsets": [3.6, 7.2, 3.6, 7.2]
    }
  },
  "children": []
}

设置效果:

  • gridSpan: 2 表示该单元格占用两个逻辑列。
  • anchor.width 应等于被合并列宽之和。
  • columnWidth 可保留单个基础列宽,便于还原网格。

#垂直合并

垂直合并使用 rowSpan

json
{
  "id": "cell-vertical",
  "type": "tableColumn",
  "depth": 3,
  "extInfo": {
    "rowSpan": 2,
    "gridSpan": 1,
    "property": {
      "realType": "TableCell",
      "anchor": [100, 80, 100, 80],
      "geometry": { "name": "tableColumn", "data": null, "avLst": null, "textBounds": null }
    }
  },
  "children": []
}

设置效果:

  • rowSpan: 2 表示该单元格占用两行。
  • anchor.height 应等于被合并行高之和。
  • 被覆盖行中不应再创建对应的独立单元格。

#单元格文本

单元格内容使用段落和文本片段:

json
{
  "id": "cell-1",
  "type": "tableColumn",
  "depth": 3,
  "extInfo": {
    "rowSpan": 1,
    "gridSpan": 1,
    "property": {
      "realType": "TableCell",
      "anchor": [100, 80, 100, 40],
      "fillStyle": {
        "type": "color",
        "color": { "scheme": "accent1", "realColor": -12028725, "color": -12028725 }
      },
      "geometry": { "name": "tableColumn", "data": null, "avLst": null, "textBounds": null },
      "textVerticalAlignment": "MIDDLE",
      "textInsets": [3.6, 7.2, 3.6, 7.2],
      "columnWidth": 100
    }
  },
  "children": [
    {
      "id": "cell-p-1",
      "pid": "cell-1",
      "type": "p",
      "depth": 4,
      "extInfo": {
        "property": {
          "textAlign": "CENTER",
          "bulletStyle": { "buNone": true },
          "leftMargin": 0
        }
      },
      "children": [
        {
          "id": "cell-r-1",
          "pid": "cell-p-1",
          "type": "r",
          "text": "表头",
          "depth": 5,
          "extInfo": {
            "property": {
              "fontSize": 18,
              "bold": true,
              "fontColor": {
                "type": "color",
                "color": { "realColor": -1, "color": -1 }
              }
            }
          },
          "children": []
        }
      ]
    }
  ]
}

#校验规则

  • table.children.length 应等于 numberOfRows
  • 每一行可见单元格跨度之和应等于 numberOfColumns
  • rowSpangridSpan 应为正整数。
  • 被合并覆盖的网格位置不应重复创建单元格。
  • 单元格 anchor 应与表格网格对齐。
  • 单元格文本应使用 p -> r,避免直接把原始文本放在 tableColumn 上。

protocol/object/container.md

分组对象

分组坐标系、嵌套、移动和缩放。

源 Markdown

#分组对象

container 表示分组对象。分组用于把多个对象作为一个整体移动、缩放或嵌套。

#结构

ts
type ContainerElement = ElementNode & {
  type: "container"
  point: Box
  extInfo: {
    property: ContainerProperty
  }
  children: ElementNode[]
}
ts
type ContainerProperty = {
  realType: "Group"
  anchor: Box
  interiorAnchor: Box
  rotation?: number
  shadow?: Shadow
}
字段设置方式效果
realTypeGroup标记该对象为分组。
anchor[x, y, width, height]定义分组在父坐标系中的外框。
interiorAnchor[x, y, width, height]定义子对象使用的内层坐标系。
rotation角度数值。同时旋转分组及其所有子对象。
shadow公共阴影对象。渲染器支持分组效果时,为分组边界添加阴影。

#坐标模型

分组有两个区域:

字段坐标系说明
point父坐标系分组可见外框。
anchor父坐标系或父级归一化空间分组放置区域。
interiorAnchor分组内部坐标系映射子对象坐标时使用的基准区域。

顶层分组通常使用页面坐标:

json
{
  "point": [300, 400, 200, 80],
  "extInfo": {
    "property": {
      "realType": "Group",
      "anchor": [300, 400, 200, 80],
      "interiorAnchor": [300, 400, 200, 80]
    }
  }
}

嵌套分组或导入数据中可能使用归一化坐标。归一化坐标需要结合父分组尺寸换算。

#位置

移动分组时,应同步更新:

  • point[0] / point[1]
  • property.anchor[0] / property.anchor[1]

如果分组使用绝对坐标,子对象 point 可以保持不变或按同样偏移更新,取决于实现方是否把子对象存储为绝对坐标。推荐策略:

存储策略移动分组移动子对象
子对象使用绝对坐标同时更新分组和子对象坐标。
子对象使用相对坐标只更新分组坐标。

实现方应在写入时保持同一种策略,避免同一分组内混合绝对和相对坐标。

#缩放

缩放分组时,应调整 anchor / point 的宽高,并按比例映射子对象。

text
scaleX = newGroupWidth / oldGroupWidth
scaleY = newGroupHeight / oldGroupHeight

child.x = group.x + (child.x - oldGroup.x) * scaleX
child.y = group.y + (child.y - oldGroup.y) * scaleY
child.width = child.width * scaleX
child.height = child.height * scaleY

如果子对象使用归一化坐标,缩放只需要更新父分组 anchor / point,子对象归一化坐标保持不变。

#嵌套分组

分组可以嵌套:

json
{
  "id": "group-1",
  "type": "container",
  "depth": 1,
  "point": [100, 100, 400, 120],
  "extInfo": {
    "property": {
      "realType": "Group",
      "anchor": [100, 100, 400, 120],
      "interiorAnchor": [100, 100, 400, 120]
    }
  },
  "children": [
    {
      "id": "group-2",
      "pid": "group-1",
      "type": "container",
      "depth": 2,
      "point": [120, 110, 180, 80],
      "extInfo": {
        "property": {
          "realType": "Group",
          "anchor": [120, 110, 180, 80],
          "interiorAnchor": [120, 110, 180, 80]
        }
      },
      "children": []
    }
  ]
}

嵌套分组解析时,应从外到内逐层建立坐标系。

#包含文本子对象的分组

json
{
  "id": "group-1",
  "type": "container",
  "depth": 1,
  "point": [80, 420, 360, 60],
  "extInfo": {
    "property": {
      "realType": "Group",
      "anchor": [80, 420, 360, 60],
      "interiorAnchor": [80, 420, 360, 60]
    }
  },
  "children": [
    {
      "id": "label-1",
      "pid": "group-1",
      "type": "text",
      "depth": 2,
      "point": [80, 420, 170, 60],
      "extInfo": {
        "property": {
          "realType": "Auto",
          "shapeType": "rect",
          "anchor": [80, 420, 170, 60],
          "fillStyle": { "type": "color", "color": { "realColor": -12028725, "color": -12028725 } },
          "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
          "textVerticalAlignment": "MIDDLE",
          "textInsets": [3.6, 7.2, 3.6, 7.2]
        }
      },
      "children": []
    }
  ]
}

#限制

  • 除非分组是临时编辑产物,否则 container.children 不应为空。
  • anchor.widthanchor.height 应大于 0
  • 子对象 pid 应指向分组 id。
  • 移动和缩放分组时,应保持子对象顺序不变。
  • 如果分组有旋转,子对象命中测试和渲染应先应用分组变换,再应用子对象自身变换。

protocol/object/other-objects.md

其他对象

音频、视频和页面媒体动画扩展。

源 Markdown

#其他对象

本文档描述当前协议中不需要单独完整章节的对象和扩展形态,包括音频、视频和媒体播放信息。

#媒体对象模型

音频和视频通常不是独立的顶层 type,而是挂载在 image 对象上:

  • image 对象提供页面上的可见封面、播放按钮或视频首帧。
  • audiovideo 字段保存媒体资源。
  • extContentType 保存媒体 MIME 类型。
  • hyperlink 使用 ppaction://media 表示点击触发媒体行为。
  • 页面 extInfo.animationExtextInfo.animation 描述播放动画、音量、循环和播放时长。

#音频

ts
type AudioImageProperty = ImageProperty & {
  audio: string
  extContentType: string
  hyperlink: Hyperlink
}
字段设置方式效果
audiodata:audio/...;base64,... 或 URL。提供音频二进制内容或资源地址。
extContentTypeMIME 类型,例如 audio/mp3告诉消费端如何解码媒体。
hyperlink.addressppaction://media标记点击动作为媒体播放。
hyperlink.actionppaction://media保留 PPTX 动作行为。

示例:

json
{
  "id": "audio-cover",
  "type": "image",
  "depth": 1,
  "point": [447.5, 237.5, 65, 65],
  "extInfo": {
    "property": {
      "realType": "Picture",
      "shapeType": "rect",
      "anchor": [447.5, 237.5, 65, 65],
      "fillStyle": {
        "type": "texture",
        "texture": {
          "imageData": "data:image/png;base64,...",
          "contentType": "image/png",
          "alpha": 100000,
          "flipMode": "NONE",
          "stretch": [0, 0, 0, 0]
        }
      },
      "hyperlink": {
        "type": "URL",
        "label": "",
        "address": "ppaction://media",
        "action": "ppaction://media"
      },
      "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
      "image": "data:image/png;base64,...",
      "contentType": "image/png",
      "audio": "data:audio/mp3;base64,...",
      "extContentType": "audio/mp3"
    }
  },
  "children": []
}

#视频

视频使用与音频相同的挂载方式,只是资源字段改为 video

ts
type VideoImageProperty = ImageProperty & {
  video: string
  extContentType: string
  hyperlink?: Hyperlink
}
字段设置方式效果
videodata:video/...;base64,... 或 URL。提供视频二进制内容或资源地址。
extContentTypeMIME 类型,例如 video/mp4告诉消费端如何解码媒体。
image缩略图、封面图或首帧。在幻灯片上显示媒体对象。
hyperlink通常为 ppaction://media点击播放行为。

示例:

json
{
  "type": "image",
  "point": [120, 90, 320, 180],
  "extInfo": {
    "property": {
      "realType": "Picture",
      "shapeType": "rect",
      "anchor": [120, 90, 320, 180],
      "fillStyle": {
        "type": "texture",
        "texture": {
          "imageData": "data:image/png;base64,...",
          "contentType": "image/png",
          "alpha": 100000,
          "flipMode": "NONE",
          "stretch": [0, 0, 0, 0]
        }
      },
      "geometry": { "name": "rect", "data": null, "avLst": null, "textBounds": null },
      "image": "data:image/png;base64,...",
      "video": "data:video/mp4;base64,...",
      "extContentType": "video/mp4"
    }
  },
  "children": []
}

#页面媒体动画

页面级 extInfo.animationExt 可描述媒体播放行为。

ts
type MediaAnimationExt = {
  elementId: string
  presetClass: "mediacall" | string
  presetId: number
  presetSubtype: number
  animation: string
  text: string | null
  startType: number
  attr: {
    media_vol?: string
    media_repeatCount?: string
    cmd?: string
    audio?: string
    video?: string
  }
  duration?: number
}
字段说明
elementId目标媒体对象 id。
presetClass媒体播放通常使用 mediacall
presetId动画预设 id。
presetSubtype动画预设子类型。
animation便于阅读的动画名称。
text可选文本说明。
startType播放触发类型。实现方应保留该值。
attr.media_vol音量,通常为 50% 这样的百分比字符串。
attr.media_repeatCount重复次数。
attr.cmd播放命令,例如 playFrom(0.0)
attr.audio目标为音频时为 true
attr.video目标为视频时为 true
duration媒体播放时长。

示例:

json
{
  "animationExt": [
    {
      "elementId": "audio-cover",
      "presetClass": "mediacall",
      "presetId": 1,
      "presetSubtype": 0,
      "animation": "媒体-播放",
      "text": null,
      "startType": 3,
      "attr": {
        "media_vol": "50%",
        "media_repeatCount": "1000",
        "cmd": "playFrom(0.0)",
        "audio": "true"
      },
      "duration": 3744
    }
  ]
}

#切换和原始动画

页面也可以包含 transitionanimation

json
{
  "transition": {
    "blinds": { "dir": "horz" },
    "spd": "slow"
  },
  "animation": {
    "tnLst": {}
  }
}

这些字段结构可能较深,第三方实现如果不需要编辑动画,建议完整保留并透传。

#兼容性说明

  • 媒体对象仍然按 image 对象渲染其封面。
  • 播放能力取决于运行环境是否支持对应 MIME 类型。
  • 媒体数据 URI 可能非常大,处理时应避免无意义复制。
  • 如果无法播放媒体,也应保留 audiovideoextContentTypehyperlink 和页面动画字段,以便回写时不丢失信息。