PDF Import

Importing source PDFs as Form XObjects with tc-lib-pdf

tc-lib-pdf can import pages from existing PDFs as Form XObjects and place them on destination pages.

Register A Source

$sourceId = $pdf->setImportSourceFile('/path/to/source.pdf');
// or: $sourceId = $pdf->setImportSourceData($rawPdfBytes);

$count = $pdf->getSourcePageCount($sourceId);

The page count is derived from the page tree actually reachable through /Kids; the declared /Count entry of the /Pages dictionary is ignored, so a forged or wrong /Count cannot influence how many pages are counted or imported. Structurally broken page trees (missing /Kids, unexpected node types, duplicate or cyclic references) raise ImportCorruptedSourceException.

The reachable-page walk runs once per registered source and produces a flattened page index, with one effective page dictionary per page and inherited attributes resolved. That index is cached and reused by getSourcePageCount(), importPage(), and importPages().

Parser Limits And Diagnostics

setImportSourceFile() and setImportSourceData() accept an optional configuration array that is forwarded to the parser. The source is parsed once, at registration time, so these settings belong here rather than in the per-page importPage() options.

KeyTypeDefaultMeaning
ignore_filter_errorsboolfalseKeep a stream that fails to decode as raw data instead of failing
decode_streamsboolfalseDecode stream payloads while parsing indirect objects
max_stream_sizeint33554432Maximum size in bytes of a single decoded stream; 0 means unlimited
max_resolution_depthint64Maximum number of indirect object resolutions in flight at once; values below 1 are clamped to 1
max_nesting_depthint256Maximum nesting depth of array and dictionary objects; values below 1 are clamped to 1
strict_limitsboolfalseFail with ImportResourceLimitException as soon as a limit leaves an object unresolved

Exceeding max_nesting_depth always raises ImportResourceLimitException: a dictionary or array that cannot be tokenized has no usable value to fall back to. Reaching max_resolution_depth, or meeting a reference cycle, leaves that reference unresolved and lets the rest of the document import; the affected pages may lose content whose object could not be reached.

Those non-fatal cases are recorded as document warnings, one per kind of event, with the number of occurrences and the first object affected:

$sourceId = $pdf->setImportSourceFile('/path/to/source.pdf');
$pdf->importPages($sourceId);
$pdf->getOutPDFString();

foreach ($pdf->getWarnings() as $warning) {
    // "The source document 3fa8c1d2 was not fully resolved while parsing: the indirect
    //  object resolution depth limit (64) left a reference unresolved 7 times, first
    //  at object 128_0"
}

Like every other document warning, the list is complete only after getOutPDFString() has been called.

To fail instead of degrading, set strict_limits:

try {
    $sourceId = $pdf->setImportSourceFile('/path/to/source.pdf', ['strict_limits' => true]);
} catch (\Com\Tecnick\Pdf\Import\ImportResourceLimitException $exc) {
    // the source needs higher limits, or cannot be imported faithfully
}

To import a document that legitimately nests deeper than the defaults, raise the limits:

$sourceId = $pdf->setImportSourceFile('/path/to/source.pdf', [
    'max_resolution_depth' => 512,
    'max_nesting_depth'    => 2048,
]);

ImportResourceLimitException extends ImportCorruptedSourceException, so a handler for the latter around source registration also catches limit failures.

Page Content Streams

A page /Contents entry is accepted in every form the specification allows: a stream, an array of streams, or an indirect reference to either. Several streams are decoded and concatenated into the single stream of the resulting Form XObject.

A page whose /Contents entry cannot be resolved to any stream is imported as an empty Form XObject and reported as a document warning, so a blank imported page is never silent:

$pdf->importPage($sourceId, 1);
$pdf->getOutPDFString();

foreach ($pdf->getWarnings() as $warning) {
    // "The imported page 1 has a /Contents entry but no content stream could be
    //  extracted: the page will be blank"
}

A /Contents stream that exists but is empty, and an empty /Contents array, are legal empty contents and are not reported.

Import One Page

$tpl = $pdf->importPage($sourceId, 1, [
    'box' => 'CropBox',          // MediaBox|CropBox|BleedBox|TrimBox|ArtBox
    'groupXObject' => true,
    'cache' => true,
    'respectRotation' => true,
]);

$pdf->addPage();
$placed = $pdf->useImportedPage($tpl, 20, 20, 120, 80, [
    'keepAspectRatio' => true,
    'align' => 'CC',             // TL|TC|TR|CL|CC|CR|BL|BC|BR
    'clip' => true,
]);

Append Pages From Another Document

$templates = $pdf->appendDocument($sourceId);
$templates = $pdf->appendDocument($sourceId, [1, 3, 5]);
$tpl = $pdf->addPageFromImport($sourceId, 2);

Runnable Examples

Limitations And Fidelity Notes

  • Imported pages are placed as Form XObjects rather than merged into editable destination structures.
  • Digital signatures from source files are not preserved as valid signatures in the output.
  • Encrypted source PDFs are not importable with the bundled parser backend. Password-like options are accepted by the import API, but encrypted inputs fail with an explicit exception.
  • Multi-stream page contents are normalized by decoding and concatenating the stream bytes; this changes the low-level byte representation while preserving the rendered appearance.
  • Transparency groups are suppressed automatically when the active conformance mode disallows transparency: PDF/A-1, PDF/X-1a, or PDF/X-3.
  • Stream filters are conformance-aware. A source stream compressed with LZWDecode, which ISO 19005 forbids, is decoded and re-encoded with FlateDecode. When the stream cannot be re-encoded (an undecodable filter chain), a PDF/A destination raises ImportUnsupportedFeatureException and any other mode keeps the source stream unchanged. Importing a JPXDecode stream into a PDF/A-1 document raises the same exception, since ISO 19005-1 does not allow that filter.
  • Setting groupXObject to false can reduce output size but may alter compositing for some pages.
  • Parsing of the source document is bounded by the tc-lib-pdf-parser limits: decoded stream size (32 MiB by default), array and dictionary nesting depth, indirect object resolution depth, and number of chained cross-reference sections. A document that exceeds one of them is either reported as a warning or refused with an exception, as described above, instead of exhausting memory.
  • Non-embedded fonts of an imported page are reported as warnings. The check walks at most 1024 resource dictionaries per page, and a warning states when it stopped there, since the source may use further non-embedded fonts beyond that point.

Previous: /docs/standards/

Overview: /docs/

Next: /docs/cache/