ZDOC 是 ZerOS 的“Word 文档”格式。ZDOC 文件本质为 ZIP 压缩包,压缩完成后将扩展名重命名为 .zdoc。
.zdoc 的文件识别为 Word 文档(ZerOS Office Word)。ZDOC 压缩包内 MUST 具备如下结构(大小写敏感):
<root>/
Description.json
pages/
assets/
Description.json:文档描述文件,记录文档“全部属性”(见 2 章)。pages/:分页内容目录,存放每一页的页面信息与内容数据(见 3 章)。assets/:资源目录,存放文档引用的外部资源(图片/字体/附件等)(见 4 章)。Description.json MUST 是 UTF-8 编码的 JSON 文档。
{
"format": "zdoc",
"formatVersion": 1,
"id": "string",
"title": "string",
"createdAt": "number",
"updatedAt": "number",
"pageCount": "number"
}
format MUST 为 "zdoc"。formatVersion MUST 为整数,当前规范为 1。id MUST 为文档全局唯一标识(建议使用随机 ID)。title MUST 为文档标题(允许为空字符串)。createdAt、updatedAt MUST 为时间戳(毫秒)。pageCount MUST 为非负整数,表示页数;pages/ 下 MUST 存在从 zd0 到 zd<pageCount-1> 的连续目录。{
"author": "string",
"description": "string",
"language": "string",
"keywords": ["string"],
"pageSize": { "width": "number", "height": "number", "unit": "px" },
"margins": { "top": "number", "right": "number", "bottom": "number", "left": "number", "unit": "px" },
"defaultStyle": {
"fontFamily": "string",
"fontSize": "number",
"lineHeight": "number"
}
}
说明:
Description.json 中持续补充“全部属性”,新增字段 SHOULD 保持向后兼容。pageSize/margins/defaultStyle 为编辑器辅助字段,实现 MAY 用于画布/默认样式初始化;其单位/解释不影响本规范对响应式渲染的强制要求。pages/ 目录 MUST 存在。
pages/ 目录下 MUST 存在以页序号命名的子目录,命名规则如下:
zd<index>(小写),其中:
z 表示 ZerOSd 表示 zdoc<index> 为从 0 开始的非负整数<index> MUST 连续且无断档:zd0、zd1、zd2 ... 依次递增。zd0、zd2 但缺少 zd1),该 ZDOC MUST 视为无效。示例:
pages/
zd0/
zd1/
zd2/
每个页面目录 pages/zd<index>/ 下 MUST 存在以下三个文件(UTF-8 JSON):
page.json:仅用于标识该页的结构style.json:仅用于表示该页的样式content.json:仅负责该页的实际数据填充page.json 外层 MUST 为一个匿名对象,且:
structure 的数组metadata 的对象,用于描述该页元数据structure/metadata 之外的其他顶层字段structure 数组的长度 MUST 为 1 到 3(含),且 MUST 按位置表达含义:
structure.length === 1:structure[0] 为“内容对象”structure.length === 2:structure[0] 为“内容对象”,structure[1] 为“页首对象”structure.length === 3:structure[0] 为“内容对象”,structure[1] 为“页首对象”,structure[2] 为“页尾对象”structure.length === 0 或 structure.length > 3 的情况;出现即 MUST 视为无效structure 数组中的每个元素 MUST 为一个对象,其键名表示该对象的 id(在当前页面内唯一且不可重复)。并且:
id:MUST 存在,且 MUST 与键名一致type:MUST 存在,表示节点类型;其值 MUST 为以下之一:
text / audio / video / image / url / asset / containerchild:MAY 存在,表示子数据集合(仅 type === "container" 时解析)ID 隔离规则:
id 的唯一性约束仅在“单页范围”内生效zd0 与 zd1)允许出现相同的节点 id,互不干扰id 建议使用随机生成的 256 位唯一值(例如 64 位 16 进制字符串)type 字段的值决定节点如何被实现与解析:
type 为不被支持的值,该页 MUST 视为无效child 表示“包含关系”type === "container" 时,实现才会解析其 child(其余类型即使存在 child 也 MUST 不解析)节点示例:
{
"n1": {
"id": "n1",
"type": "container",
"child": {}
}
}
当 type === "container" 时,child MUST 存在且 MUST 为对象。child 对象下的每个键/值对 MUST 表示一个子节点,其中:
child 的键名仅作为映射键使用,实现 MUST 以节点对象中的 id 为准child 的值 MUST 为节点对象,且 MUST 包含 id/type;child MAY 存在container.child 下的子节点 id MUST 唯一;若出现重复,该页 MUST 视为无效type === "container",其 child MUST 存在且 MUST 为对象;递归适用于任意层级容器子项顺序(v1):
type === "container" 的节点对象 MAY 额外包含 order 字段(数组),用于声明该容器子项的渲染顺序order:
order MUST 为字符串数组order MUST 列出该 child 下所有子节点 id,且每个 id MUST 恰好出现一次order 顺序解析/渲染子项order:实现 MUST 按子节点 id 的字典序升序解析/渲染子项,以保证稳定顺序page.json 最小结构示例:
{
"structure": [
{
"n1": {
"id": "n1",
"type": "container",
"child": {}
}
}
]
}
metadata(可选)字段建议(v1,MAY):
{
"metadata": {
"createdBy": "string",
"updatedBy": "string",
"createdAt": "number",
"updatedAt": "number",
"tags": ["string"],
"note": "string"
}
}
说明:
createdAt/updatedAt 建议为毫秒时间戳style.json MUST 存在,且顶层 MUST 为一个匿名数组。
该数组 MAY 包含 N 个对象元素。每个对象 MUST 满足:
link:字符串,表示样式应用到 page.json 中某个节点的 idproperty:对象,表示样式属性集合link/property 之外的其他字段property 的字段集合取决于被链接节点的 type。实现 MUST 仅解析该 type 支持的 property-key,其他字段 MUST 忽略。
property MAY 使用一层嵌套对象表示语义分组样式(例如 border: { width, color })。实现 MUST 仅解析最多一层嵌套;若出现更深层级嵌套,实现 MUST 忽略该嵌套分支。
property 值类型约束(v1):
property 下的值 SHOULD 为字符串/数字/布尔建议语义分组(v1,MAY):
layout:尺寸与定位(width/height/min/max/align/translateX/translateY/top/left/right/bottom)spacing:间距(padding/margin/gap/indent/letterSpacing/wordSpacing)border:边框(borderWidth/borderColor/borderStyle/borderRadius)background:背景(backgroundColor)transform:变换(rotate/scale)text:文本(size/color/fontFamily/weight/style/decoration/lineHeight/whiteSpace/wrap/maxLines/ellipsis/align)media:多媒体(fit/position/poster/autoplay/loop/muted/controls/volume)link:链接(target/rel)asset:资源(label/icon/downloadName/openMode)分组 key 映射(v1):
layout.width → widthlayout.height → heightlayout.minWidth → minWidthlayout.maxWidth → maxWidthlayout.minHeight → minHeightlayout.maxHeight → maxHeightlayout.align → alignlayout.translateX → translateXlayout.translateY → translateYlayout.top → toplayout.left → leftlayout.right → rightlayout.bottom → bottomspacing.padding → paddingspacing.paddingTop → paddingTopspacing.paddingRight → paddingRightspacing.paddingBottom → paddingBottomspacing.paddingLeft → paddingLeftspacing.margin → marginspacing.marginTop → marginTopspacing.marginRight → marginRightspacing.marginBottom → marginBottomspacing.marginLeft → marginLeftspacing.gap → gapspacing.indent → indentspacing.letterSpacing → letterSpacingspacing.wordSpacing → wordSpacingborder.width → borderWidthborder.color → borderColorborder.style → borderStyleborder.radius → borderRadiusbackground.color → backgroundColortransform.rotate → rotatetransform.scale → scaletext.size → sizetext.color → colortext.fontFamily → fontFamilytext.weight → weighttext.style → styletext.decoration → decorationtext.lineHeight → lineHeighttext.whiteSpace → whiteSpacetext.wrap → wraptext.maxLines → maxLinestext.ellipsis → ellipsistext.align → textAlignmedia.fit → fitmedia.position → positionmedia.poster → postermedia.autoplay → autoplaymedia.loop → loopmedia.muted → mutedmedia.controls → controlsmedia.volume → volumelink.target → targetlink.rel → relasset.label → labelasset.icon → iconasset.downloadName → downloadNameasset.openMode → openMode单位约束(v1):
%)#RRGGBB)size)MUST 使用 rem 单位width/height)MUST 使用百分比(%)单位minWidth/maxWidth/minHeight/maxHeight/translateX/translateY/gap/padding/margin/borderWidth/borderRadius/letterSpacing/wordSpacing/indent)MUST 使用百分比(%)单位样式合并规则(v1):
link,实现 MUST 按数组顺序依次合并property-key 被重复赋值时:后出现的值 MUST 覆盖先出现的值支持的 property-key(v1,全部启用):
display:block|inline|noneopacity:0..1visible:booleanzIndex:integerrotate:degscale:numbertranslateX:%translateY:%align:left|center|rightbackgroundColor:#RRGGBBtype === "container":
width:%height:%minWidth:%maxWidth:%minHeight:%maxHeight:%padding:%paddingTop:%paddingRight:%paddingBottom:%paddingLeft:%margin:%marginTop:%marginRight:%marginBottom:%marginLeft:%gap:%direction:row|columnjustify:start|center|end|space-between|space-arounditems:start|center|end|stretchwrap:nowrap|wrapborderWidth:%borderColor:#RRGGBBborderStyle:solid|dashed|dotted|noneborderRadius:%shadow:字符串type === "text":
size:remcolor:#RRGGBBfontFamily:字符串weight:数字或 normal|boldstyle:normal|italicdecoration:none|underline|line-throughlineHeight:numberletterSpacing:%wordSpacing:%align:left|center|right|justifyindent:%whiteSpace:normal|nowrap|pre|pre-wrapwrap:word|char|nonemaxLines:integerellipsis:booleantype === "image":
width:%height:%fit:contain|cover|fill|none|scale-downposition:center|top|bottom|left|rightborderRadius:%opacity:0..1type === "video":
width:%height:%autoplay:booleanloop:booleanmuted:booleancontrols:booleanposter:字符串(资源引用)type === "audio":
autoplay:booleanloop:booleanmuted:booleancontrols:booleanvolume:0..1type === "url":
color:#RRGGBBdecoration:none|underlinetarget:_self|_blankrel:字符串type === "asset":
label:字符串icon:字符串(资源引用)downloadName:字符串openMode:preview|download最小示例:
[
{ "link": "n_container_1", "property": { "width": "100%", "height": "50%" } },
{ "link": "n_text_1", "property": { "size": "1rem", "color": "#333333", "lineHeight": 1.5 } }
]
content.json MUST 存在,且顶层 MUST 为一个匿名数组。
该数组 MAY 包含 N 个对象元素。每个对象 MUST 满足:
link:字符串,表示链接到 page.json 中某个节点的 idvalue:表示该节点的具体值;value MUST 为字符串link/value 之外的其他字段link 作用域(v1):
link MUST 指向该页内任意节点 id(包含 structure 三段根节点与任意层级 container.child 子节点)value 的取值约定(v1):
type === "text":value MUST 为字符串type === "asset":
value 为 URL 字符串(例如匹配 ^[a-zA-Z][a-zA-Z0-9+.-]*:):实现 MAY 视为外部链接value MUST 视为相对于 assets/ 目录的路径(例如 attachments/a.pdf)type === "image":
value 为 URL 字符串(例如匹配 ^[a-zA-Z][a-zA-Z0-9+.-]*:):实现 MAY 视为外部链接value 以 images/ 开头:value MUST 视为相对于 assets/ 的路径(例如 images/logo.png)value MUST 视为相对于 assets/images/ 的路径(例如 logo.png)type === "audio":
value 为 URL 字符串(例如匹配 ^[a-zA-Z][a-zA-Z0-9+.-]*:):实现 MAY 视为外部链接value 以 audios/ 开头:value MUST 视为相对于 assets/ 的路径(例如 audios/intro.mp3)value MUST 视为相对于 assets/audios/ 的路径(例如 intro.mp3)type === "video":
value 为 URL 字符串(例如匹配 ^[a-zA-Z][a-zA-Z0-9+.-]*:):实现 MAY 视为外部链接value 以 videos/ 开头:value MUST 视为相对于 assets/ 的路径(例如 videos/clip.mp4)value MUST 视为相对于 assets/videos/ 的路径(例如 clip.mp4)type === "url":value MUST 为 URL 字符串内容合并规则(v1):
link,实现 MUST 以数组中最后出现的一条为准最小示例:
[
{ "link": "n_text_1", "value": "Hello ZerOS" },
{ "link": "n_img_1", "value": "logo.png" },
{ "link": "n_url_1", "value": "https://example.com" },
{ "link": "n_asset_1", "value": "attachments/readme.txt" }
]
assets/ 目录 MUST 存在,用于存放资源文件。
assets/ 目录下 MUST 存在以下子目录(大小写敏感):
assets/
images/
audios/
videos/
attachments/
images/:图片资源目录audios/:音频资源目录videos/:视频资源目录attachments/:附件资源目录Description.json 中对资源的引用 MUST 使用相对于 assets/ 的路径,例如:images/logo.png。assets/images/assets/audios/assets/videos/assets/attachments/assets/attachments/。ZDOC 在被 ZerOS Office 打开前 SHOULD 做如下校验:
Description.json、pages/、assets/pages/ 下必须存在连续页目录 zd0..zdN,且 N = pageCount - 1assets/images/、assets/audios/、assets/videos/、assets/attachments/Description.json 可被 JSON 解析,且 format === "zdoc"、formatVersion 为受支持值page.json、style.json、content.json,且三者均可被 JSON 解析page.json 必须包含 structure 数组,且 structure.length 必须为 1..3structure 数组内每个元素必须为“单键对象”,其键名在当前页面内必须唯一structure 数组内每个节点值对象必须包含 id/type,且 id 必须与键名一致type 必须为受支持值(text/audio/video/image/url/asset/container),否则该页无效type === "container" 时,child MUST 存在且 MUST 为对象;child 下的每个值必须为节点对象(至少包含 {id,type}),且同一层级的子节点 id 必须唯一type === "container" 且存在 order 时:order 必须覆盖全部子节点 id 且无重复;实现按 order 排序content.json 顶层必须为数组;每个元素必须包含 link/value,且不得包含嵌套对象或嵌套数组content.json 中每个元素不得包含除 link/value 外的字段,且 value 必须为字符串style.json 顶层必须为数组;每个元素必须包含 link/property,且 property 必须为对象(允许一层嵌套)style.json 中每个元素不得包含除 link/property 外的字段content.json 与 style.json 中的 link SHOULD 指向该页内已存在的节点 id;若未命中,实现 SHOULD 忽略该条记录本节用于保证不同实现之间的预览与编辑行为一致。
page.json 的结构树生成渲染节点style.json 中同 link 的样式合并得到content.json 中同 link 的内容合并得到container 子项顺序 MUST 遵循 order 规则;无 order 时使用子节点 id 字典序升序page.json 中节点树与 container.order 的一致性content.json/style.json 中引用到已删除节点的条目(实现 SHOULD 删除或忽略)id SHOULD 使用随机 256 位唯一值,以避免同页冲突page.json 中节点的 id 字段child 对象中的键名(与新 ID 保持一致)order 数组中的 IDstyle.json 中所有引用该 ID 的 link 字段content.json 中所有引用该 ID 的 link 字段%)外,实现 MAY 也支持以下单位:
px:像素单位rem:相对单位(推荐用于文本)%),但实现可根据场景选择合适的单位text/url/asset):显示文本相关属性container):显示容器布局相关属性image/audio/video):显示媒体相关属性width/height/top/left/right/bottom):适用于所有节点类型