OFD 文档附件
本节介绍 OFD 文档附件的枚举、读写、权限控制、电子凭证识别与附件目录管理。附件通过 OFDDoc 上的 Attachments 集合或文档级快捷接口访问;单个附件由 Attachment 表示。取得 OFDDoc 请参阅 OFD 文档与页面。
任务场景
- 通过
GetAttachments或CreateAttachments取得附件集合并枚举文档内附件。 - 使用
AddAttachment添加附件,设置AttachmentInfo与文件内容(路径或ReaderCallback)。 - 导出附件字节流(
GetFile)或通过OFDDoc::ExportAttachment快捷导出。 - 配置附件可见、可删、可编辑、可导出等阅读器侧权限(
EnableVisible等)。 - 识别并导出内嵌电子凭证(
GetElectronicBillsCount、ExportElectronicBillsFile)。 - 使用
AttachmentDirectory构建层级目录并将已有附件按 ID 挂载到目录节点。 - 修改后通过
OFDPackage::Save/SaveAs持久化。
API 概览
| 功能 | C++ API(foxit::ofd) | 核心参数 / 描述 |
|---|---|---|
| 附件集合 | OFDDoc::GetAttachments、CreateAttachments | 无集合时须先 CreateAttachments |
| 集合操作 | Attachments | GetAttachmentCount、AddAttachment、RemoveAttachment、RemoveAllAttachments、GetAttachmentByID |
| 附件实体 | Attachment | SetFile / GetFile;GetID、GetAttachmentInfo |
| 元数据 | AttachmentInfo | name、format、usage、creation_date、modification_date |
| 阅读器权限 | Attachment::Enable* / Is* | Visible、Deletable、Editable、Exportable |
| 文档级快捷 | OFDDoc::GetAttachmentCount、GetAttachment、GetAttachmentByName、ExportAttachment、AddAttachment | 按索引/名称访问或一步添加文件附件 |
| 电子凭证 | Attachments::ElectronicBillsType | GetElectronicBillsCount、GetElectronicBillsType、ExportElectronicBillsFile |
| 附件目录 | AttachmentDirectory | CreateSubDirectory、AddAttachment(attach_id)、GetAttachment、RemoveAttachmentByID |
| 目录扩展信息 | Attachments::Get/SetAttachmentDirectoryExtInfoFile | XML 格式的目录扩展描述 |
| 持久化 | OFDPackage::Save、SaveAs | 附件变更后须包层保存 |
获取附件并读取元数据
文档若尚无附件集合,GetAttachments() 可能为空,需调用 CreateAttachments()。遍历时可读取 AttachmentInfo 及权限状态。
c++
#include "ofd/fs_ofdpackage.h"
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdattachment.h"
using namespace foxit;
using namespace foxit::ofd;
void ListAttachments(OFDDoc& doc) {
if (doc.IsEmpty()) {
return;
}
Attachments attachments = doc.GetAttachments();
if (attachments.IsEmpty()) {
attachments = doc.CreateAttachments();
}
if (attachments.IsEmpty()) {
return;
}
int count = attachments.GetAttachmentCount();
for (int i = 0; i < count; ++i) {
Attachment att = attachments.GetAttachment(i);
if (att.IsEmpty()) {
continue;
}
// 读取元数据与权限
AttachmentInfo info = att.GetAttachmentInfo();
uint32 id = att.GetID();
bool visible = att.IsVisible();
bool exportable = att.IsExportable();
(void)info;
(void)id;
(void)visible;
(void)exportable;
}
// 文档级快捷:按名称查找
Attachment by_name = doc.GetAttachmentByName("output.ofd");
(void)by_name;
}
1
java
import com.foxit.sdk.ofd.Attachment;
import com.foxit.sdk.ofd.AttachmentInfo;
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.OFDDoc;
public class OFDListAttachmentsExample {
public static void listAttachments(OFDDoc doc) throws Exception {
if (doc.isEmpty()) {
return;
}
Attachments attachments = doc.getAttachments();
if (attachments.isEmpty()) {
attachments = doc.createAttachments();
}
if (attachments.isEmpty()) {
return;
}
int count = attachments.getAttachmentCount();
for (int i = 0; i < count; i++) {
Attachment att = attachments.getAttachment(i);
if (att.isEmpty()) {
continue;
}
// 读取元数据与权限
AttachmentInfo info = att.getAttachmentInfo();
int id = att.getID();
boolean visible = att.isVisible();
boolean exportable = att.isExportable();
}
// 文档级快捷:按名称查找
Attachment byName = doc.getAttachmentByName("output.ofd");
}
}
1
添加附件并设置属性
典型流程:AddAttachment() 取得空附件 → 设置 AttachmentInfo 或各字段 → SetFile 写入内容 → 可选配置权限。
c++
#include "ofd/fs_ofdattachment.h"
using namespace foxit;
using namespace foxit::ofd;
bool AddAttachmentFromFile(OFDDoc& doc, const char* file_path) {
if (doc.IsEmpty()) {
return false;
}
Attachments attachments = doc.GetAttachments();
if (attachments.IsEmpty()) {
attachments = doc.CreateAttachments();
}
if (attachments.IsEmpty()) {
return false;
}
Attachment att = attachments.AddAttachment();
if (att.IsEmpty()) {
return false;
}
DateTime creation_date, mod_date;
AttachmentInfo info("sample.pdf", "pdf", "attachment", creation_date, mod_date);
att.SetAttachmentInfo(info);
att.SetModifier("Foxit SDK");
att.SetDescription("Sample attachment");
// 阅读器侧权限
att.EnableVisible(true);
att.EnableDeletable(true);
att.EnableEditable(true);
att.EnableExportable(true);
// 从本地文件写入附件内容
return att.SetFile(file_path, "sample.pdf");
}
1
java
import com.foxit.sdk.common.DateTime;
import com.foxit.sdk.ofd.Attachment;
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.OFDDoc;
public class OFDAddAttachmentExample {
public static boolean addAttachmentFromFile(OFDDoc doc, String filePath)
throws Exception {
if (doc.isEmpty()) {
return false;
}
Attachments attachments = doc.getAttachments();
if (attachments.isEmpty()) {
attachments = doc.createAttachments();
}
if (attachments.isEmpty()) {
return false;
}
Attachment att = attachments.addAttachment();
if (att.isEmpty()) {
return false;
}
DateTime creationDate = new DateTime(2026, 4, 17, 10, 0, 0, 0, (short) 0, 0);
DateTime modDate = new DateTime(creationDate);
att.setName("sample.pdf");
att.setFormat("pdf");
att.setUsage("attachment");
att.setCreationDate(creationDate);
att.setModDate(modDate);
att.setModifier("Foxit SDK");
att.setDescription("Sample attachment");
// 阅读器侧权限
att.enableVisible(true);
att.enableDeletable(true);
att.enableEditable(true);
att.enableExportable(true);
// 从本地文件写入附件内容
return att.setFile(filePath, "sample.pdf");
}
}
1
导出与删除附件
导出可使用 Attachment::GetFile(路径或 WriterCallback),或 OFDDoc::ExportAttachment 按索引导出。删除使用 Attachments::RemoveAttachment 或 RemoveAllAttachments。
c++
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdattachment.h"
using namespace foxit;
using namespace foxit::ofd;
bool ExportAndRemoveFirstAttachment(OFDDoc& doc, const wchar_t* export_path) {
if (doc.IsEmpty()) {
return false;
}
Attachments attachments = doc.GetAttachments();
if (attachments.IsEmpty() || attachments.GetAttachmentCount() == 0) {
return false;
}
// 方式一:Attachment 对象导出
Attachment att = attachments.GetAttachment(0);
if (!att.GetFile(WString(export_path))) {
return false;
}
// 方式二:文档级按索引导出(等价快捷接口)
// doc.ExportAttachment(0, export_path);
return attachments.RemoveAttachment(0);
}
1
java
import com.foxit.sdk.ofd.Attachment;
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.OFDDoc;
public class OFDExportAttachmentExample {
public static boolean exportAndRemoveFirstAttachment(OFDDoc doc,
String exportPath) throws Exception {
if (doc.isEmpty()) {
return false;
}
Attachments attachments = doc.getAttachments();
if (attachments.isEmpty() || attachments.getAttachmentCount() == 0) {
return false;
}
// 方式一:Attachment 对象导出
Attachment att = attachments.getAttachment(0);
if (!att.getFile(exportPath)) {
return false;
}
// 方式二:文档级按索引导出
// doc.exportAttachment(0, exportPath);
return attachments.removeAttachment(0);
}
}
1
电子凭证
OFD 可将增值税发票、银行回单等电子凭证作为特殊附件嵌入。通过 Attachments::GetElectronicBillsCount 获取数量,GetElectronicBillsType 识别类型,ExportElectronicBillsFile 导出对应文件。
常见 ElectronicBillsType 包括:e_ElectronicInvoiceOrdIssuer(增值税电子普通发票-开票方)、e_ElectronicBkerIssuer(银行电子回单-开具方)、e_ElectronicEinvOrd(数电票普通发票)等;完整枚举见接口文档。
c++
#include "ofd/fs_ofdattachment.h"
using namespace foxit;
using namespace foxit::ofd;
bool ExportElectronicBills(OFDDoc& doc, const char* output_dir) {
if (doc.IsEmpty()) {
return false;
}
Attachments attachments = doc.GetAttachments();
if (attachments.IsEmpty()) {
return false;
}
int bills_count = attachments.GetElectronicBillsCount();
for (int i = 0; i < bills_count; ++i) {
// 先识别类型再处理
Attachments::ElectronicBillsType type = attachments.GetElectronicBillsType(i);
(void)type;
String out_path;
out_path.Format("%sbill_%d.ofd", output_dir, i);
if (!attachments.ExportElectronicBillsFile(i, out_path)) {
return false;
}
}
return true;
}
1
java
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.OFDDoc;
public class OFDElectronicBillsExample {
public static boolean exportElectronicBills(OFDDoc doc, String outputDir)
throws Exception {
if (doc.isEmpty()) {
return false;
}
Attachments attachments = doc.getAttachments();
if (attachments.isEmpty()) {
return false;
}
int billsCount = attachments.getElectronicBillsCount();
for (int i = 0; i < billsCount; i++) {
// 先识别类型再处理
int type = attachments.getElectronicBillsType(i);
String outPath = outputDir + "bill_" + i + ".ofd";
if (!attachments.exportElectronicBillsFile(i, outPath)) {
return false;
}
}
return true;
}
}
1
附件目录
对附件较多的文档,可通过 CreateAttachmentDirectory 创建根目录,用 CreateSubDirectory 建立子目录,再将已添加附件的 GetID() 通过 AddAttachment(attach_id) 挂入目录。目录中的附件须先在 Attachments 集合中创建。
c++
#include "ofd/fs_ofdattachment.h"
#include "ofd/fs_ofdattachmentdirectory.h"
using namespace foxit;
using namespace foxit::ofd;
bool BuildAttachmentDirectory(OFDDoc& doc, const char* file_path) {
if (doc.IsEmpty()) {
return false;
}
Attachments attachments = doc.GetAttachments();
if (attachments.IsEmpty()) {
attachments = doc.CreateAttachments();
}
Attachment att = attachments.AddAttachment();
if (!att.SetFile(file_path)) {
return false;
}
uint32 attach_id = att.GetID();
// 创建目录树并挂载附件
AttachmentDirectory root = attachments.CreateAttachmentDirectory();
root.SetName("root");
AttachmentDirectory sub = root.CreateSubDirectory("凭证");
return sub.AddAttachment(attach_id);
}
1
java
import com.foxit.sdk.ofd.Attachment;
import com.foxit.sdk.ofd.AttachmentDirectory;
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.OFDDoc;
public class OFDAttachmentDirectoryExample {
public static boolean buildAttachmentDirectory(OFDDoc doc, String filePath)
throws Exception {
if (doc.isEmpty()) {
return false;
}
Attachments attachments = doc.getAttachments();
if (attachments.isEmpty()) {
attachments = doc.createAttachments();
}
Attachment att = attachments.addAttachment();
if (!att.setFile(filePath)) {
return false;
}
int attachId = att.getID();
// 创建目录树并挂载附件
AttachmentDirectory root = attachments.createAttachmentDirectory();
root.setName("root");
AttachmentDirectory sub = root.createSubDirectory("凭证");
return sub.addAttachment(attachId);
}
}
1
保存
附件增删改或目录结构调整后,须调用包层保存写回文件。
c++
#include "ofd/fs_ofdpackage.h"
using namespace foxit::ofd;
bool SaveDocumentWithAttachments(OFDPackage& package, const wchar_t* output_path) {
if (package.IsEmpty()) {
return false;
}
return package.SaveAs(WString(output_path));
}
1
java
import com.foxit.sdk.ofd.OFDPackage;
public class OFDSaveAttachmentExample {
public static boolean saveDocumentWithAttachments(OFDPackage pkg,
String outputPath) throws Exception {
if (pkg.isEmpty()) {
return false;
}
return pkg.saveAs(outputPath);
}
}
1
注意事项
- 集合初始化:文档无附件时
GetAttachments()为空,须CreateAttachments()后再AddAttachment。 - 大文件读写:
SetFile/GetFile支持ReaderCallback/WriterCallback,处理大附件时建议使用回调,避免整文件载入内存。 - 权限语义:
EnableVisible、EnableDeletable等控制阅读器侧行为;SDK 侧开发者仍可读写附件,保存后权限标记会写入文档。 - 元数据:添加附件时建议填写
format、usage,便于阅读器分类显示;AttachmentInfo含创建/修改时间。 - 电子凭证:导出前先用
GetElectronicBillsType判断类型;无凭证时GetElectronicBillsCount为 0,ExportElectronicBillsFile将失败。 - 附件目录:目录节点通过
attach_id引用已存在于Attachments集合中的附件,而非直接嵌入新文件。 - 文档级快捷接口:
OFDDoc::AddAttachment(file_path)可一步添加文件;复杂元数据与权限仍推荐Attachments+Attachment流程。 - 持久化:变更后须
OFDPackage::Save/SaveAs;与 OFD 文件包 保存流程一致。