Skip to content
getnextpdf.com

Migrating off legacy: TCPDF, FPDF, and friends

Spec: ISO 32000-2Spec: ISO 19005-4Spec: ETSI EN 319 142-1

If your PDFs are generated by TCPDF, FPDF, mPDF, or dompdf, the code probably still works. That is exactly why the trouble is easy to miss. The library runs, the file opens, and the gap only shows up the day someone asks for a signed, archivable, or accessible document and the answer is “we can’t from here.”

This page is the migration story: what those walls are, why they are structural rather than incidental, and how NextPDF gives you a staged path off them — including a TCPDF-compatibility surface that is a migration aid, not a promise of a byte-identical drop-in.

A PDF library is not a render call you make once. It is a dependency your documents inherit for as long as they exist. When that dependency stops moving, your documents stop being able to do new things — and you find out at the worst possible moment, when a customer, an auditor, or a regulator sets the bar.

The walls look like this. The format moved on: PDF 2.0 is the current edition of the standard (Spec: ISO 32000-2), and a writer stuck on the 1.x structure is behind the format the rest of your toolchain assumes. Signing is thin or bolted on, well short of the PAdES baseline profiles that make a signature hold up (Spec: ETSI EN 319 142-1, §4). Archival output to the PDF/A family, and tagged structure for accessibility, are either absent or fragile. And the API itself is untyped — string orientations, positional booleans, defaults you discover by accident — so the compiler can’t help you and neither can a reviewer.

None of these are bugs you can patch around. They are the shape of a tool built for an earlier decade, and several of those tools are no longer actively moving toward the standards your documents now have to meet.

  • Legacy PHP PDF libraries mostly still run. The problem is what they commonly cannot produce with full modern conformance: PDF 2.0, baseline-conformant signatures, validated PDF/A, tagged accessibility — support across the named libraries is limited or absent.
  • NextPDF is a PHP 8.4 engine that writes PDF 2.0 by default, with strict types, archival profiles, and PAdES signing as first-class outputs.
  • You do not have to rewrite everything on day one. The TCPDF-compatibility surface lets familiar calls keep working while you move the document logic that matters.
  • That surface is compatible with, not byte-identical to TCPDF. It is a bridge across the migration, with documented behavioral differences — not a claim that every script runs unchanged.
  • The test is whether the new capabilities are worth the move. For some workloads they are not.

The approach is to make migration a sequence, not a leap. You keep producing documents the whole way, and you trade in old constraints one at a time rather than betting a release on a big-bang rewrite.

  1. InventoryCatalogue what your documents actually need to emit — signatures, archival profiles, tagged structure, fonts — not just which calls you make today.
  2. BridgeAdopt the TCPDF-compatibility surface so the existing call sites keep producing files while the engine underneath becomes NextPDF.
  3. PortMove the document logic that matters onto the native typed API, where intent is explicit and the compiler checks it.
  4. UpgradeTurn on the outputs many legacy libraries cannot reach with full modern conformance: PDF 2.0 structure, validated PDF/A, PAdES signatures, tagged accessibility.
  5. VerifyConfirm the result against a real validator, so 'archival' or 'signed' means a tool agrees, not just that the file opened.
A staged migration off a legacy PDF library: start on the compatibility surface so existing calls keep working, then move document logic onto the typed native API, then turn on the standards-grade outputs (PDF 2.0, PDF/A, PAdES, accessibility) that many legacy libraries cannot produce with full modern conformance.

PDF 2.0 is the baseline, not a feature flag. NextPDF writes the current edition of the format by default (Spec: ISO 32000-2), and can serialize older structures when a profile asks for them. A library frozen on the 1.x structure cannot meet you here; it is not a setting it is missing, it is an era it predates.

Archival and accessibility are writer properties. Producing a file a validator accepts as PDF/A is something the engine has to do as it writes — it cannot be stapled on afterward (Spec: ISO 19005-4). The same is true of the tagged structure that makes a PDF accessible. NextPDF builds these during generation, which is precisely the step many legacy tools cannot take — or take only partially, short of what a validator accepts.

Signing clears the baseline bar. Advanced electronic signatures in a PDF follow the PAdES profiles (Spec: ETSI EN 319 142-1, §4), where the digest covers a declared byte range and the signature carries the metadata a validator checks. A bolt-on signing helper rarely reaches that bar. NextPDF treats it as a first-class output, not an afterthought.

