夜雨聆风学习资料网

ARTICLE · 1132305

OFD 主入口与文档根节点:标准第 7 章细读

OFD 主入口与文档根节点:标准第 7 章细读

很多 OFD 问题最后都会回到两个文件:

OFD.xml        包的唯一主入口Document.xml   某个 DocBody 的文档根节点

主入口决定“包里有哪些文书、签名和版本挂在哪”;文档根节点决定“这本文书如何组织页面、权限和资源”。GB/T 33190 第 7 章以及附录 A 的 Schema,对两者的元素顺序、必选性、类型和枚举都有硬约束。

本文按标准资料 OFD.xsd、Document.xsd、Definitions.xsd 细读这两层结构,说明每个字段的交换含义、常见生成错误,以及与 XSD / 语义校验的分工。

标准中的“应 / 可”与默认值,不等于具体阅读器一定会执行;本文不构成符合性认证。

一、两层入口各管什么

ZIP 包  └── OFD.xml                 主入口:DocBody 列表        ├── DocInfo           文书元数据        ├── DocRoot ──────────> Document.xml        ├── Versions?         版本入口        └── Signatures?       签名清单路径Document.xml  ├── CommonData              页区、资源、模板、MaxUnitID  ├── Pages                   页树(顺序 = 页序)  ├── Outlines? / Bookmarks?  导航  ├── Permissions?            权限声明  ├── Actions? / VPreferences?  ├── Annotations? / CustomTags?  └── Attachments? / Extensions?

要点:

  1. 一个包只能有一个 OFD.xml,文件名不应修改。
  2. DocBody 可重复
    一包多文书(多文档体)。
  3. 每个 DocBody 有自己的 DocInfo、DocRoot,以及可选的版本与签名入口。
  4. Document.xml
     里可选节点很多,但 CommonData 与 Pages 在结构上是骨架必选(Schema 中 Pages 无 minOccurs=0)。

二、OFD.xml:标准与 Schema 怎么写

1. 根元素固定项

Schema(对应附录 A)要点:

<xs:elementname="OFD"><xs:sequence><xs:elementname="DocBody"maxOccurs="unbounded"> ... </xs:element></xs:sequence><xs:attributename="Version"type="xs:string"use="required"fixed="1.0" /><xs:attributename="DocType"use="required"fixed="OFD" /></xs:element>
项
约束
含义
Version
必选,fixed 1.0
文档模型/格式版本,不是“你的业务版本号”
DocType
必选,枚举 OFD
文档子集类型
DocBody至少 1 个
,有序
多文书列表

命名空间(标准资料与 Schema):

http://www.ofdspec.org/2016

只写本地名、命名空间写错,XSD 与语义校验都会按“非 OFD 文档模型”处理。

最小合法骨架:

<?xml version="1.0" encoding="UTF-8"?><OFDxmlns="http://www.ofdspec.org/2016"Version="1.0"DocType="OFD"><DocBody><DocInfo><DocID>demo-001</DocID></DocInfo><DocRoot>Doc_0/Document.xml</DocRoot></DocBody></OFD>

2. DocInfo:文书级元数据

Schema 中 CT_DocInfo必选子元素只有 DocID,其余均可选:

DocID            必选,文书标识Title?           标题Author? Subject? Abstract?CreationDate? ModDate?   xs:dateDocUsage?        用途Cover?           封面 ST_LocKeywords?        Keyword 1..nCreator? CreatorVersion?CustomDatas?     Name + 文本值,用户自定义元数据

交换时的实用含义:

  • DocID 在包内应可区分不同文书
    多 DocBody 时不要全部复制同一 ID(合并工具会对重复 DocID 做处理,生成端最好一开始就不重复)。
  • Title
     常被阅读器用于标签页/大纲标题;空标题会退回文件名一类显示。
  • CreationDate
    /ModDate 是声明,不自动等于可信时间源;需要强时间证据仍要靠签名时间戳等机制。
  • CustomDatas
     是标准内建的轻量扩展元数据,适合放与文书绑定的业务键;不要与第 16/17 章的标引/扩展混为一谈。

