Help us shape the future of Go! Take the 2026 Go Developer Survey

ofd-creator

command
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

README

ofd-creator

ofd-creator 根据 JSON 或 YAML manifest 创建 OFD 文件包。

在 Go 程序中直接使用文档模型和创建 API,见 创建器库指南。

使用本工具创建或生成文档前,请阅读项目根目录的 免责声明。生成结果应根据实际业务、法律和合规要求进行独立复核。

构建

go build -o ofd-creator ./cmd/ofd-creator

用法

ofd-creator \
  --input document.yaml \
  --output result.ofd \
  --validate

只检查 manifest 和资源,不写出文件:

ofd-creator --input document.yaml --check

资源路径默认相对于 manifest 所在目录,也可以显式指定资源根目录:

ofd-creator -i document.yaml -o result.ofd --asset-root ./assets

输出目录不存在时会自动创建。生成文件先写入同目录临时文件,成功后再原子替换目标文件。

除创建外,ofd-creator 还提供 export、export-all、merge、replace、watermark 和 schema 子命令,分别用于导出 manifest、批量导出、合并 OFD、替换/新增/删除包内条目、添加/替换/删除水印以及输出 manifest 字段参考文档。详见下文各节。

manifest 字段参考

schema 子命令输出 manifest 全部字段的类型和说明,用于查证某个字段怎么写、含义是什么:

ofd-creator schema              # 输出 Markdown 到标准输出
ofd-creator schema -o schema.md # 写入文件
ofd-creator schema --format json

字段说明取自 internal/manifest/manifest.go 的文档注释,由 go generate ./internal/manifest 生成到 internal/manifest/fielddocs.go,因此不会与实现脱节。改动 manifest 的字段、类型或注释后必须重新生成,internal/manifest 的同步守卫测试会检查生成结果与源码是否一致。

结构体按首次到达的路径分组。manifest 的字段路径构成一棵树,而结构体构成一张图:复合对象可以嵌套复合对象,同一个结构体(例如 Item)会在几十条路径下出现,因此每个结构体只列一次,表中给出的路径是首次到达它的示例路径:

## Item

描述页面、模板或复合图形中的图元。

示例路径:`templates[].items[]`。

| 字段    | 类型     | 可省略 | 说明                           |
|---------|----------|:------:|--------------------------------|
| `type`  | `string` |   是   | 图元类型。                     |
| `items` | `[]Item` |   是   | 定义复合或页面块图元的子图元。 |

字段的完整路径是在示例路径后追加字段名,结构体字段还要追加 [],例如 pages[].items[].fill_color.axial.segments[]。

“可省略”列表示 manifest tag 是否带 omitempty,它只描述序列化行为,不等于“必填”:version 带 omitempty,但缺省或不是 1 仍会被拒绝。

--format json 输出同样的内容,供其它工具消费,字段名使用 omitempty 而不是含义模糊的 required。

导出 OFD 配置

支持将单文档体 OFD 导出为可再次用于 ofd-creator 的 YAML 或 JSON manifest,并将字体、图片和其他多媒体资源复制到资源目录。默认格式为 YAML:

ofd-creator export \
  --input input.ofd \
  --output exported/document.yaml \
  --asset-root assets

未指定 --format 时,会根据输出文件扩展名自动选择格式:.json 使用 JSON,.yaml/.yml 使用 YAML;标准输出或没有扩展名时默认使用 YAML,其他扩展名会被拒绝。也可以显式使用 --format json 或 --format yaml:

ofd-creator export \
  --input input.ofd \
  --output exported/document.json \
  --format json

ofd-creator export \
  --input input.ofd \
  --output exported/document.yaml \
  --format yaml

JSON 默认使用紧凑格式;需要便于阅读时增加 --json-indent,使用 2 个空格缩进:

ofd-creator export -i input.ofd -o exported/document.json --json-indent

导出的资源路径相对于 manifest 文件所在目录。当前导出支持文档元数据、页面尺寸、模板页、页面模板引用、动作、书签、大纲、文字、路径、图片、复合图元、字体、多媒体、绘制参数和基础坐标变换;不保证原始 OFD 的字节级还原。签名、附件、扩展和版本信息不会在第一版中完整保留。

包含多个文档体时,可以使用从 0 开始的索引选择其中一个文档体:

ofd-creator export \
  --input input.ofd \
  --document 1 \
  --output exported/document-1.yaml

不指定 --document 时,多文档输入会被拒绝,避免将不同文档体的元数据和资源错误合并。

也可以一次性导出全部文档体为目录包:

ofd-creator export-all \
  --input input.ofd \
  --output exported/

目录包结构如下:

exported/
  index.yaml
  documents/
    000.yaml
    001.yaml
    000-assets/
    001-assets/

index.yaml(或指定格式的 index.json)列出每个文档体的索引、ID、标题、manifest 路径、资源目录和页数。每个子 manifest 都可以独立执行普通创建命令;批量导出要求目标目录不存在,以避免覆盖已有文件。

