Skip to content

OFD 文档附件

本节介绍 OFD 文档附件的枚举、读写、权限控制、电子凭证识别与附件目录管理。附件通过 OFDDoc 上的 Attachments 集合或文档级快捷接口访问;单个附件由 Attachment 表示。取得 OFDDoc 请参阅 OFD 文档与页面

任务场景

  • 通过 GetAttachmentsCreateAttachments 取得附件集合并枚举文档内附件。
  • 使用 AddAttachment 添加附件,设置 AttachmentInfo 与文件内容(路径或 ReaderCallback)。
  • 导出附件字节流(GetFile)或通过 OFDDoc::ExportAttachment 快捷导出。
  • 配置附件可见、可删、可编辑、可导出等阅读器侧权限(EnableVisible 等)。
  • 识别并导出内嵌电子凭证(GetElectronicBillsCountExportElectronicBillsFile)。
  • 使用 AttachmentDirectory 构建层级目录并将已有附件按 ID 挂载到目录节点。
  • 修改后通过 OFDPackage::Save / SaveAs 持久化。

API 概览

功能C++ API(foxit::ofd核心参数 / 描述
附件集合OFDDoc::GetAttachmentsCreateAttachments无集合时须先 CreateAttachments
集合操作AttachmentsGetAttachmentCountAddAttachmentRemoveAttachment
RemoveAllAttachmentsGetAttachmentByID
附件实体AttachmentSetFile / GetFileGetIDGetAttachmentInfo
元数据AttachmentInfonameformatusagecreation_datemodification_date
阅读器权限Attachment::Enable* / Is*VisibleDeletableEditableExportable
文档级快捷OFDDoc::GetAttachmentCountGetAttachment
GetAttachmentByNameExportAttachment
AddAttachment
按索引/名称访问或一步添加文件附件
电子凭证Attachments::ElectronicBillsTypeGetElectronicBillsCountGetElectronicBillsType
ExportElectronicBillsFile
附件目录AttachmentDirectoryCreateSubDirectoryAddAttachment(attach_id)
GetAttachmentRemoveAttachmentByID
目录扩展信息Attachments::Get/SetAttachmentDirectoryExtInfoFileXML 格式的目录扩展描述
持久化OFDPackage::SaveSaveAs附件变更后须包层保存

获取附件并读取元数据

文档若尚无附件集合,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;
}
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");
    }
}

添加附件并设置属性

典型流程: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");
}
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");
    }
}

导出与删除附件

导出可使用 Attachment::GetFile(路径或 WriterCallback),或 OFDDoc::ExportAttachment 按索引导出。删除使用 Attachments::RemoveAttachmentRemoveAllAttachments

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);
}
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);
    }
}

电子凭证

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;
}
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;
    }
}

附件目录

对附件较多的文档,可通过 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);
}
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);
    }
}

保存

附件增删改或目录结构调整后,须调用包层保存写回文件。

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));
}
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);
    }
}

注意事项

  • 集合初始化:文档无附件时 GetAttachments() 为空,须 CreateAttachments() 后再 AddAttachment
  • 大文件读写SetFile / GetFile 支持 ReaderCallback / WriterCallback,处理大附件时建议使用回调,避免整文件载入内存。
  • 权限语义EnableVisibleEnableDeletable 等控制阅读器侧行为;SDK 侧开发者仍可读写附件,保存后权限标记会写入文档。
  • 元数据:添加附件时建议填写 formatusage,便于阅读器分类显示;AttachmentInfo 含创建/修改时间。
  • 电子凭证:导出前先用 GetElectronicBillsType 判断类型;无凭证时 GetElectronicBillsCount 为 0,ExportElectronicBillsFile 将失败。
  • 附件目录:目录节点通过 attach_id 引用已存在于 Attachments 集合中的附件,而非直接嵌入新文件。
  • 文档级快捷接口OFDDoc::AddAttachment(file_path) 可一步添加文件;复杂元数据与权限仍推荐 Attachments + Attachment 流程。
  • 持久化:变更后须 OFDPackage::Save / SaveAs;与 OFD 文件包 保存流程一致。