3. Versions:可选的版本入口

<Versions><VersionID="V1"Index="1"Current="true"BaseLoc="/Doc_0/Versions/V1.xml"/></Versions>
属性
含义
IDxs:ID
,XML 层唯一
Index
版本序号
Current
默认 false;true 标识当前版本
BaseLoc
指向版本描述文件

版本描述文件(Version.xsd / DocVersion)包含:

FileList/File+   该版本涉及的文件(ST_Loc + ID)DocRoot          该版本的文档根属性:ID、Version、Name、CreationDate

标准语义:因注释或其它改动产生的多版本,通过入口 + 版本文件描述“这一版有哪些文件、根在哪”。生成器若不用多版本,整段省略即可,不要留空的 Versions 却无 Version 子节点(会违反 maxOccurs/结构)。

4. Signatures:签名清单指针

<Signatures>Signatures/Signatures.xml</Signatures>
  • 类型 ST_Loc,指向签名清单,不是某一个 Signature.xml。
  • 清单再指向各个签名文件——两级间接,便于一文书多签。
  • 无签名时不要写空元素占位(minOccurs=0,应省略)。

5. 多 DocBody 的交换含义

DocBody[0] -> Doc_0/...  全局页 1..nDocBody[1] -> Doc_1/...  其后页码继续

标准允许一包多文书;转换与阅读器通常按出现顺序展开为全局页序。生成端注意:

  • 各 DocRoot 路径基于包内定位写清。
  • 各文书资源默认按文档作用域理解,不要假设全包共享同一 ID 空间(字体等资源 ID 在不同文档体可重号,引用按文档解析)。
  • 签名入口是 per DocBody 的,不是整包唯一一处。

三、Document.xml:文档根节点骨架

1. 元素顺序受 Schema sequence 约束

Document.xsd 中顺序示意:

CommonDataPagesOutlines?Permissions?Actions?VPreferences?Bookmarks?Annotations?CustomTags?Attachments?Extensions?

生成器若随意打乱顺序,strict XSD 会失败。手工改 XML 时尤其容易把 Attachments 提到 Pages 前面。

2. CommonData:全局默认

<CommonData><MaxUnitID>100</MaxUnitID><PageArea><PhysicalBox>0 0 210 297</PhysicalBox></PageArea><PublicRes>PublicRes.xml</PublicRes><DocumentRes>DocumentRes.xml</DocumentRes><TemplatePageID="10"Name="Header"ZOrder="Background"BaseLoc="Templates/T0.xml"/><DefaultCS>1</DefaultCS></CommonData>
字段
标准含义
实务
MaxUnitID
已用最大对象 ID
编辑追加时从此递增;0 无效 ID
PageArea
默认页面区域
至少 PhysicalBox;缺页级 Area 时回退
PublicRes
 0..n
公共资源索引
字型、颜色空间“宜”放公共资源
DocumentRes
 0..n
文档自身资源索引
文档特有资源
TemplatePage
 0..n
模板页声明
ZOrder
: Background/Foreground
DefaultCS
默认颜色空间引用
缺省可按 RGB 理解,但建议显式

页面区域四盒(标准概念):

PhysicalBox     物理区域,必选,页面空间参考ApplicationBox  显示/打印区域ContentBox      版心BleedBox        出血,生产裁切

可选盒缺失、越界时标准有处理规则;生成端不要把“四盒都写满”当成唯一合法,也不要写宽高 ≤ 0 的 Box(ST_Box 要求宽高 > 0)。

3. Pages:页序的唯一权威

<Pages><PageID="1"BaseLoc="Pages/Page_0/Content.xml"/><PageID="2"BaseLoc="Pages/Page_1/Content.xml"/></Pages>

