Skip to content

Merge document ranges into one PDF

Merges a set of document ranges into a single PDF. The merge itself is always performed without conformance; PDF/A is applied only as an optional, best-effort post-merge pass whose failure is logged rather than surfaced.

Field Value
Operation ID OP-SFC-012
Trigger Caller request — SyncfusionPdfService constructs new PdfMerger(environmentService, logService) and calls Merge (PdfAPI\Services\Syncfusion\SyncfusionPdfService.cs:23)
Inputs MergeRequest (DocumentRanges[], Options, SourceSystemId), optional trackingId
Outputs PdfResult with SourceConversionType.Cache, Status = Succeeded, PdfProcessorType = Syncfusion, Elapsed, ImageId, TrackingId
Primary components PdfMerger.Merge → private MergeDocumentRanges
Entry point PdfApi.SyncFusionCore\Merge\PdfMerger.cs

Steps

  1. Merge creates a discarded Guid and a placeholder PdfResult, starts a Stopwatch, and calls MergeDocumentRanges inside a try/finally; the finally stops the stopwatch and stamps Elapsed.
  2. On failure: there is no catch, so any exception in MergeDocumentRanges propagates to the caller (BR-SFC-028).
  3. Create the output PdfDocument with conformance None — the merge itself is always non-conformant, regardless of Options.IsPdfa (BR-SFC-029).
  4. For each DocumentRange: call range.Document.Load() (reads FilePath from disk when File is null, PdfAPI.Core), then use the whole file when the range is a single original range, or call Extract page ranges from a PDF when the range has more than one PageRange or its first range IsPartial. Collect the byte arrays (BR-SFC-030).

Origin: Step 3's extraction is implemented in Extract page ranges from a PDF (PdfAPI.SyncFusionCore) and executed on behalf of this operation. 4. Call Syncfusion.Pdf.PdfDocument.Merge(outputDoc, docs.ToArray()), save the result to a MemoryStream and capture the bytes. 5. When Options.IsPdfa: reload the merged bytes, set Conformance = Pdf_A1B, save, and — only when the produced byte array is non-empty — replace the result and set request.Options.OutputFormat = PdfFormat.PdfA (BR-SFC-031). - If the PDF/A pass throws: build a warning message (appending [TrackingId: n] when a tracking id was supplied), construct an ApplicationExceptionDetail via Build an application exception detail (PdfHelper.GetApplicationDetail(_envService, "Merge", msg, ex)), and call _logService.LogException(detail). Processing continues and the non-conformant PDF is returned (BR-SFC-032).

 > **Origin**: The exception-detail construction is implemented in [`Build an application exception detail`](build-application-exception-detail.md) (`PdfAPI.SyncFusionCore`) and executed on behalf of this operation.
  1. Wrap the bytes with Document.Load(result, "pdf"), set Status = Succeeded unconditionally, PdfProcessorType = Syncfusion, and OutputFormat = PdfFormat.Pdf — the local outputFormat variable is never updated by the PDF/A branch, so a successful PDF/A merge still reports OutputFormat = Pdf on the result while request.Options.OutputFormat reports PdfA (BR-SFC-033).
  2. Parse request.SourceSystemId into ImageId with long.TryParse, set TrackingId = trackingId ?? 0, and return the result (BR-SFC-009).

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start(["Merge called with<br>MergeRequest, trackingId"]) --> CreateOut["Create output PdfDocument<br>with conformance None"]
    CreateOut --> ForEach{"More DocumentRanges<br>to process?"}
    ForEach -->|Yes| LoadRange[range.Document.Load]
    LoadRange --> RangeType{"Single original<br>range?"}
    RangeType -->|Yes| WholeFile[Use whole file]
    RangeType -->|No| ExtractSub["Extract page ranges<br>from a PDF"]
    WholeFile --> ForEach
    ExtractSub --> ForEach
    ForEach -->|No| MergeDocs["PdfDocument.Merge<br>all collected docs"]
    MergeDocs --> Save[Save merged bytes]
    Save --> PdfaCheck{"Options.IsPdfa?"}
    PdfaCheck -->|No| Wrap["Wrap with Document.Load,<br>Status = Succeeded"]
    PdfaCheck -->|Yes| Reload["Reload, set Conformance<br>= Pdf_A1B, save"]
    Reload --> ReloadOk{"Reload throws?"}
    ReloadOk -->|No| ReplaceCheck{"Produced bytes<br>non-empty?"}
    ReplaceCheck -->|Yes| PromoteFormat["Replace result,<br>promote OutputFormat<br>to PdfA"]
    ReplaceCheck -->|No| Wrap
    PromoteFormat --> Wrap
    ReloadOk -->|Yes| LogWarn["Log warning via<br>ApplicationExceptionDetail,<br>keep non-conformant PDF"]
    LogWarn --> Wrap
    Wrap --> ParseId["Parse SourceSystemId,<br>set TrackingId"]
    ParseId --> Result([Return PdfResult])

Failure and edge-case behavior

  • A failure before or during the initial merge (steps 1–4) propagates to the caller — there is no surrounding catch.
  • A failure only in the post-merge PDF/A pass (step 5) is caught, logged, and does not fail the operation: the plain PDF is returned and Status is still Succeeded.
  • A merge result is always reported Succeeded, even when the PDF/A upgrade failed and only the non-conformant PDF was returned.

Sources

  • PdfApi.SyncFusionCore\Merge\PdfMerger.cs
  • PdfApi.SyncFusionCore\Extract\PdfExtractor.cs
  • PdfApi.SyncFusionCore\PdfHelper.cs
  • PdfAPI\Services\Syncfusion\SyncfusionPdfService.cs