Add FPdfMerge to the uses clause to enable TPdfNavigationMergeHelper.MergeFilesWithNavigation, or call the global MergePdfFilesWithNavigation function with an explicit destination document
The helper stages native page imports, rebuilds hierarchical outlines, combines named destinations, remaps page links and independently reopens the result before replacing the caller document
pmnPreserve retains source hierarchy; pmnGroupBySource adds a heading named after each source file
Titles, parent relationships, styles, colors, expanded branches, URI actions and resolved internal page destinations are preserved; selected, repeated and reordered pages map to the first imported occurrence, and destinations to omitted pages become headings
Existing destination outlines shift when pages are inserted; source files remain open with write sharing denied while navigation and native page imports are captured
Only stop-on-error merging is supported; signed destinations, unsupported outline actions and encrypted postprocessing are rejected with an error report, and failure or cancellation preserves the caller document
This API retains the existing native form policy; use FPdfFormMerge when you need advanced AcroForm field collision and resource handling
PreserveNamedDestinations and PreservePageLinks both default to True; setting either option to False skips that part of the additional navigation processing
Named destinations are read from both the legacy catalog /Dests dictionary and the /Names/Dests name tree, including hierarchical trees, indirect arrays and destination dictionaries containing /D; when both forms define the same key, the name-tree value takes precedence
The result uses a balanced /Names/Dests tree and retains unrelated destination name trees such as /EmbeddedFiles; source trees unrelated to destinations are not imported by this helper
Keys retain their original PDF string bytes, including UTF-16BE encoding; the exact view mode and parameters are preserved, including /FitH null and /XYZ null 34 null
| Policy | Behavior |
|---|---|
pndReject | A source key that already exists in the destination rejects the whole transaction |
pndRename | The default policy keeps nonconflicting names and prefixes conflicting names with DestinationPrefix, which defaults to Imported_; further collisions use deterministic prefixes such as 2_Imported_Shared |
pndNamespace | Every source key is prefixed with DestinationPrefix, its one-based source index and an underscore, such as Imported_1_Shared; remaining collisions receive a deterministic numeric prefix |
DestinationPrefix must contain 1 to 256 printable ASCII characters; prefixes are inserted after a UTF-16BE BOM when a key uses that encoding
Internal link annotations using /Dest or a single /A /GoTo action are rewritten as explicit destinations; named link targets therefore remain correct after a collision rename
Existing link owner pages and targets shift when pages are inserted; each occurrence of a repeatedly imported page receives an independent link annotation, while its internal targets map to the first selected occurrence of the target page
Destinations whose target page was omitted are removed from the named-destination tree; an affected link has its /Dest or GoTo action removed while retaining the annotation and its other properties
URI links remain intact; chained link actions using /Next are rejected because remapping action chains is outside this helper's current scope
Malformed or cyclic destination trees, unavailable object references and duplicate name-tree keys reject the transaction; failure, cancellation or an exceeded budget leaves the caller document unchanged
Default limits are 10,000 outline items, depth 128, 1,048,576 title characters, 10,000 named destinations, 100,000 page links and 256 MiB of document bytes; the document-byte ceiling must not exceed MaxInt
When additional destination or link processing is enabled, the maximum configured depth is 256, the named-destination limit must not exceed 65,536 and the page-link limit must not exceed 1,000,000; source capture also limits raw key bytes to 1 MiB
Combined named keys use a separate byte budget equal to MaxTitleCharacters; each source is captured within the configured budgets before selected-page filtering
PreservedNamedDestinationCount counts final named destinations, RenamedNamedDestinationCount counts imported keys changed by the selected policy and RemovedNamedDestinationCount counts unresolved or omitted named targets
RemappedPageLinkCount counts final internal destinations across both existing and imported links, and RemovedPageLinkCount counts removed internal destinations; a link containing both /Dest and GoTo contributes once for each destination
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 exposes CapturePdfNavigation and WritePdfNavigation for callers that manage their own page imports and sparse PDF revisions
TPdfNavigationNamedDestination.NameKey contains raw PDF key bytes; page numbers are absolute and one-based, while zero denotes an unresolved target
TPdfNavigationPageLink.PageNumber identifies the owner page; AnnotationIndex is the zero-based ordinal among all original annotations and LinkIndex is the zero-based ordinal among link annotations only
The writer identifies links by LinkIndex, clones each annotation with its final page owner and writes the supplied page targets and exact destination suffixes; callers must supply the complete mapping needed for their imports
The writer requires distinct source and output streams and an unencrypted, unsigned document; argument rejection with identical streams preserves the source, and other failures clear the separate output stream
These stream functions do not import pages or replace a live TPdf document; use the higher-level transactional function for complete navigation merging
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;