标准级事实:

  1. 顺序 = 阅读页序
    ,与文件夹名数字无关。
  2. ID
     为 ST_ID(无符号整数),供动作、注释、StampAnnot@PageRef 等引用。
  3. BaseLoc
     为 ST_Loc,相对当前文档根所在上下文解析。

常见错误:

  • 生成时按字符串排序文件名,导致 Page_10 插到 Page_2 前(若错误地用文件名排序)。
  • Page@ID
     与对象 ID 撞号且作用域未分清。
  • BaseLoc
     写成相对 ZIP 根或相对错误的上级目录。

4. 导航:大纲与书签

大纲 Outlines(树):

OutlineElem  @Title 必选  @Count? @Expanded 默认 true  Actions? -> Action...  OutlineElem*  递归子节点

书签 Bookmarks(平表):

Bookmark @Name 必选  Dest -> CT_Dest

CT_Dest(Definitions.xsd):

@Type 必选:XYZ | Fit | FitH | FitV | FitR@PageID 必选:ST_RefID@Left @Top @Right @Bottom @Zoom 可选

差别:

  • 大纲适合章节树 + 激活动作。
  • 书签适合命名位置快跳。
  • 两者都通过 Dest/PageID 挂到页 ID;页 ID 错则导航全错。

生成端应保证:大纲/书签引用的 PageID 必须存在于 Pages;未保留页(如抽页合并)要重写或丢弃失效目标。

5. Permissions:声明,不是 DRM

<Permissions><Edit>false</Edit><Annot>true</Annot><Export>false</Export><Signature>true</Signature><Watermark>true</Watermark><PrintScreen>true</PrintScreen><PrintPrintable="false"Copies="0"/><ValidPeriodStartDate="2026-01-01T00:00:00"EndDate="2027-01-01T00:00:00"/></Permissions>

Schema 中布尔项 default 多为 true,且均可 minOccurs=0:省略 ≈ 默认允许。

标准与实现的边界:

  • 节点表达文档作者的权限声明。
  • 阅读器是否强制拒绝打印/导出,取决于产品策略与威胁模型;项目支持清单也写明不会自动强制全部权限。
  • ValidPeriod
     是时间窗声明,不自动等于密码学有效期。

因此:写 Export=false 不能替代加密与访问控制;读到 Printable=true 也不能解释为“已授权打印具有法律意义”。

6. VPreferences:打开时的阅读偏好

PageMode     None/FullScreen/UseOutlines/UseThumbs/UseCustomTags/             UseLayers/UseAttatchs/UseBookmarksPageLayout   OnePage/OneColumn/TwoPageL/TwoColumnL/TwoPageR/TwoColumnRTabDisplay   DocTitle/FileNameHideToolbar/HideMenubar/HideWindowUI   默认 falseZoomMode     Default/FitHeight/FitWidth/FitRect或 Zoom      数值

注意 Schema 中存在历史拼写 UseAttatchs——生成时必须用枚举原值,不能“纠正”为 UseAttachments 否则 XSD 失败。

这些是查看偏好;WASM/桌面阅读器可能只实现子集(例如执行 PageMode/PageLayout/Zoom,忽略 HideToolbar)。偏好未实现时应视为兼容性缺口,而不是文件损坏。

7. 可选挂载点一览

节点
指向
标准章
Annotations
注释清单 ST_Loc
15
CustomTags
自定义标引清单
16
Attachments
附件清单
20
Extensions
扩展清单
17
Actions
文档级动作序列
14

全部 minOccurs=0:没有就省略。写了指针就必须保证目标文件存在且根元素正确——这是模型层检查,不是“ZIP 里碰巧有同名文件”就能过。

四、基础类型在两层里怎么用

类型
Schema
用在哪
校验注意
ST_Loc
anyURI
DocRoot、Res、BaseLoc、Signatures…
相对当前文件上下文;大小写敏感
ST_ID
unsignedInt
对象 ID
文档内唯一语义
ST_RefID
unsignedInt
引用 ID
必须解析到定义
ST_Box
string
PageArea 等
X Y W H
,W/H>0
xs:ID
XML ID
Version/@ID、签名清单 ID 等
XML 文档级唯一,另一套作用域