The compatibility surface is the bridge. The TCPDF-compat layer exists so your existing call sites keep producing documents while you migrate the parts that matter. It follows the same model as every NextPDF migration guide: compatible with the source library, not byte-identical, with the behavioral differences written down.

The shape of a migration is small at the call site. Old code keeps producing a file through the compatibility surface; new code states intent through the typed native API and asks for an output the legacy library cannot reach, or reaches only with limited conformance.

<?php
declare(strict_types=1);
use NextPDF\Compat\Tcpdf\TCPDF;
use NextPDF\Contracts\Orientation;
use NextPDF\Contracts\OutputDestination;
use NextPDF\Core\Document;
use NextPDF\ValueObjects\PageSize;
// 1) The bridge: a familiar TCPDF-shaped call keeps producing a file
// while the engine underneath is already NextPDF. Behaviour is
// compatible, not byte-identical — differences are documented.
$legacy = new TCPDF();
$legacy->AddPage();
$legacy->SetFont('helvetica', 'B', 16);
$legacy->Cell(0, 12, 'Migrated invoice', ln: 1);
$bridgedBytes = $legacy->Output('', 'S');
// 2) The destination: the same document expressed natively, where intent
// is typed and the engine can emit what many legacy tools cannot.
$document = Document::createStandalone();
$document->setTitle('Migrated invoice');
$document->addPage(PageSize::a4(), Orientation::Portrait);
$document->setFont('helvetica', 'B', 16);
$document->cell(0, 12, 'Migrated invoice', newLine: true);
// Bytes only, no HTTP headers, no file side effect — stated, not inferred.
$nativeBytes = $document->output(dest: OutputDestination::String);

The first block is the foothold: nothing in your application has to change for documents to keep flowing. The second is the destination: a typed call where “portrait”, “string output”, and the font are explicit, and where archival, signing, and accessibility become outputs you can switch on rather than walls you run into.

The frequent hope is “there must be a flag that makes my old library do PDF 2.0 and signatures.” There is not. These are not options a mature library forgot to expose; they are capabilities its architecture was never built around. You cannot configure your way to a format edition or a signature profile a writer does not implement.

The mirror-image misconception is that NextPDF is a 100% TCPDF drop-in, so migration is free. It is not. The compatibility surface covers a real, documented slice of the API to carry you across the move; some calls behave differently, and a few are out of scope. Treat it as a bridge with a published map, not a guarantee that every legacy script runs untouched.

TCPDF-compatibility surface as a migration aid — edition availability
EditionAvailability
Core

The compatibility surface is compatible with, not byte-identical to TCPDF. It covers a documented subset of the API to keep existing call sites producing files during migration. It is a bridge, not a drop-in: some behaviours differ and some calls are unsupported, all listed in the method-coverage and migration pages. The destination is the native typed API, where standards-grade output lives.

ProAvailable
EnterpriseAvailable

Migration is a means, not a virtue. If your documents are simple, your library is still maintained, and you will never need PDF 2.0, signing, PDF/A, or accessibility, the answer may be to stay where you are — switching cost is real and a move you do not need is a move you should not make. The page on when not to use NextPDF draws that line.

This page describes the migration path and the engine’s targets. The exact API coverage, the behavioral differences, and the step-by-step procedure live in the compatibility documentation, which is the authority for what each call does. Nothing here promises that an arbitrary legacy script runs unchanged.

  • PDF 2.0 — the current edition of the Portable Document Format standard (ISO 32000-2). Expanded on first use; the format NextPDF writes by default.
  • PDF/A — the archival conformance family (the ISO 19005 series) that defines what makes a PDF safe to preserve over the long term. A property the writer must produce, not one a caller can add later.
  • PAdES — PDF Advanced Electronic Signatures, the ETSI profile family (EN 319 142) for embedding standards-grade signatures in a PDF. Expanded on first use; covered in depth on the signing pages.
  • Compatibility surface — an API layer shaped like a source library (here, TCPDF) that lets existing call sites keep working during migration. Compatible with, not byte-identical to, the original — a bridge, not a drop-in.
  • Drop-in replacement — a substitute that runs existing code unchanged. The TCPDF-compat surface is deliberately not described this way; it is a documented migration aid with known behavioral differences.