合并多个 OFD

merge 子命令把多个 OFD 的文档体(DocBody)打包成一个多文档 OFD:

ofd-creator merge \
  --output merged.ofd \
  input1.ofd \
  input2.ofd

也可以用 --input/-i 重复指定输入文件:

ofd-creator merge -i input1.ofd -i input2.ofd -o merged.ofd

合并采用 ZIP 级方式:每个输入文档体的目录树原样复制到新的 Doc_0、Doc_1 … 目录,只重写 OFD.xml 中的 DocRoot、Signatures、版本 BaseLoc;页面、资源等内部 XML 不被解析或重写。因此:

  • 各文档体的字体、图片等资源 ID 保持独立作用域,不需要重映射。
  • 若合并后出现重复的 DocID,会为重复项自动生成新的 ID。

输入文件的文档目录必须在包内,且除 OFD.xml 外不能存在文档目录之外的条目,否则合并会失败。

签名处理

--signatures 控制签名文件的处理方式:

preserve   默认。签名文件字节保持不变。签名使用相对路径且文档目录名未变时签名完全有效;
           若签名使用包内绝对路径(如 /Doc_0/Pages/...)且文档被改名,无法同时保持引用有效
           和签名不变,此时直接报错,避免产出签名已失效的结果。
rewrite    重写签名文件中的包内绝对路径,使引用指向新目录。引用与摘要仍有效,但签名值
           (SignedValue)覆盖签名清单,重写后原签名值失效,需要重新签名。
drop       删除签名目录以及 OFD.xml 中的签名引用,产出无签名文档。
ofd-creator merge --signatures rewrite -o merged.ofd input1.ofd input2.ofd
ofd-creator merge --signatures drop -o merged.ofd input1.ofd input2.ofd

合并过程会把每个输入签名的处理结果(保留/重写/丢弃)汇总到标准错误。加 --verify-signatures 会在写出前解析输出文档,逐签名报告摘要匹配情况、SM2/SES 两层密码学签名结果,以及完整验签通过、失败或未完成的状态与原因:

ofd-creator merge -o merged.ofd --signatures rewrite --verify-signatures input1.ofd input2.ofd

摘要匹配、两层密码学签名通过仍可能伴随完整验签失败或未完成,例如算法声明 不一致、保护范围未确认或缺少证书信任及状态证据。此选项只报告结果,完整验签 失败或未完成本身不改变退出码;需要业务接受判断时,使用 sign.Verify 并检查 Result.FullyValid()。

注意 rewrite 会改写签名文件本身,可能使 SES 的数据摘要(对签名 XML 计算)失效,即使被引用文件摘要仍然匹配;这类签名需要重新签名。

签名清单和签名描述文件按主入口及清单中的路径定位,支持自定义文件名。 drop 只移除清单、签名描述、签名值和印章文件,共用目录中的普通文件会保留。 preserve 会核对搬运后的引用;签名保护了 OFD.xml 且合并将改变其字节时,返回错误,需选择 rewrite 后重签或 drop。 合并生成的主入口以及重写的签名、资源 XML 的 OFD 元素统一使用 ofd: 前缀。 ZIP 级合并读取的主入口和签名 XML,以及模型级合并重映射的资源 XML,使用 UTF-8 XML 1.0;DOCTYPE、自定义实体及非 UTF-8 编码会返回读取错误。

外部命令重签名

合并会重写资源名和路径,旧签名无法沿用。--sign-cmd 在合并完成后调用外部命令,为每个文档体追加一个新签名:

ofd-creator merge -o signed.ofd --pages 1 --sign-cmd ./ofd-signer --sign-id sign-1 --verify-signatures input.ofd