两类“ID”并存是标准实现里最容易混的点:

ST_ID/ST_RefID  -> OFD 对象号,语义按文档/资源类型解释xs:ID           -> XML 层 ID(如 Version、Signature 清单项)

校验器必须分开建索引;生成器不要假设“全包数字不重复就万事大吉”。

五、XSD 通过之后还要查什么

以主入口/文档根为例:

层次
检查
例子
XML/XSD
顺序、必选、fixed/枚举
Version
 必须 1.0;DocType=OFD
引用
Loc 文件存在、RefID 有定义
DocRoot
 可打开;DefaultCS 有指向
语义
ID 作用域、页序、重复 DocID
多文书 DocID、Page ID 被 Dest 引用
成像
页区回退、模板/图层可绘制
缺 PageArea 时 PhysicalBox 是否可推
签名
清单与 References
Signatures
 指针可解析、摘要匹配

项目校验对应关系(概念上):

strict  -> ZIP + XML + XSD + 引用 + 语义 + 摘要compat  -> XSD 错误降级为警告structural -> 跳过 XSD,仍做引用与语义

只跑 XSD、不跑引用语义,会漏掉“结构完美、DocRoot 路径写错”的坏包。

六、生成器检查清单

□ 仅一个 OFD.xml,UTF-8,命名空间正确□ Version=1.0,DocType=OFD□ 每个 DocBody:DocID 非空且不与其它文书冲突;DocRoot 存在□ 无签名则省略 Signatures;无版本则省略 Versions□ Document 元素顺序符合 Schema sequence□ CommonData:MaxUnitID、PageArea/PhysicalBox 合法□ Pages 至少一页;ID 唯一;BaseLoc 存在;顺序即页序□ 可选节点:要么省略,要么目标文件根元素正确□ Dest/PageID、Template@ID、DefaultCS 均可解析□ Permissions/VPreferences 用枚举原值(含 UseAttatchs)□ strict 校验错误 0

手工最小包与 ofd-creator 生成都建议最后跑:

go run ./cmd/ofd-validator --mode strict --format text document.ofd

七、阅读器解析顺序建议

1. 打开 ZIP,读唯一 OFD.xml2. 校验根元素、Version、DocType、命名空间3. 按序遍历 DocBody4. 解析 DocInfo(至少 DocID)5. 解析 ST_Loc -> Document.xml6. 读 CommonData(页区、资源、模板、MaxUnitID)7. 按序读 Pages,建立 ID -> 页面文件索引8. 再按需加载 Outlines/Permissions/Attachments/...9. 最后处理各 DocBody 的 Signatures 清单

先骨架后可选、先页序后内容,失败时才能明确报“入口错 / 根错 / 页树错 / 内容错”,而不是一律“文件损坏”。

结语:两个根文件定义“可交换”

可以把标准第 7 章收成两条链:

包级交换链  OFD.xml -> DocBody -> DocRoot / Signatures / Versions文书级组织链  Document -> CommonData + Pages -> Page Content           -> 可选导航/权限/注释/附件/扩展

主入口答对“有谁、根在哪、签挂哪”;文档根答对“怎么排页、默认什么、还能挂什么”。这两层闭合了,后面的成像与签名才有意义;这两层错了,再精细的 CTM 与字体也救不回一份无法交换的文件。

实现生成器或阅读器时,先把本章的 Schema 顺序、必选项和 ST_Loc 上下文钉死,再扩展第 8 章以后的绘制能力——这是标准阅读顺序,也是工程上最省返工的顺序。


参考资料:GB/T 33190-2016 第 7 章及附录 A;internal/schema/xsd/OFD.xsd、Document.xsd、Definitions.xsd、Version.xsd。

相关学习资料