PDFium Delphi Component Docs

事务式导航合并

在 uses 子句中添加 FPdfMerge,即可使用 TPdfNavigationMergeHelper.MergeFilesWithNavigation;也可明确传入目标文档,调用全局 MergePdfFilesWithNavigation 函数

辅助函数先暂存原生页面导入,重建分层书签,合并命名目标,重映射页面链接,并独立重新打开结果,再替换调用方文档

pmnPreserve 保留源层次;pmnGroupBySource 为每个源文件添加以文件名命名的标题

保留标题、父子关系、样式、颜色、展开分支、URI 动作和已解析的内部页面目标;所选、重复和重排页面映射到首次导入的位置,指向被省略页面的目标则变为标题

插入页面时,目标文档原有书签的位置随之移动;捕获导航和原生页面导入期间,源文件保持打开,并禁止共享写入

仅支持出错即停止的合并;已签名目标、不支持的书签动作和加密后处理会被拒绝并返回错误报告,失败或取消保留调用方文档

该 API 保留现有原生表单策略;需要高级 AcroForm 字段冲突和资源处理时,请使用 FPdfFormMerge

命名目标与页面链接

PreserveNamedDestinations 和 PreservePageLinks 均默认为 True;将某项设为 False 会跳过相应的附加导航处理

命名目标同时从旧 catalog /Dests 字典和 /Names/Dests 名称树读取,支持分层树、间接数组和含 /D 的目标字典;两种形式定义同一键时,名称树的值优先

结果使用平衡的 /Names/Dests 树,并保留目标文档中与目标无关的名称树,例如 /EmbeddedFiles;辅助函数不导入源文档中与目标无关的树

键保留原始 PDF 字符串字节,包括 UTF-16BE 编码;视图模式和参数精确保留,包括 /FitH null 和 /XYZ null 34 null

策略行为
pndReject源键若已存在于目标文档,整个事务都会被拒绝
pndRename默认策略保留无冲突名称,为冲突名称添加 DestinationPrefix 前缀,默认值为 Imported_;继续冲突时使用确定性前缀,例如 2_Imported_Shared
pndNamespace每个源键都添加 DestinationPrefix、从一开始的源索引和下划线前缀,例如 Imported_1_Shared;若仍有冲突,再添加确定性数字前缀

DestinationPrefix 须包含 1 到 256 个可打印 ASCII 字符;键采用 UTF-16BE 时,前缀插入 BOM 之后

使用 /Dest 或单个 /A /GoTo 动作的内部链接注解会重写为显式目标,因此命名链接目标在冲突重命名后仍然正确

插入页面时,现有链接的所属页和目标页随之移动;重复导入的每个页面实例都获得独立链接注解,其内部目标映射到目标页首次被选中的位置

目标页被省略的命名目标会从名称树移除;受影响链接会删除 /Dest 或 GoTo 动作,但保留注解及其他属性

URI 链接保持原样;含 /Next 的链式链接动作被拒绝,因为动作链重映射超出该辅助函数当前范围

格式错误或循环的目标树、不可用对象引用和重复名称树键都会拒绝事务;失败、取消或超预算保留调用方文档

预算与报告

默认上限为 10,000 个书签项、深度 128、1,048,576 个标题字符、10,000 个命名目标、100,000 个页面链接和 256 MiB 文档字节;文档字节上限不得超过 MaxInt

启用附加目标或链接处理时,可配置深度最多 256,命名目标上限不得超过 65,536,页面链接上限不得超过 1,000,000;源捕获还将原始键字节限制为 1 MiB

合并后的命名键另有独立字节预算,其值等于 MaxTitleCharacters;每个源先在配置预算内完成捕获,再过滤所选页面

PreservedNamedDestinationCount 统计最终命名目标,RenamedNamedDestinationCount 统计按所选策略改名的导入键,RemovedNamedDestinationCount 统计未解析或被省略的命名目标

RemappedPageLinkCount 统计现有和导入链接中的最终内部目标,RemovedPageLinkCount 统计移除的内部目标;同一链接同时含 /Dest 和 GoTo 时,每个目标各计一次

公开声明

type
  TPdfMergeNavigationPolicy= (pmnPreserve, pmnGroupBySource);
  TPdfMergeNamedDestinationPolicy= (pndReject, pndRename, pndNamespace);

  TPdfNavigationMergeOptions= record
    NativeOptions: TPdfBatchMergeOptions;
    NavigationPolicy: TPdfMergeNavigationPolicy;
    MaxOutlineItems: Integer;
    MaxOutlineDepth: Integer;
    MaxTitleCharacters: Integer;
    MaxDocumentBytes: Int64;
    PreserveNamedDestinations, PreservePageLinks: Boolean;
    NamedDestinationPolicy: TPdfMergeNamedDestinationPolicy;
    DestinationPrefix: string;
    MaxNamedDestinations, MaxPageLinks: Integer;
    class function Default: TPdfNavigationMergeOptions; static;
  end;

  TPdfNavigationMergeReport= record
    Succeeded: Boolean;
    Cancelled: Boolean;
    ImportedSourceCount: Integer;
    ImportedPageCount: Integer;
    PreservedOutlineCount: Integer;
    RemovedDestinationCount: Integer;
    SourceGroupCount: Integer;
    PreservedNamedDestinationCount, RenamedNamedDestinationCount: Integer;
    RemovedNamedDestinationCount, RemappedPageLinkCount, RemovedPageLinkCount: Integer;
    ErrorMessage: string;
  end;

  // The caller's document is replaced only after the complete staged result
  // has been independently reopened and its outline destinations checked
  TPdfNavigationMergeHelper= class helper for TPdf
    function MergeFilesWithNavigation(const Sources: TPdfBatchMergeSources;
      const Options: TPdfNavigationMergeOptions;
      out Report: TPdfNavigationMergeReport): Boolean;
  end;

function MergePdfFilesWithNavigation(Destination: TPdf;
  const Sources: TPdfBatchMergeSources;
  const Options: TPdfNavigationMergeOptions;
  out Report: TPdfNavigationMergeReport): Boolean;

底层流处理

FPdfCompress 提供 CapturePdfNavigation 和 WritePdfNavigation,供自行管理页面导入和稀疏 PDF 修订的调用方使用

TPdfNavigationNamedDestination.NameKey 包含原始 PDF 键字节;页码是从一开始的绝对页码,零表示未解析目标

TPdfNavigationPageLink.PageNumber 标识所属页;AnnotationIndex 是所有原始注解中从零开始的序号,LinkIndex 则是仅在链接注解中从零开始的序号

写入器通过 LinkIndex 标识链接,以最终所属页克隆各注解,再写入传入的目标页和精确目标后缀;调用方须提供导入所需的完整映射

写入器要求源流和输出流不同,且文档未加密、未签名;同流参数被拒绝时保留源流,其他失败会清空独立输出流

这些流函数不导入页面,也不替换活动的 TPdf 文档;完整导航合并应使用更高层的事务函数

function CapturePdfNavigation(Source: TStream; MaxNamedDestinations,
  MaxPageLinks, MaxDepth: Integer; out Names: TPdfNavigationNamedDestinations;
  out Links: TPdfNavigationPageLinks; out ErrorText: string): Boolean;

function WritePdfNavigation(Source, Output: TStream;
  const Names: TPdfNavigationNamedDestinations;
  const Links: TPdfNavigationPageLinks; ReplaceNamedDestinations: Boolean;
  out ErrorText: string): Boolean;