约定:

  • ofd-creator 已为每个文档体计算被引用文件的摘要,生成最终的 Signature.xml(SignedInfo,含各 Reference@FileRef 与 CheckValue,以及 SignedValue 路径),通过标准输入传给命令;
  • 命令向标准输出写 SignedValue.dat 字节(SES 结构,DataHash 是对输入 Signature.xml 字节的摘要),错误写标准错误;
  • 命令按空白拆分参数,不支持引号或 shell 语法;
  • 元数据通过环境变量传入:OFD_SIGN_DOCUMENT、OFD_SIGN_ID、OFD_SIGN_PROVIDER、OFD_SIGN_PROVIDER_VERSION、OFD_SIGN_COMPANY、OFD_SIGN_SIGNATURE_METHOD、OFD_SIGN_CHECK_METHOD、OFD_SIGN_TIME;
  • 私钥和密码学算法由命令负责,ofd-creator 不接触密钥。--sign-id 必须是合法的 XML xs:ID(默认 sign-1);
  • --sign-provider、--sign-provider-version、--sign-company、--sign-method、--sign-check-method 分别设置 SignedInfo 的 Provider 名称/版本/公司、SignatureMethod(默认 1.2.156.10197.1.501)和 References@CheckMethod(默认 SM3);这些值同时通过上面的环境变量传给命令。
  • 重签会删除文档体已有的签名文件,包括当前布局的 Signatures/ 和历史生产者使用的 Signs/(Signatures.xml/Signs.xml 及其目录),避免旧签名残留。
  • --sign-stamp 会在 Signature.xml 写入 StampAnnot,让阅读器把 SignedValue.dat 里的印章图片绘制到页面上;默认放在文档体首页右下角(40mm),可用 --sign-stamp-page 指定页面 ID、--sign-stamp-boundary "x y width height"(毫米)指定位置。真实印章图片来自签名值(外部命令输出的 SES_ESPictrueInfo),演示签名器 ofd-signer-demo 会运行时渲染带「非正式印章」声明的测试章 PNG。
  • --sign-stamp-seams 会把同一枚印章按参与页面数量切成条带,在同一文档体每页边缘写入一个 StampAnnot;--sign-stamp-seam-edge left|right|top|bottom|all 选择边缘,默认 right,all 会在每页同时覆盖四个边缘;左右边缘使用竖向条带,顶部和底部使用横向条带;--sign-stamp-seam-pages all|odd|even|1,3,5-7|11-|-10 选择参与页面,默认 all,页码从 1 开始,11- 表示第 11 页到末页,-10 表示第 1 页到第 10 页;--sign-stamp-seam-group-pages 20 可将参与页面按每 20 页分组,每组独立拼成一枚章;分组模式不能同时指定 --sign-stamp-seam-pieces。总页数不整除分组页数时,末尾不足两页的组按完整印章处理(单页无法自成一枚骑缝章,例如 5 页按 2 页分组得到 2+2+1,最后一页画整章)。未分组时,--sign-stamp-seam-pieces 可显式指定拆分份数,必须与参与页面数一致,省略时自动使用参与页面数。每个标注通过 Clip 显示不同条带,所有页面拼回完整骑缝章。定位按每页自身 Area/PhysicalBox 计算,缺失时回退文档公共 PageArea;印章默认沿对应边缘居中;--sign-stamp-seam-x 控制上下边缘的水平位置,--sign-stamp-seam-y 控制左右边缘的垂直位置,负值表示居中。--sign-stamp-seam-size 设置边长(默认 40mm),--sign-stamp-seam-min-strip 设置每页裁片最小宽度(默认 2mm,建议 4 或 8mm);不足时直接报错。该模式与 --sign-stamp 互斥,并要求阅读器支持 StampAnnot.Clip。
  • --sign-seal <路径> 指定独立的电子印章文件(.esl,可由 ofd-seal 生成)。提供后签名目录会同时包含 Seal.esl,且 Signature.xml 写入 <Seal BaseLoc="Seal.esl"/>,印章图片优先从该文件解析。

仓库提供最小示例签名器 cmd/ofd-signer-demo,可用来验证整条链路(它现场生成 SM2 自签名证书,签署 SES 印章与 TBS_Sign):

go build -o /tmp/ofd-signer-demo ./cmd/ofd-signer-demo
go run ./cmd/ofd-creator merge -o /tmp/signed.ofd --pages 1 \
  --sign-cmd /tmp/ofd-signer-demo --sign-stamp --verify-signatures testdata/ofd-creator/hello.ofd

多页文档可以按 OFDRW 样例使用 Clip 将同一枚印章切成条带,贴到每页右边缘:

go run ./cmd/ofd-creator merge -o /tmp/seam-signed.ofd \
  --sign-cmd /tmp/ofd-signer-demo \
  --sign-stamp-seams --sign-stamp-seam-size 40 \
  --verify-signatures testdata/ofdrw/999.ofd

每页写入一个 StampAnnot,所有标注引用同一份签章数据;页面尺寸可不同,位置会按每页自身的 Area/PhysicalBox 计算。

指定页码列表或范围:

go run ./cmd/ofd-creator merge -o /tmp/seam-selected.ofd \
  --sign-cmd /tmp/ofd-signer-demo \
  --sign-stamp-seams \
  --sign-stamp-seam-edge right \
  --sign-stamp-seam-pages 1,3,5-7 \
  --verify-signatures testdata/ofdrw/999.ofd

每 20 页重新拼一枚骑缝章。40mm 印章每页条带为 2mm,达到默认最小条带宽度;如果希望更清晰,可将印章边长提高到 80mm:

go run ./cmd/ofd-creator merge -o /tmp/seam-group20.ofd \
  --sign-cmd /tmp/ofd-signer-demo \
  --sign-stamp-seams \
  --sign-stamp-seam-edge right \
  --sign-stamp-seam-pages 5- \
  --sign-stamp-seam-group-pages 20 \
  --sign-stamp-seam-size 40 \
  --verify-signatures testdata/pdf2ofd/GBT_33190-2016.ofd

四边同时添加条带:

