Skip to content
getnextpdf.com

One engine, every framework

Spec: PSR-11 Container, §1.1.2Spec: PSR-4 Autoloader, §3

Most growing PHP estates end up with more than one framework. NextPDF is one PDF engine that meets each of them on its own terms: idiomatic bridges for Laravel, Symfony, and CodeIgniter, plus a standalone path for code that runs in none of them. The document model is shared. Only the way you call it changes.

A different PDF library per stack is a quiet tax. Each one has its own quirks, its own font handling, its own idea of what valid means. An invoice that renders correctly from the Laravel service may render subtly differently from the Symfony worker, because a different library drew it. Now your archival target, your signature placement, and your accessibility tags depend on which team shipped the document. The bug report says “the PDF is wrong”, and the answer depends on which of three engines produced it.

Standardizing on one engine collapses that surface. There is a single place where a PDF/A profile is decided, one font pipeline to certify, one validator to trust. The framework you happen to be in stops being a variable in whether the document is correct.

  • The core engine is framework-agnostic. nextpdf/core knows nothing about HTTP, routing, or container wiring. It is a PDF 2.0 engine and nothing more.
  • Each bridge adapts, it does not reimplement. Laravel, Symfony, and CodeIgniter packages give you a facade or factory, an HTTP response helper, and a queued or async generation path — over the same engine.
  • A bridge follows your framework, not your document. It changes how you call the engine, never what the engine can produce.
  • Standalone is always available. A CLI tool, a daemon, or a library has no framework to bridge from; it constructs a document directly.
  • One document model travels across all four. The same value objects, enums, and output contract appear everywhere, so a document moves between call sites unchanged.

The architecture is a deliberate split. The engine is the asset; the bridge is a thin adapter that speaks one framework’s idioms. A bridge registers a small namespace over the shared core through standard autoloading (Spec: PSR-4 Autoloader, §3) and hands back a document through the container contract (Spec: PSR-11 Container, §1.1.2). That contract is the quiet hero here: it allows two resolutions of the same identifier to return different instances, which is exactly how a bridge gives you a fresh, disposable document per request while keeping the parsed font registry and the image cache as process-wide singletons. Long-lived workers — Octane, RoadRunner, Swoole, Messenger — get amortized font parsing with no cross-request state leak, by construction.

The four idioms differ only at the surface:

  1. Core enginenextpdf/core — the framework-agnostic PDF 2.0 engine; the single shared document model, value objects, and output contract.
  2. Laravel bridgenextpdf/laravel — auto-discovered provider, a Pdf facade, a PdfResponse helper, and a queued GeneratePdfJob.
  3. Symfony bridgenextpdf/symfony — an auto-registered bundle, an injectable PdfFactory, a PdfResponse, and an optional Messenger handler.
  4. CodeIgniter bridgenextpdf/codeigniter — a service and pdf() helper, a Pdf library over a disposable Document, and a PdfResponse.
  5. StandaloneNo framework to bridge from — construct a Document directly in a CLI tool, daemon, or library.
One framework-agnostic core engine reached through four idiomatic surfaces: a Laravel facade, an injected Symfony factory, a CodeIgniter service, or a directly constructed standalone document — each handing back the same disposable Document model.

Read the diagram left to right and the lesson is the symmetry. Every surface resolves to the same Document. The Laravel facade, the Symfony factory, the CodeIgniter service, and the standalone constructor are four doors into one room.

The same three lines of intent, expressed in each idiom. The body that builds the document — pages, fonts, cells, signing, conformance — is identical in all four, because it is the same engine.

<?php
declare(strict_types=1);
// Laravel — resolve a fresh document from the container.
use NextPDF\Contracts\PdfDocumentInterface;
$document = app(PdfDocumentInterface::class);
// Symfony — inject the factory, then ask it for a document.
use NextPDF\Symfony\Service\PdfFactory;
$document = $factory->create(); // PdfFactory injected into your service
// CodeIgniter — pull it from the Services layer.
use NextPDF\CodeIgniter\Config\Services;
$document = Services::pdfDocument();
// Standalone — no framework; construct it directly.
use NextPDF\Core\Document;
$document = Document::createStandalone();
// From here, the code is identical regardless of how $document arrived.
$document->addPage();
$document->cell(0, 10, 'One engine, every framework', newLine: true);
$bytes = $document->getPdfData();

The first lines are the only difference. Everything after is portable: move a document-building service from Symfony to a standalone worker and the rendering code does not change, because the contract it depends on did not.

The frequent assumption is that the framework bridge unlocks capabilities — that long-term signature validation or structured e-invoicing arrives because you installed nextpdf/laravel rather than calling the engine directly. It does not. A bridge changes the call site, never the engine’s reach. Core capabilities such as PDF/A output and PAdES baseline signing are open-source and reach every surface; advanced capabilities are unlocked by an edition and are then available through any bridge or the standalone path equally. Choosing a framework integration is not choosing a feature set.

The mirror misconception is that “one engine” must mean one rendering path for every document. It does not. The in-process engine renders PDF directly; when a document genuinely needs a browser-grade layout engine, a renderer package handles that. Rendering and invocation are separate axes — the integration decision guide is the place that maps them.

A bridge does not expand what the engine can render: capability lives in the core and the tier, not in the adapter you reach it through.

Framework bridges over one engine — edition availability
EditionAvailability
Core

Every bridge (Laravel, Symfony, CodeIgniter) and the standalone path are Apache-2.0 and work against Core. They adapt or expose the engine; they do not gate features and do not change what it can produce.

Pro

Advanced capabilities such as long-term signature validation (PAdES B-LT and B-LTA) are unlocked by an edition, then reached identically through any bridge or standalone — never by switching framework. PDF/A archival output and PAdES baseline signing (B-B and B-T) are already in Core, available the same way through every surface.

Enterprise

Structured e-invoicing (EN 16931) and deeper compliance tooling are edition capabilities too, likewise the same whichever surface calls the engine, while conformance validation itself ships in Core.

Two further boundaries are worth stating plainly. First, each bridge tracks a current major of its framework — Laravel, Symfony, and CodeIgniter each pin a supported range, so “every framework” means the supported version of each, not every historical release; treat each package’s own documentation as authoritative for its API. Second, the bridges are framework adapters, not rendering backends. If a document needs a full browser layout engine, that is a renderer choice independent of which framework called the engine.

  • The integration decision guide — the use-case-to-package map, including renderers and the Connect service surface, when you need to decide rather than standardize.
  • Open core, no lock-in — why the engine is the asset and the bridges are thin, so standardizing does not trap you.
  • The HTML pipeline — what the in-process engine covers, so you know when a browser renderer is the separate question.
  • The PHP 8.4 foundations — the runtime floor every bridge and the standalone path share.
  • Core enginenextpdf/core, the framework-agnostic PDF 2.0 engine every bridge and the standalone path build on.
  • Framework bridge — an integration package (Laravel, Symfony, CodeIgniter) that adapts the engine to a framework’s idioms — facade, factory, response, queued job — without changing its capabilities.
  • Standalone path — using the core engine directly, with no framework, by constructing a Document yourself; the route for CLI tools, daemons, and libraries.
  • Disposable document — the use-once Document contract: build, emit, discard. Each container resolution returns a fresh one, so no state leaks between requests in a long-lived worker.
  • PAdES — PDF Advanced Electronic Signatures, the ETSI profile family for PDF signing. Baseline signing (B-B and B-T) is in Core; long-term validation (B-LT and B-LTA) is an advanced-edition capability. Either is reached through any surface, covered in depth on the signing pages.