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