go run ./cmd/ofd-creator merge -o /tmp/seam-all-edges.ofd \
  --sign-cmd /tmp/ofd-signer-demo \
  --sign-stamp-seams --sign-stamp-seam-edge all \
  --sign-stamp-seam-size 40 \
  --verify-signatures testdata/ofdrw/999.ofd

重签名通常配合 --signatures drop(先清掉旧签名)或有 --pages 的模型级合并使用。

--deterministic 同样作用于签名后的重新打包(固定 ZIP 条目时间);但签名时间默认取当前时间,写入 Signature.xml 的 SignatureDateTime 会变化,若要完全可复现需要使用固定时间的签名命令,或直接用 pkg/sign 的 Options.Date。

merge 支持与创建命令相同的压缩策略和确定性选项,并可使用 --validate 在写出前执行严格校验:

ofd-creator merge -o merged.ofd --compression auto --deterministic --validate input1.ofd input2.ofd

--output - 可以把合并结果写入标准输出;merge 不支持从标准输入读取。

--orphans 控制文档目录之外的条目的处理方式:error(默认,直接失败)、ignore(跳过)或 preserve(按原路径保留到包根)。--signatures rewrite 重写签名路径时会向标准错误输出警告,说明哪些签名值已失效。

输入中的各文档体目录必须互不重叠;相同目录或父子嵌套目录会返回错误。

为防止恶意或异常文档造成解压放大,合并默认限制条目数 10000、单条解压 64MB、解压总计 512MB,可分别用 --max-entries、--max-entry-mb、--max-total-mb 调整。

合并逻辑同时以库的形式公开在 pkg/merge:Files(文件路径、输入按需读取)、Bytes(内存字节)、Sources(Path/Data/io.ReaderAt 三种来源)和 Marshal(返回完整字节)。

选页与重排

--pages 使用模型级合并,把各输入的页面解析后重新生成一个单文档 OFD,并按给出的页序输出。支持全局页序和按来源两种写法,来源序号从 1 开始、按 -i/位置参数顺序编号:

# 全局页序:按输入顺序拼接后的第 1、3-5 页
ofd-creator merge -o selected.ofd --pages 1,3-5 input1.ofd input2.ofd

# 按来源:第 1 个输入的第 2 页,再第 2 个输入的全部页面
ofd-creator merge -o selected.ofd --pages "s1:2;s2" input1.ofd input2.ofd

# 重排
ofd-creator merge -o reversed.ofd --pages 2,1 input1.ofd input2.ofd

模型级合并会自动重命名/重编号文档级资源(字体名、绘制参数名、图片/颜色空间/复合图元/模板 ID)并改写引用;签名和版本会丢弃,大纲、书签、动作和页面注解会保留并重写跳转页索引。因此不能与 --signatures、--orphans、--max-* 同时使用。输出文档元数据默认沿用首个来源,可用 --document-id、--title、--author 覆盖;--workers 控制并行解析输入的并发数(默认 4),与 ofd-converter --workers 命名一致。

替换、新增与删除包内条目

replace 子命令按包内路径替换、新增或删除条目,不解析页面或资源模型,其余条目原样搬运:

ofd-creator replace -i in.ofd -o out.ofd \
  --set 'Doc_0/Pages/Page_0/Content.xml=./content.xml' \
  --set 'Doc_0/Res/Image_3.png=./logo.png' \
  --add 'Doc_0/Res/extra.txt=./extra.txt' \
  --delete 'Doc_0/Thumbnail.xml'
  • 条目路径为包内路径,不带前导斜杠,例如 Doc_0/Pages/Page_0/Content.xml。
  • --set 要求目标已存在(不存在报错),--add 要求目标不存在(已存在报错),--delete 只给路径。三者均可重复;同一路径出现在多个操作中会报错。执行顺序为 delete → set → add。
  • NAME=FILE 以第一个 = 切分,FILE 可以含 =;FILE 为 - 时从标准输入读取内容。
  • 任何字节改动都会使已有签名摘要失效,因此 --signatures 默认 drop(丢弃签名目录),也可显式选择 preserve 或 rewrite;rewrite 与 preserve 等价,因为路径不会改变。
  • 新内容若命中 .xml 条目(--set/--add),默认会先解析校验良构性(含根元素);可用 --no-validate 关闭。--validate 则是在替换后对整体输出执行严格 OFD 校验,两者作用不同。
  • --verify-signatures 在替换后校验输出文档的签名摘要与密码学签名:只报告结果不因摘要失效而失败,签名结构损坏等输出级错误返回资源错误退出码(与 merge --verify-signatures 一致)。
  • 也可以像 merge 一样在替换后直接追加签名:--sign-cmd 调用外部命令为输出签名,--sign-id/--sign-provider/--sign-provider-version/--sign-company/--sign-method/--sign-check-method/--sign-stamp/--sign-stamp-page/--sign-stamp-boundary/--sign-stamp-seams/--sign-stamp-seam-edge/--sign-stamp-seam-pages/--sign-stamp-seam-pieces/--sign-stamp-seam-size/--sign-stamp-seam-x/--sign-stamp-seam-y/--sign-seal/--sign-include/--sign-exclude/--sign-root 的含义与 merge 的 --sign-* 一致。--signatures 默认 drop 会先丢弃旧签名,再按替换后的内容签署新签名。
  • 支持 --compression、--deterministic、--validate,以及 --max-entries/--max-entry-mb/--max-total-mb 解压规模限制。
  • --output - 可以把结果写入标准输出;replace 不支持从标准输入读取 OFD。

