Skip to content

OFD 书签、大纲与目录

本节介绍 OFD 文档的三类导航与目录能力:书签(Bookmarks 用于阅读器侧栏的平面跳转列表;大纲(Outline 为可嵌套的树形导航,支持页内跳转或 URI;目录页(Catalog 在文档正文中插入排版好的目录页(首页或次页)。取得 OFDDoc 请参阅 OFD 文档与页面

任务场景

  • 通过 GetBookmarks / CreateBookmarks 取得书签集合并枚举、添加或删除书签。
  • 为书签设置名称与跳转目标(BookmarkDestinationInfopage_indexx_posy_pos)。
  • 使用 CreateOutline / GetOutline 构建多级大纲树,配置页内跳转(SetDestination)或外部链接(SetURI)。
  • 递归遍历 Outline 子节点,读取标题、动作与目标信息。
  • 调用 RemoveOutline 清除整棵大纲树,或 RemoveOutline(index) 删除单个子节点。
  • 配置 Catalog 的位置、边距、字体格式与目录条目,通过 OFDDoc::AddCatalog 插入目录页。
  • 在目录页中关联 Attachments 集合,实现附件列表排版;附件准备请参阅 OFD 附件
  • 变更后通过 OFDPackage::Save / SaveAs 持久化。

API 概览

功能C++ API(foxit::ofd核心参数 / 描述
书签集合OFDDoc::GetBookmarksCreateBookmarks无书签时须 CreateBookmarks
书签操作BookmarksGetBookmarkCountAddBookmarkRemoveBookmark
书签实体BookmarkSetName / GetNameSetDest / GetDest
书签目标BookmarkDestinationInfopage_index(0 起)、x_posy_pos(OFD 坐标)
大纲根OFDDoc::CreateOutlineGetOutline
RemoveOutline
创建/读取/删除整棵大纲
大纲节点OutlineAddOutlineGetOutlineRemoveOutlineSetTitleEnableExpanded
大纲跳转Outline::SetDestinationSetURI页内坐标跳转或 URI 外链;GetDestination 返回 ActionDestinationInfo
目录页CatalogSetPositionSetMarginSetFormatAddItemInfoAddAttachment
目录位置Catalog::CatalogPositionTypee_CatalogPositionTypeFirstPage(首页前插入)、
e_CatalogPositionTypeSecondPage(次页前插入)
写入目录页OFDDoc::AddCatalog传入配置好的 Catalog 对象
持久化OFDPackage::SaveSaveAs书签/大纲/目录变更后须包层保存

书签(Bookmarks)

文档级书签为平面列表(无嵌套子书签 API)。通过 Bookmarks::AddBookmark 添加条目,用 BookmarkDestinationInfo 指定目标页索引与坐标。

c++
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdbookmark.h"

using namespace foxit;
using namespace foxit::ofd;

bool AddBookmarks(OFDDoc& doc) {
  if (doc.IsEmpty()) {
    return false;
  }

  Bookmarks bookmarks = doc.GetBookmarks();
  if (bookmarks.IsEmpty()) {
    bookmarks = doc.CreateBookmarks();
  }
  if (bookmarks.IsEmpty()) {
    return false;
  }

  // 添加书签并设置跳转目标
  Bookmark bm = bookmarks.AddBookmark();
  if (bm.IsEmpty()) {
    return false;
  }

  bm.SetName("第一章");
  BookmarkDestinationInfo dest(0, 100.0f, 200.0f);  // 第 0 页,坐标 (100, 200)
  return bm.SetDest(dest);
}
java
import com.foxit.sdk.ofd.Bookmark;
import com.foxit.sdk.ofd.BookmarkDestinationInfo;
import com.foxit.sdk.ofd.Bookmarks;
import com.foxit.sdk.ofd.OFDDoc;

public class OFDBookmarkExample {
    public static boolean addBookmarks(OFDDoc doc) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        Bookmarks bookmarks = doc.getBookmarks();
        if (bookmarks.isEmpty()) {
            bookmarks = doc.createBookmarks();
        }
        if (bookmarks.isEmpty()) {
            return false;
        }

        // 添加书签并设置跳转目标
        Bookmark bm = bookmarks.addBookmark();
        if (bm.isEmpty()) {
            return false;
        }

        bm.setName("第一章");
        BookmarkDestinationInfo dest = new BookmarkDestinationInfo(0, 100.0f, 200.0f);
        return bm.setDest(dest);
    }
}

遍历已有书签并读取属性:

c++
#include "ofd/fs_ofdbookmark.h"

using namespace foxit::ofd;

void TraverseBookmarks(Bookmarks& bookmarks) {
  if (bookmarks.IsEmpty()) {
    return;
  }

  int count = bookmarks.GetBookmarkCount();
  for (int i = 0; i < count; ++i) {
    Bookmark bm = bookmarks.GetBookmark(i);
    if (bm.IsEmpty()) {
      continue;
    }

    String name = bm.GetName();
    BookmarkDestinationInfo dest = bm.GetDest();
    (void)name;
    (void)dest;
  }
}
java
import com.foxit.sdk.ofd.Bookmark;
import com.foxit.sdk.ofd.BookmarkDestinationInfo;
import com.foxit.sdk.ofd.Bookmarks;

public class OFDTraverseBookmarksExample {
    public static void traverseBookmarks(Bookmarks bookmarks) throws Exception {
        if (bookmarks.isEmpty()) {
            return;
        }

        int count = bookmarks.getBookmarkCount();
        for (int i = 0; i < count; i++) {
            Bookmark bm = bookmarks.getBookmark(i);
            if (bm.isEmpty()) {
                continue;
            }

            String name = bm.getName();
            BookmarkDestinationInfo dest = bm.getDest();
        }
    }
}

大纲(Outline)

大纲为树形结构:根节点由 CreateOutline 取得,各级子节点通过 AddOutline 添加。节点可设置页内跳转(SetDestination(page_index, x, y))或 URI(SetURI),并控制默认展开状态(EnableExpanded)。

c++
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdoutline.h"

using namespace foxit::ofd;

bool BuildOutlineTree(OFDDoc& doc) {
  if (doc.IsEmpty()) {
    return false;
  }

  doc.RemoveOutline();  // 可选:清除已有大纲
  Outline root = doc.CreateOutline();
  if (root.IsEmpty()) {
    return false;
  }

  // 一级节点:页内跳转
  Outline chapter = root.AddOutline();
  chapter.SetTitle("第一章");
  chapter.EnableExpanded(true);
  chapter.SetDestination(0, 0.0f, 0.0f);

  // 二级节点
  Outline section = chapter.AddOutline();
  section.SetTitle("1.1 概述");
  section.SetDestination(0, 0.0f, 100.0f);

  // 一级节点:URI 外链
  Outline link = root.AddOutline();
  link.SetTitle("官方网站");
  link.SetURI("https://www.foxitsoftware.com");

  return root.GetOutlineCount() > 0;
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.Outline;

public class OFDOutlineExample {
    public static boolean buildOutlineTree(OFDDoc doc) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        doc.removeOutline();
        Outline root = doc.createOutline();
        if (root.isEmpty()) {
            return false;
        }

        // 一级节点:页内跳转
        Outline chapter = root.addOutline();
        chapter.setTitle("第一章");
        chapter.enableExpanded(true);
        chapter.setDestination(0, 0.0f, 0.0f);

        // 二级节点
        Outline section = chapter.addOutline();
        section.setTitle("1.1 概述");
        section.setDestination(0, 0.0f, 100.0f);

        // 一级节点:URI 外链
        Outline link = root.addOutline();
        link.setTitle("官方网站");
        link.setURI("https://www.foxitsoftware.com");

        return root.getOutlineCount() > 0;
    }
}

读取已保存文档的大纲树时,自根节点递归调用 GetOutline(index)。页内跳转节点可通过 GetDestination() 取得 ActionDestinationInfo(含 page_idlefttop);URI 节点使用 GetURI()

目录页(Catalog)

Catalog 用于在文档中插入物理目录页(非侧栏导航)。配置边距、各级字体格式与目录条目后,调用 OFDDoc::AddCatalog 写入文档;可选 AddAttachment 将附件集合排版进目录。

c++
#include "ofd/fs_ofddoc.h"
#include "ofd/fs_ofdcatalog.h"
#include "ofd/fs_ofdattachment.h"

using namespace foxit;
using namespace foxit::ofd;

bool AddCatalogPage(OFDDoc& doc) {
  if (doc.IsEmpty() || doc.GetPageCount() == 0) {
    return false;
  }

  Catalog catalog;
  catalog.SetPosition(Catalog::e_CatalogPositionTypeFirstPage);
  catalog.SetMargin(10.0f, 15.0f);  // 横向/纵向边距(毫米)

  // section:章节层级(从 0 起);font_size、lower_space 单位为毫米
  catalog.SetFormat(0, "SimSun", 4.0f, 2.0f);
  catalog.SetFormat(1, "SimHei", 3.5f, 1.5f);

  // page_count 为目标页面索引(0 至 GetPageCount()-1)
  if (!catalog.AddItemInfo("前言", 0)) {
    return false;
  }
  if (!catalog.AddItemInfo("正文", doc.GetPageCount() - 1)) {
    return false;
  }

  // 可选:关联附件集合(须非空,参见 attachment.md)
  Attachments attachments = doc.GetAttachments();
  if (!attachments.IsEmpty() && attachments.GetAttachmentCount() > 0) {
    catalog.AddAttachment(attachments);
  }

  return doc.AddCatalog(catalog);
}
java
import com.foxit.sdk.ofd.Attachments;
import com.foxit.sdk.ofd.Catalog;
import com.foxit.sdk.ofd.OFDDoc;

public class OFDCatalogExample {
    public static boolean addCatalogPage(OFDDoc doc) throws Exception {
        if (doc.isEmpty() || doc.getPageCount() == 0) {
            return false;
        }

        Catalog catalog = new Catalog();
        catalog.setPosition(Catalog.e_CatalogPositionTypeFirstPage);
        catalog.setMargin(10.0f, 15.0f);

        catalog.setFormat(0, "SimSun", 4.0f, 2.0f);
        catalog.setFormat(1, "SimHei", 3.5f, 1.5f);

        if (!catalog.addItemInfo("前言", 0)) {
            return false;
        }
        if (!catalog.addItemInfo("正文", doc.getPageCount() - 1)) {
            return false;
        }

        Attachments attachments = doc.getAttachments();
        if (!attachments.isEmpty() && attachments.getAttachmentCount() > 0) {
            catalog.addAttachment(attachments);
        }

        return doc.addCatalog(catalog);
    }
}

带精确坐标的目录条目可使用 AddItemInfo(title, page_count, x, y) 重载(xy 单位为毫米)。

保存

书签、大纲或目录页变更后须包层保存。

c++
#include "ofd/fs_ofdpackage.h"

using namespace foxit::ofd;

bool SaveNavigationChanges(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 OFDSaveNavigationExample {
    public static boolean saveNavigationChanges(OFDPackage pkg,
            String outputPath) throws Exception {
        if (pkg.isEmpty()) {
            return false;
        }

        return pkg.saveAs(outputPath);
    }
}

注意事项

  • 三者区别Bookmarks 为侧栏平面书签,不占物理页面;Outline 为树形大纲(可嵌套),供阅读器导航面板;Catalog 会在首页或次页前插入目录页,改变页序与页数。
  • 书签 vs 大纲:书签 API 仅支持文档级平面列表;需要多级嵌套导航请使用 Outline
  • 页面索引BookmarkDestinationInfo::page_indexCatalog::AddItemInfopage_count 均为 0 起页面索引,须满足 0 ≤ index < GetPageCount();超出范围时 AddCatalog 可能失败。
  • 坐标:书签 x_pos / y_pos 使用 OFD 页面坐标;仅跳转到页顶时可设为 (0.0f, 0.0f)。大纲 SetDestination 参数含义与书签类似,读取时 GetDestination 返回 page_idleft / top
  • 大纲动作:URI 节点调用 SetURI 后不应再依赖 GetDestination;页内跳转与 URI 互斥,读取时需区分(参见发布包 ofd_outline 示例中的异常处理)。
  • 目录格式SetFormatfont_name 须非空,font_sizelower_space 须 ≥ 0;section 表示目录层级(从 0 起)。
  • 目录附件Catalog::AddAttachment 要求传入非空且含至少一个附件的 Attachments 对象。
  • 删除大纲OFDDoc::RemoveOutline() 删除整棵大纲;单节点删除使用 Outline::RemoveOutline(index)
  • 持久化:结构变更后须 OFDPackage::Save / SaveAs;插入目录页后页码可能变化,请重新核对跳转目标与目录条目索引。