OFD 自定义标引与公文节点
本节介绍自定义标引(CustomTags / CustomTag)、标引树节点(TagTreeNode)以及公文语义节点(OfficeNode)的访问与编辑。这些能力用于公文、档案、电子证照等场景下的语义标引与结构树,不属于 图元与图层 章节(图元遍历请参阅该章)。
任务场景
- 通过
CustomTag维护标引类型,并将外部文件与标引条目绑定(SetFile/ExtractFile)。 - 使用
OFDDoc::InitializeTagTreeNode等接口构建或读取票据/证照类标引树(TagTreeNode),并关联页面图元。 - 从 XML 导入或流式构建公文语义结构(
CreateOfficeNodeByXmlBuffer、BeginOfficeNode/EndOfficeNode)。 - 手动维护
OfficeNode子节点树,将页面图元挂载到公文节点,并设置权限与 Schema 属性。
API 概览
| 功能 | C++ API(foxit::ofd) | 核心参数 / 描述 |
|---|---|---|
| 文档入口 | OFDDoc::CreateCustomTags、GetCustomTags | 创建或获取文档级 CustomTags 集合 |
| 自定义标引条目 | CustomTag | GetType / SetType;SetFile / ExtractFile 绑定或导出外部附件 |
| 标引集合与公文入口 | CustomTags | GetCustomTagCount、AddCustomTag、GetCustomTag;CreateOfficeNode、GetOfficeNode;XML 导入与流式构建 |
| 标引树(文档级写入) | OFDDoc::InitializeTagTreeNode、BeginTagTreeNode、AddTagTreeNodeMetaData、EndTagTreeNode | 按 type 初始化标引树;InitializeCertTagTreeNode 为电子证照快捷入口 |
| 标引树(读取) | OFDDoc::GetTagTreeRoot | 按类型字符串(如 "Cert"、"od")获取根 TagTreeNode |
| 标引树节点 | TagTreeNode | GetKey、GetText;递归 GetTagTreeNode;GetGraphicsObject 关联 graphics::OFDGraphicsObject |
| 公文语义节点 | OfficeNode | AddOfficeNode、GetOfficeNode;AddGraphicsObject(page, rect) 或 AddGraphicsObject(page_id, obj_id);SetPermission、SetOfficeSchema |
| 公文 XML 导出 | OFDDoc::ExportOfficeDoc | 将公文语义导出为 XML 文件 |
自定义标引与外部文件
通过 OFDDoc::CreateCustomTags 创建标引集合,使用 AddCustomTag 添加条目(索引 -1 表示追加到末尾)。SetType 设置标引类型,SetFile / ExtractFile 用于绑定或导出关联的外部文件。
c++
#include "ofd/fs_ofdpackage.h"
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdcustomtag.h"
using namespace foxit;
using namespace foxit::ofd;
bool ConfigureCustomTag(OFDDoc& doc, const char* type, const char* payload_path) {
if (doc.IsEmpty()) {
return false;
}
CustomTags custom_tags = doc.CreateCustomTags();
if (custom_tags.IsEmpty()) {
return false;
}
// index = -1 表示在列表末尾追加
CustomTag custom_tag = custom_tags.AddCustomTag(-1);
if (custom_tag.IsEmpty()) {
return false;
}
if (!custom_tag.SetType(type)) {
return false;
}
return custom_tag.SetFile(payload_path);
}
1
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.CustomTag;
import com.foxit.sdk.ofd.CustomTags;
public class OFDCustomTagExample {
public static boolean configureCustomTag(OFDDoc doc, String type, String payloadPath)
throws Exception {
if (doc.isEmpty()) {
return false;
}
CustomTags customTags = doc.createCustomTags();
if (customTags.isEmpty()) {
return false;
}
CustomTag customTag = customTags.addCustomTag(-1);
if (customTag.isEmpty()) {
return false;
}
if (!customTag.setType(type)) {
return false;
}
return customTag.setFile(payloadPath);
}
}
1
从 XML 构建公文结构
公文语义节点挂在 CustomTags 下。常见方式包括:
- 一次性导入:
CreateOfficeNodeByXmlBuffer或CreateOfficeNodeByXmlFile,传入符合规范的公文 XML。 - 流式构建:
CreateOfficeNode创建根节点后,成对调用BeginOfficeNode/EndOfficeNode模拟 XML 层级(标签名需正确嵌套)。 - 手动建树:
GetOfficeNode取得根节点后,通过OfficeNode::AddOfficeNode添加子节点。
c++
#include "ofd/fs_ofdcustomtag.h"
using namespace foxit;
using namespace foxit::ofd;
bool ImportOfficeNodeFromXml(CustomTags& custom_tags, const char* xml_path) {
if (custom_tags.IsEmpty()) {
return false;
}
// 须先存在至少一条 CustomTag(与 SDK 单测用法一致)
if (custom_tags.GetCustomTagCount() <= 0) {
custom_tags.AddCustomTag(-1);
}
if (!custom_tags.CreateOfficeNodeByXmlFile(xml_path)) {
return false;
}
OfficeNode root = custom_tags.GetOfficeNode();
return !root.IsEmpty();
}
void BuildOfficeNodeByStream(CustomTags& custom_tags) {
if (custom_tags.IsEmpty()) {
return;
}
if (custom_tags.GetCustomTagCount() <= 0) {
custom_tags.AddCustomTag(-1);
}
custom_tags.CreateOfficeNode();
// Begin/End 成对调用,形成嵌套结构(类似 XML 标签栈)
custom_tags.BeginOfficeNode("root");
custom_tags.BeginOfficeNode("section");
custom_tags.EndOfficeNode("section");
custom_tags.EndOfficeNode("root");
OfficeNode root = custom_tags.GetOfficeNode();
}
1
java
import com.foxit.sdk.ofd.CustomTags;
import com.foxit.sdk.ofd.OfficeNode;
public class OFDOfficeNodeXmlExample {
public static boolean importOfficeNodeFromXml(CustomTags customTags, String xmlPath)
throws Exception {
if (customTags.isEmpty()) {
return false;
}
if (customTags.getCustomTagCount() <= 0) {
customTags.addCustomTag(-1);
}
if (!customTags.createOfficeNodeByXmlFile(xmlPath)) {
return false;
}
OfficeNode root = customTags.getOfficeNode();
return !root.isEmpty();
}
public static void buildOfficeNodeByStream(CustomTags customTags) throws Exception {
if (customTags.isEmpty()) {
return;
}
if (customTags.getCustomTagCount() <= 0) {
customTags.addCustomTag(-1);
}
customTags.createOfficeNode();
customTags.beginOfficeNode("root");
customTags.beginOfficeNode("section");
customTags.endOfficeNode("section");
customTags.endOfficeNode("root");
}
}
1
XML 结构示例:
xml
<?xml version="1.0" encoding="UTF-8"?>
<officeRoot xmlns:od="http://www.officedocument.org">
<section>CustomTagUT</section>
</officeRoot>
1
构建与遍历标引树
标引树的 写入 在 OFDDoc 上完成:先 InitializeTagTreeNode(或 InitializeCertTagTreeNode),再通过 BeginTagTreeNode / AddTagTreeNodeMetaData / EndTagTreeNode 添加节点。读取时使用 GetTagTreeRoot(type) 取得根 TagTreeNode,再递归访问子节点与关联图元。
c++
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdtagtreenode.h"
#include "ofd/graphics/fs_ofdgraphicsobject.h"
using namespace foxit;
using namespace foxit::ofd;
using namespace foxit::ofd::graphics;
void BuildCustomTagTree(OFDDoc& doc) {
if (doc.IsEmpty()) {
return;
}
const char* tree_type = "UTCustomTree";
doc.ClearTagTreeNode();
doc.InitializeTagTreeNode(tree_type, "urn:example:tagtree", "示例标引树",
"TagTree", true);
doc.BeginTagTreeNode("invoice");
doc.AddTagTreeNodeMetaData("DocNo", "INV-001");
doc.AddTagTreeNodeMetaData("Issuer", "Foxit");
doc.EndTagTreeNode("invoice");
}
void TraverseTagTreeNode(TagTreeNode& node) {
if (node.IsEmpty()) {
return;
}
String key = node.GetKey();
String text = node.GetText();
// 读取与本节点关联的页面图元
int graphics_count = node.GetGraphicsObjectCount();
for (int i = 0; i < graphics_count; ++i) {
OFDGraphicsObject obj = node.GetGraphicsObject(i);
if (!obj.IsEmpty()) {
ObjectType type = obj.GetType();
}
}
// 递归子节点
int child_count = node.GetTagTreeNodeCount();
for (int i = 0; i < child_count; ++i) {
TagTreeNode child = node.GetTagTreeNode(i);
TraverseTagTreeNode(child);
}
}
void ReadTagTreeRoot(OFDDoc& doc, const char* tree_type) {
TagTreeNode root = doc.GetTagTreeRoot(tree_type);
if (!root.IsEmpty()) {
TraverseTagTreeNode(root);
}
}
1
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.TagTreeNode;
import com.foxit.sdk.ofd.graphics.OFDGraphicsObject;
public class OFDTagTreeExample {
public static void buildCustomTagTree(OFDDoc doc) throws Exception {
if (doc.isEmpty()) {
return;
}
String treeType = "UTCustomTree";
doc.clearTagTreeNode();
doc.initializeTagTreeNode(treeType, "urn:example:tagtree", "示例标引树",
"TagTree", true);
doc.beginTagTreeNode("invoice");
doc.addTagTreeNodeMetaData("DocNo", "INV-001");
doc.addTagTreeNodeMetaData("Issuer", "Foxit");
doc.endTagTreeNode("invoice");
}
public static void traverseTagTreeNode(TagTreeNode node) throws Exception {
if (node.isEmpty()) {
return;
}
String key = node.getKey();
String text = node.getText();
int graphicsCount = node.getGraphicsObjectCount();
for (int i = 0; i < graphicsCount; i++) {
OFDGraphicsObject obj = node.getGraphicsObject(i);
if (!obj.isEmpty()) {
int type = obj.getType();
}
}
int childCount = node.getTagTreeNodeCount();
for (int i = 0; i < childCount; i++) {
TagTreeNode child = node.getTagTreeNode(i);
traverseTagTreeNode(child);
}
}
public static void readTagTreeRoot(OFDDoc doc, String treeType) throws Exception {
TagTreeNode root = doc.getTagTreeRoot(treeType);
if (!root.isEmpty()) {
traverseTagTreeNode(root);
}
}
}
1
操作公文节点
取得 CustomTags::GetOfficeNode 后,可手动添加组节点或元素节点,并将页面图元绑定到节点:
AddGraphicsObject(OFDPage, RectF):按页面与矩形区域关联图元(由 SDK 在区域内查找匹配)。AddGraphicsObject(page_id, obj_id):按页面 ID 与图元 ID 精确绑定。
c++
#include "ofd/fs_ofdcustomtag.h"
#include "ofd/fs_ofdofficenode.h"
#include "ofd/fs_ofdpage.h"
using namespace foxit;
using namespace foxit::ofd;
void ConfigureOfficeNode(CustomTags& custom_tags, OFDPage& page) {
if (custom_tags.IsEmpty() || page.IsEmpty()) {
return;
}
if (custom_tags.GetCustomTagCount() <= 0) {
custom_tags.AddCustomTag(-1);
}
OfficeNode root = custom_tags.CreateOfficeNode();
if (root.IsEmpty()) {
return;
}
root.SetOfficeSchema("xmlns:od", "http://www.officedocument.org", "officeRoot");
root.SetPermission(true, true, true, "replace-text", true);
// group = true 表示组节点,可包含子节点
OfficeNode section = root.AddOfficeNode("section", true);
if (!section.IsEmpty()) {
section.SetAttributeValue("Version", "1.0");
// 方式一:按页面 ID + 图元 ID 精确绑定
section.AddGraphicsObject(static_cast<uint32>(page.GetID()), 0);
// 方式二:按页面与矩形区域绑定
section.AddGraphicsObject(page, RectF(0.0f, 0.0f, 500.0f, 500.0f));
}
}
1
java
import com.foxit.sdk.common.fxcrt.RectF;
import com.foxit.sdk.ofd.CustomTags;
import com.foxit.sdk.ofd.OFDPage;
import com.foxit.sdk.ofd.OfficeNode;
public class OFDOfficeNodeExample {
public static void configureOfficeNode(CustomTags customTags, OFDPage page)
throws Exception {
if (customTags.isEmpty() || page.isEmpty()) {
return;
}
if (customTags.getCustomTagCount() <= 0) {
customTags.addCustomTag(-1);
}
OfficeNode root = customTags.createOfficeNode();
if (root.isEmpty()) {
return;
}
root.setOfficeSchema("xmlns:od", "http://www.officedocument.org", "officeRoot");
root.setPermission(true, true, true, "replace-text", true);
OfficeNode section = root.addOfficeNode("section", true);
if (!section.isEmpty()) {
section.setAttributeValue("Version", "1.0");
section.addGraphicsObject(page.getID(), 0);
section.addGraphicsObject(page, new RectF(0.0f, 0.0f, 500.0f, 500.0f));
}
}
}
1
注意事项
- 数据模型分离:
OfficeNode/TagTreeNode描述语义结构,与页面内容流中的图元遍历是不同模型,请勿在 图元与图层 章节混用。 - CustomTag 前置条件:操作
OfficeNode(创建、XML 导入、流式构建)前,建议先通过AddCustomTag添加至少一条自定义标引,与 SDK 用法保持一致。 - 对象有效性:调用任何方法前使用
IsEmpty()检查CustomTag、CustomTags、TagTreeNode、OfficeNode是否有效。 - 标引树类型参数:
GetTagTreeRoot、InitializeTagTreeNode的type须与初始化时一致(如"Cert"、"od"或自定义类型字符串)。 - XML 格式:
CreateOfficeNodeByXmlBuffer/CreateOfficeNodeByXmlFile要求 XML 符合 OFD 公文语义规范;格式错误将返回失败(单测中对畸形 XML 验证为false)。 - Begin/End 配对:
BeginOfficeNode与EndOfficeNode、BeginTagTreeNode与EndTagTreeNode须按层级成对调用,标签名与嵌套顺序需与目标结构一致。 - 持久化:标引与公文节点修改均为内存操作,须通过
OFDPackage::Save或SaveAs写回文件,请参阅 OFD 文件包。