替换逻辑同时以库的形式公开在 pkg/replace:Files(任意输入 + []Operation)和 Paths(name -> 本地文件 映射,等价于一组 set)。

添加、替换与删除水印

watermark 子命令在文档级或页面级操作 Type="Watermark" 注解,自动维护各页注解文件、Annotations.xml 索引与 Document.xml 的 <Annotations> 声明,无需手工指定包内路径:

# 在全部页面上添加一条文字水印,平铺铺满整页(默认布局)
ofd-creator watermark add -i in.ofd -o out.ofd --text "保密资料" --font-size 9

# 单条文字水印居中放置并降低不透明度
ofd-creator watermark add -i in.ofd -o out.ofd --text "定稿" --layout center --opacity 50

# 文字水印整体倾斜 45 度(屏幕顺时针:左边往上、右边往下),绕文本中心旋转
ofd-creator watermark add -i in.ofd -o out.ofd --text "保密资料" --rotate 45

# 右端上抬(屏幕逆时针)用负值
ofd-creator watermark add -i in.ofd -o out.ofd --text "保密资料" --rotate -45

# 图片水印:自动嵌入图片资源并平铺,PNG 支持 --opacity 烘焙半透明
ofd-creator watermark add -i in.ofd -o out.ofd --image logo.png --image-width 40 --opacity 60

# 只修改第 1 页(下标 0)上 ID 为 5 的水印,外观使用自备 XML 片段
ofd-creator watermark replace -i in.ofd -o out.ofd --page 0 --match-id 5 \
  --appearance ./watermark.xml

