Include FPdfMerge in the uses clause for TPdfNavigationMergeHelper.MergeFilesWithNavigation, or pass a destination document explicitly to the global MergePdfFilesWithNavigation function
Before replacing the caller's document, the helper stages native page imports, rebuilds outline hierarchies, combines named destinations, remaps page links and reopens the result independently
Use pmnPreserve for the source hierarchy, or pmnGroupBySource to add a heading bearing each source filename
The merge retains titles, parent relationships, styles, colours, expanded branches, URI actions and resolved internal page destinations; selected, repeated or reordered pages point to the first imported occurrence, whilst destinations on omitted pages become headings
Inserting pages shifts existing destination outlines; source files remain open without write sharing while navigation and native page imports are captured
Merging must stop on error; signed destinations, unsupported outline actions and encrypted postprocessing are refused with an error report, and neither failure nor cancellation alters the caller's document
Native form handling follows the existing policy; advanced AcroForm collision and resource handling belongs to FPdfFormMerge
Both PreserveNamedDestinations and PreservePageLinks start as True; changing either to False omits its additional navigation-processing step
The reader handles the legacy catalog /Dests dictionary and the /Names/Dests name tree, including nested trees, indirect arrays and destination dictionaries with /D; the name-tree entry wins if both define the same key
Output has a balanced /Names/Dests tree and retains other destination name trees such as /EmbeddedFiles; the helper does not import unrelated source trees
Keys keep their original PDF string bytes, including UTF-16BE; exact view modes and parameters survive, including /FitH null and /XYZ null 34 null
| Policy | Behaviour |
|---|---|
pndReject | If a source key is already present in the destination, the entire transaction is refused |
pndRename | The default retains unique names and adds DestinationPrefix to conflicting names; its default is Imported_, with further conflicts resolved deterministically, for example 2_Imported_Shared |
pndNamespace | Each source key gains DestinationPrefix, its one-based source index and an underscore, for example Imported_1_Shared; any remaining conflict receives a deterministic numeric prefix |
DestinationPrefix needs 1 to 256 printable ASCII characters; for UTF-16BE keys the prefix follows the BOM
An internal link with /Dest or one /A /GoTo action is rewritten to an explicit destination, so collision renaming does not break a named link target
Page insertion shifts existing link owners and targets; every repeated import gets its own link annotation, whose internal targets point to the first selected occurrence of the target page
A named destination on an omitted page is removed from the tree; affected links lose their /Dest or GoTo action but retain the annotation and its remaining properties
URI links are retained; /Next action chains are refused because this helper does not currently remap chained actions
The transaction rejects malformed or cyclic destination trees, unresolved object references and duplicate name-tree keys; failed, cancelled or over-budget operations leave the caller's document intact
Defaults permit 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 limit cannot exceed MaxInt
With extra destination or link processing enabled, depth cannot exceed 256, named destinations cannot exceed 65,536 and page links cannot exceed 1,000,000; capturing a source also limits raw key data to 1 MiB
Combined named keys have their own byte allowance equal to MaxTitleCharacters; each source is captured within the configured allowances before page selection is applied
PreservedNamedDestinationCount counts final named destinations, RenamedNamedDestinationCount counts imported keys renamed by the policy, and RemovedNamedDestinationCount counts omitted or unresolved named targets
RemappedPageLinkCount counts final internal destinations in existing and imported links; RemovedPageLinkCount counts those removed, and a link with both /Dest and GoTo counts separately 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 provides CapturePdfNavigation and WritePdfNavigation for applications managing their own page imports and sparse PDF revisions
TPdfNavigationNamedDestination.NameKey holds the original PDF key bytes; page numbers are absolute and one-based, and zero identifies an unresolved target
TPdfNavigationPageLink.PageNumber is the owner page; AnnotationIndex is its zero-based position amongst all original annotations, whilst LinkIndex counts link annotations alone
The writer uses LinkIndex to find each link, clones the annotation for its final owner page and writes the supplied targets with their exact destination suffixes; provide every mapping your imports require
Writing requires separate source and output streams and an unsigned, unencrypted document; rejection of identical streams preserves the source, whilst other failures empty the separate output
Stream functions neither import pages nor replace a live TPdf document; complete navigation merging should use the higher-level transactional function
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;