# 删除文档体 0 中 ID 为 6 的水印
ofd-creator watermark remove -i in.ofd -o out.ofd --document 0 --match-id 6
  • 操作与目标:--document 选择文档体(默认 -1 全部);--page 是 0 起的页面下标,可重复,省略表示全部页面;--match-id 仅对 replace/remove 生效,省略表示匹配全部水印注解。加水印在目标范围内每个页面各加一条。
  • 外观三选一:--text 自动生成文字外观(可配 --font/--font-size/--color "R G B"/--text-x/--text-y/--boundary "X Y Width Height"/--opacity/--layout/--rotate),--image 生成图片水印(自动写入 DocumentRes.xml 的 <MultiMedias> 与图片条目),--appearance 直接读取外观 XML 片段文件;三者互斥。都不给时外观为空 <Appearance/>。replace/remove 会回收不再被任何页面引用、且由水印命名的图片条目;多页共享同一图片时仍被引用则不回收。
  • 布局与透明度:--layout tile(默认,按 --boundary 区域平铺网格)或 center(区域内单条居中);--text-x/--text-y 在平铺/居中时作为整体偏移。--opacity 取值 0-100(0 表示不设置):文字水印写成 Alpha 属性;PNG 图片水印烘焙进图片 alpha 通道(--image-width 毫米,高度按图片像素比例推算)。
  • 文字旋转:--rotate 以度为单位,让文字绕每个文本实例自身的中心倾斜,旋转时位置不偏移;正值屏幕顺时针(左边往上、右边往下,\ 形),负值右端上抬(屏幕逆时针,/ 形)。仅对 --text 生效。
  • 字体:文字水印的 --font 接受文档资源中(同一文档体的 PublicRes 与 DocumentRes)的字体 ID 或名称,命中名称时会解析为对应数值 ID 再写入外观;省略 --font 时自动选用文档中最小 ID 的字体。显式指定且文档中不存在时直接报错,不会写出非法的默认字体引用。
  • 注解属性:--id(0 自动分配,取 MaxUnitID 与页面内既有注解 ID 之后)、--creator、--subtype、--visible/--print/--no-zoom/--no-rotate、--read-only、--remark、--parameter Name=Value(可重复)。
  • 默认门控:文档 Permissions/Watermark=false 时拒绝任何修改(--skip-permissions-check 跳过);ReadOnly 缺省或为 true 的水印拒绝 replace/remove(--skip-readonly-check 跳过)。本命令写出的水印显式标记 ReadOnly=false,可直接再删。
  • 清理:remove 后某页无任何注解会删除该页注解文件,索引与 Document.xml 的 <Annotations> 同步清理。
  • 复用 replace 框架,因此 --compression/--compression-level/--deterministic/--validate/--max-entries/--max-entry-mb/--max-total-mb/--signatures 行为一致:任何修改都会使既有签名摘要失效,--signatures 默认 drop。也可以像 replace/merge 一样在修改后直接追加签名:--sign-cmd 调用外部命令为输出签名,--sign-id/--sign-provider/--sign-provider-version/--sign-company/--sign-method/--sign-check-method/--sign-stamp/--sign-stamp-page/--sign-stamp-boundary/--sign-stamp-seams/--sign-stamp-seam-edge/--sign-stamp-seam-pages/--sign-stamp-seam-pieces/--sign-stamp-seam-size/--sign-stamp-seam-x/--sign-stamp-seam-y/--sign-seal/--sign-include/--sign-exclude/--sign-root 的含义与 merge/replace 的 --sign-* 一致;--verify-signatures 在写出前校验输出文档的签名摘要与密码学签名。--output - 写标准输出,不支持从标准输入读取。
  • 保留原始命名空间风格:默认 xmlns 文档的水印使用无前缀元素,xmlns:ofd 文档的新增索引/页面文件以及 --appearance 原始外观片段均套用 ofd 前缀;无既有注解索引时从 Document.xml 根元素继承前缀。自备外观片段无法解析时直接报错,不会静默写成空 <Appearance/>。
  • 水印编辑逻辑以库的形式公开在 pkg/watermark:Add、Replace、Remove 三个函数接收任意输入(路径/字节/io.Reader/*core.Package)与 Target,外观自动分配 ID;Watermark.Appearance 可直接放入原始 XML 片段,或用 watermark.TextAppearance/watermark.ImageAppearance 生成平铺/居中的文字或图片外观(TextOptions.Rotation 指定文字旋转角度,TextOptions.CTM 直接透传变换矩阵、优先级更高);Watermark.Image 提供图片水印的资源嵌入能力。

OFD profile(DocType)

OFD.xml 根节点的 DocType 属性声明文档遵循的 profile,取值区分大小写:

OFD     基础版式文档(GB/T 33190—2016),默认值
OFD-A   档案长期保存(GB/T 42133—2022《信息技术 OFD档案应用指南》)
OFD-H   电子病历版式文档(GB/T 48666-2026《电子病历版式文档技术要求》,征求意见稿)

GB/T 33190 把该属性固定为 OFD,后两份标准在它之上把取值收紧为 OFD-A 与 OFD-H,因此生成这两类文件必须显式声明。OFD-A 与 OFD-H 是标准之间的叠加关系:OFD-H 在 OFD-A 的基础上追加医疗领域要求。

取值来源有两处,命令行优先:

# 命令行指定
ofd-creator -i document.yaml -o result.ofd --doc-type OFD-A

# 或在 manifest 的 document 下声明
version: 1
document:
  id: archive-001
  doc_type: OFD-A

两处都留空时使用 OFD。传入未知取值(包括大小写错误,如 ofd-a)会报错并列出可用取值,不会静默写入。

ofd-creator export 在导出 manifest 时会保留非基础取值,因此 export 后再 ofd-creator 重建不会把 profile 退回 OFD。

--validate 会按生成的 DocType 自动应用对应校验:OFD-A 与 OFD-H 除 GB/T 33190 基础模式与 XSD 外,还会执行 ofd-validator 的 profile 规则。但标准的「去除×××」一类条款本质是转换动作而非合规条件,归档处理流水线负责,校验器只判定并上报;字型子集化、图像插值、扫描件分层等需要阈值或启发式判断的条款也尚未实现。

压缩策略

auto       图片、音频、视频、PDF 和压缩归档使用 Store,其他文件使用 Deflate
deflate    所有文件使用 Deflate
store      所有文件使用 Store

--compression-level 控制 Deflate 的压缩级别:0(默认)使用默认级别 5,显式范围 1(最快、压缩率最低)到 9(最慢、压缩率最高)。级别只影响实际使用 Deflate 的条目;--compression store 或 auto 选择 Store 的条目(图片、PDF 等)不受影响。

例如:

ofd-creator -i document.yaml -o result.ofd --compression deflate --compression-level 1

流式创建超大 OFD

默认情况下,通过 file 引用的图片、多媒体、附件和封面会先读入内存,整个 OFD 也会先在内存中生成后写出。创建包含大资源或超大页数的 OFD 时,可以使用 --stream:

ofd-creator -i document.yaml -o result.ofd --stream --validate

--stream 会:

  • 通过 file 引用的封面、图片、多媒体、附件、页面图片、字体、公共/页面资源文件、扩展数据文件和印章文件使用惰性文件来源,按需流式读取,不整体驻留内存。
  • 直接以流式方式写出 OFD 到目标文件,不再把整个文件包驻留内存。

字体默认会按实际使用的字形子集化,此时会逐个字体读取来源并物化后再子集化,因此常驻内存上限约为单个原始字体加全部子集结果之和;不使用子集化时字体按来源流式写入。

流式模式要求 --output 是文件路径,不能配合 -o - 写入标准输出。启用 --validate 时会在临时文件上执行严格校验,通过后再原子替换目标文件。

Manifest 示例

version: 1

document:
  id: demo-document
  title: 示例文档
  author: creator
  page_size:
    name: A4

resources:
  fonts:
    - name: SimSun
      file: fonts/simsun.ttf
      format: ttf
  images:
    - id: 100
      file: images/logo.png
      format: PNG

pages:
  - items:
      - type: text
        x: 20
        y: 30
        width: 100
        height: 10
        value: 你好,OFD
        font: SimSun
        size: 4.23
      - type: image
        x: 20
        y: 50
        width: 40
        height: 30
        resource_id: 100

字体资源支持 ttf、otf 和 ttc。对于包含多字体 CFF 的 TTC,创建器会提取第一个字体面并保留其完整字形数据,不进行不安全的 CFF 子集化,以保证阅读器能够正常加载。

支持以下页面图元:

  • text
  • path
  • image
  • composite
  • page-block
  • table

table 图元在页面上绘制表格,支持列宽(column_widths)、表头行数(header_rows,跨页重复)、单元格合并(单元格的 col_span/row_span)、自动跨页、表题与续表题(caption/continuation_caption,后者支持 {caption} 占位符)。style 可分别控制横线/竖线开关、内部线宽(line_width)、外框线宽(outer_line_width)、表头分隔线宽(header_line_width)与表头字体字号(header_font/header_size/header_weight)。单元格支持对齐(align:left/center/right)、缩进(first_line_indent/hanging_indent,单位毫米)与自动换行:value 中的 \n 强制换行,超宽时按宽度自动断行(CJK 逐字断、ASCII 按词断)。单元格设 format: markdown 时按行内 Markdown 解析:**粗体**、*斜体*、`代码`(用 style.code_font 指定的字体)、~~删除线~~;样式变化处会拆成多个文字对象。表格在生成阶段展开为标准的 PathObject 与 TextObject,不引入新的 OFD 元素。完整示例见 cmd/ofd-creator/examples/table.yaml。

还支持 draw_params、color_spaces、templates、layers、ctm、fill_color、stroke_color、text_codes、cg_transforms、渐变和 Pattern 填充。

完整的 YAML 渐变实例见 cmd/ofd-creator/examples/gradients.yaml,包含轴向、径向、Gouraud、LaGouraud 和 Pattern 填充。

楷体文字样式实例见 cmd/ofd-creator/examples/kaiti-styles.yaml,包含多种字号、字重、斜体、描边和字宽设置。

字体样式矩阵实例见 cmd/ofd-creator/examples/font-styles.yaml,覆盖 100--900 字重、斜体、HScale、填充/描边及轴向/径向渐变文字。

绘制参数实例见 cmd/ofd-creator/examples/drawparam.yaml,覆盖线宽、Cap/Join、MiterLimit、DashPattern/DashOffset、颜色及 Relative 三级继承。

着色合集实例见 cmd/ofd-creator/examples/shading.yaml,覆盖 Axial/Radial/Gouraud/LaGouraud 及 Direct/Repeat/Reflect 映射。

三角网格渐变实例见 cmd/ofd-creator/examples/gouraud.yaml,单页六格覆盖单三角形、EdgeFlag=1 三角带、EdgeFlag=2 扇形、EdgeFlag=0 独立三角对,以及 Extend=0 与 Extend=1+Back Color 的未覆盖区域对照。

底纹翻转与图案填充实例见 cmd/ofd-creator/examples/pattern-reflect.yaml,第 1 页覆盖底纹 ReflectMethod 的 Normal/Row/Column/RowAndColumn 平铺,第 2 页为 Pattern 图案填充示例(自 shading.yaml 移入)。

径向渐变综合实例见 examples/radial_demo.yaml,覆盖 Eccentricity/Angle、轴向与径向的 Extend/MapType。

径向渐变 Extend 与 MapType / MapUnit 实例见 cmd/ofd-creator/examples/radial-extend.yaml,5 页 22 格覆盖 MapType=Direct/Repeat/Reflect × Extend 0/1/2/3(MapUnit=12,红→黄→蓝三色段)及 start_point 偏离圆心的偏心径向对照(含 Repeat/Reflect 平铺镜像与起始圆白色圆孔);末两页几何相同、仅 Extend 不同,可在两圆外离与内含两种偏心几何下对照 Extend 的延伸行为。

封面与复合图元缩略图实例见 cmd/ofd-creator/examples/cover-thumbnail.yaml,包含 Cover 封面(base64 内嵌小图写入 Doc_0/Cover/)与复合图元 Thumbnail 缩略图引用。

文档默认颜色空间实例见 cmd/ofd-creator/examples/default-cs.yaml,声明 DefaultCS 指向 RGB 颜色空间,并演示 GRAY 分量色的显式 color_space 引用。

动作与附件实例见 cmd/ofd-creator/examples/actions.yaml,覆盖 URI、书签、音频与影片动作,并包含三条带 NewWindow 新窗口参数的 GotoA 附件动作(覆盖 true/false 两种取值):文本附件、内嵌的最小单页 OFD 测试文件与同窗口文本附件。

线条样式 JSON 实例见 cmd/ofd-creator/examples/line-styles.json,包含线宽、端点、连接、虚线、偏移、折线和透明度效果。

两页路径填充规则实例见 cmd/ofd-creator/examples/path-fill-rules.json,包含默认规则、NonZero、Even-Odd、嵌套轮廓、星形、多孔图形和重叠路径。

现代信息卡片 / 活动海报样式实例见 cmd/ofd-creator/examples/modern-info-card.yaml,包含背景层、内容卡片、进度条、标签卡和页脚信息。

图片图元的 ctm 会直接写入图片变换矩阵;跳转动作还支持 left、top、right、bottom 和 zoom 定位参数。

page_size 提供 width 和 height 时优先使用显式尺寸;只有在两者都未设置时,才支持使用 name: A4。其他名称需要同时提供宽高。

文字图元的 fill 可以省略、设置为 true 或设置为 false。省略时不会强制输出 Fill 属性。

文档级 actions、bookmarks、outlines、attachments、extensions、signatures 和 versions 也可以直接在 document 下声明。签名值和 seal_file_base64 使用 Base64 字符串,附件、签名文件和版本根文件使用资源相对路径。

多版本示例见 examples/versions.yaml。按 GB/T 33190—2016 §19.2,versions[].version 表示适用的 OFD 格式版本(如 1.0);files 应列出该版本入口、页面及资源依赖。 files[].path 输入相对于 OFD 文档目录 Doc_0,creator 写入版本描述时转换为 /Doc_0/... 包内绝对路径;版本的 BaseLoc 和 DocRoot 也使用包内绝对路径。 doc_root_base64 中的版本入口 XML 保留原内容,建议使用包内绝对路径;若使用相对路径, 则从 Doc_0/Versions/Files 解析。字体等资源必须由该入口或页面明确引用。 示例采用版本序号 1、2 和包内绝对路径,此组合的复测文件已在官方及第三方阅读器中确认可显示。

文档元数据还支持 creation_date、mod_date、keywords、custom_data、cover、area、default_cs、permissions 和 preferences;页面支持 area、layer_type、actions、页面专属 resources 以及裁剪区域 clips。

页面的 items 与 layers 互斥;页面资源的 images 与原始 file、data_base64、files 互斥。

动作可以配置 region,区域命令支持 move、line、quadratic、cubic、arc 和 close。

二进制资源通常使用相对文件路径;小型内嵌资源也可以使用对应的 data_base64 字段,两者不能同时设置。该字段可用于字体、图片、媒体、附件、页面图片、公共资源、自定义标签、封面、扩展数据和版本根文件。

颜色空间的 ICC Profile 可使用 profile_file 或 profile_base64,并可通过 profile_name 指定包内文件名。

profile_name 必须是单层文件名,不能包含目录分隔符或路径穿越片段。

输入格式支持 JSON 和 YAML;使用 --format auto 时根据 .json、.yaml、.yml 扩展名自动判断,标准输入或没有扩展名时默认按 YAML 解析。格式不支持时返回参数错误(退出码 2)。输入字段未知、资源路径越出资源根目录或资源文件不存在时,命令会失败并报告 manifest 字段位置。

退出码

退出码 含义
0 创建、导出、合并、替换或校验成功;--version 输出版本号也算成功
2 参数错误:未知选项、缺少必填项、-i/-o 组合非法、--format 取值不支持、--shape 非法、输出会覆盖输入
3 构建失败:manifest 已加载但无法生成 OFD(例如路径图元或资源不合法)
4 资源错误:输入 manifest、输入 OFD 或图片等资源读不到、解析失败
5 输出失败:输出目录不可写、写出 OFD 或报告失败
6 校验失败:--validate 发现生成的文档不合法

2 与其余各档的界线是「命令要不要改」:2 是命令行写错了,其余是文档本身或环境 的问题。3~6 分离出来是为了让 CI 能区分「manifest 有 bug」「文件没找到」 「磁盘/权限问题」「产物不合格」——这四类的处理方式完全不同。

--version(及 -v)对根命令和所有子命令一致生效,输出发行版本号后立即返回。

标准输入和标准输出

cat document.yaml | ofd-creator -i - -o result.ofd
ofd-creator -i document.yaml -o - > result.ofd

错误信息始终写入标准错误,不会污染标准输出中的 OFD 二进制数据。

格式化JSON

go install github.com/zc310/pretty/cmd/pretty@latest

pretty  -max-depth 10 -min-depth 2 --input text-directions.json -output text-directions.json
pretty  -max-depth 10 -min-depth 2 --input path-fill-rules.json -output path-fill-rules.json
pretty  -max-depth 10 -min-depth 2 --input line-styles.json -output line-styles.json

Documentation

Overview

Command ofd-creator 根据 JSON 或 YAML manifest 创建 OFD 文件。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL