Library / SDK
Setasign/FPDI avatar
Setasign/FPDI

FPDI puts pages from an existing PDF behind a form in FPDF

FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF.

1,250 stars178 forksPHPMIT

At a glance

What is it?
FPDI is a set of PHP classes that reads pages from existing PDF documents and imports them as templates into FPDF, TCPDF or tFPDF, without requiring any special PHP extension. It is MIT licensed, at version 2.6.9, and the last push was on 2026-10-13.
Who is it for?
FPDI suits a PHP developer who has to produce a PDF on top of a pre-existing document, an invoice on company letterhead for instance, and who wants the import to happen in pure PHP rather than through a binary. It is the wrong tool if you need to edit the content of the imported page, since FPDI places the original page as a template and does not change its content, and it is the wrong tool if you need to produce the PDF from scratch.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A template importer, not a PDF editor

FPDI is a collection of PHP classes that lets developers read pages from existing PDF documents and use them as templates in FPDF, which was developed by Olivier Plathey.

The distinction that matters is between importing a page and editing one. FPDI brings in a page from a source document as a reusable template, and then you draw your own content on top of it. That is exactly the invoice-on-letterhead case, the form-with-a-branded-background case, and the report-with-a-cover-page case. It is not the case where you need to change a paragraph in the source PDF, because the imported page is a fixed piece of content that FPDI places, not a document model you can manipulate.

The plugin relationship is also unusual and worth understanding. FPDI can be used as an extension for TCPDF or tFPDF, and in each case the class you instantiate is a different one, because the base PDF generator differs. The README's example makes this explicit with the alternative use statements for setasign\Fpdi\Fpdi, setasign\Fpdi\Tcpdf\Fpdi and setasign\Fpdi\Tfpdf\Fpdi.

One practical advantage is stated plainly: apart from a copy of FPDF, FPDI does not require any special PHP extensions. There is no Ghostscript, no ImageMagick and no compiled helper. A PDF template import that needs no system dependency is a meaningful constraint to have, because the alternatives in this space generally want a rendering binary on the host.

The library is MIT licensed, published on Packagist as setasign/fpdi, and the current release is 2.6.9 from 2026-10-13, with the last push the same day.

Installing with Composer means declaring the generator yourself

The Composer installation is the part with a decision in it, and the README explains why.

Because FPDI can be used with FPDF, TCPDF or tFPDF, the project has not added a fixed dependency in the main composer.json file. You need to add the dependency to the PDF generation library of your choice yourself.

So for FPDF:

json
{
    "require": {
        "setasign/fpdf": "1.9.*",
        "setasign/fpdi": "^2.6"
    }
}

For TCPDF the require block changes the PDF library and keeps the same FPDI constraint:

json
{
    "require": {
        "tecnickcom/tcpdf": "6.11.*",
        "setasign/fpdi": "^2.6"
    }
}

And for tFPDF:

json
{
    "require": {
        "setasign/tfpdf": "1.33.*",
        "setasign/fpdi": "^2.6"
    }
}

That the generator is a separate requirement is the design, not an oversight. A single package that hard requires FPDF would make TCPDF users pull in two generators, and the alternative is a meta package or a plugin architecture that the project has not chosen. The cost is that the composer.json of your application now has to carry a line the library author cannot put there for you.

There is also a manual installation path for projects not using Composer, and it is two lines:

php
require_once('src/autoload.php');

And for a PSR-4 autoloader, you register the namespace yourself with addNamespace for setasign\Fpdi pointed at the src path. The second form is what a framework integration does.

The import workflow is four calls

The README's example is short, and it is the whole workflow: set the source file, import a page, use the template, output.

php
<?php
use setasign\Fpdi\Fpdi;
// or for usage with TCPDF:
// use setasign\Fpdi\Tcpdf\Fpdi;

// or for usage with tFPDF:
// use setasign\Fpdi\Tfpdf\Fpdi;

// setup the autoload function
require_once('vendor/autoload.php');

// initiate FPDI
$pdf = new Fpdi();
// add a page
$pdf->AddPage();
// set the source file
$pdf->setSourceFile("Fantastic-Speaker.pdf");
// import page 1
$tplId = $pdf->importPage(1);
// use the imported page and place it at point 10,10 with a width of 100 mm
$pdf->useTemplate($tplId, 10, 10, 100);

$pdf->Output();            

Four steps, and each one has a job. setSourceFile points at the template document. importPage takes a page number and returns a template identifier, which is an opaque handle rather than a page object. useTemplate places that template at an x and y coordinate with a width, and the height follows the template's aspect ratio unless you pass one. Output then writes the document.

The handle approach is the notable design choice. Because a template is a reference into the imported page's resources rather than a value you can manipulate, you import once and can place the same page several times, which is how a watermark or a repeated letterhead is applied. It also means you cannot, for instance, ask the template for its text and rewrite it, which is the limit mentioned above.

AddPage comes before the import in the example, and the order matters for the output document: the pages you add are the new pages, and the imported ones are drawn onto them.

Version 2 changed method signatures and return shapes

The README devotes a section to the differences from version 1, and it is worth reading even if you are starting fresh, because those are the shapes the current API has.

Version 2 is a complete rewrite from scratch of FPDI which comes with namespaced code, a clean and up-to-date code base and style, PSR-4 compatible autoloading, performance improvements by up to 100%, less memory consumption, native support for reading PDFs from strings or stream-resources, support for documents with invalid data before their file-header, optimized page tree resolving, usage of individual exceptions, and several test types which are unit, functional and visual tests.

The stated goal was to keep the main methods and logical workflow the same as in version 1, but there were incompatible changes to consider:

You need to load the code using the src/autoload.php file instead of classes/FPDI.php. The classes and traits are namespaced now under setasign\Fpdi. Page boundaries beginning with a slash, such as /MediaBox, are not supported anymore, and you should remove the slash or use a constant of PdfReader\PageBoundaries. The parameters $x, $y, $width and $height of the useTemplate() or getTemplateSize() method have more logically correct default values now, and passing 0 as width or height results in an InvalidArgumentException. The return value of getTemplateSize() changed to an array with more speaking keys, so use width instead of w and height instead of h. And for FPDI with TCPDF you need to refactor to use the class Tcpdf\Fpdi, since 2.1, instead of TcpdfFpdi.

Two of these bite hardest in an upgrade. The getTemplateSize() key rename is a silent behaviour change if you read the old keys, since an undefined array key is a notice rather than an error in many PHP configurations. And the slash-in-page-boundary change converts a working configuration into one that fails to find the page box.

The main version line is marked in the README as referring to FPDI 2, and version 1 is deprecated with development discontinued.

The repository is a modern PHP project with real tooling

The top level of the repository shows a library that is still actively maintained and takes its tooling seriously.

There is a composer.json and a composer.lock, a phpcs.xml for coding standards and a phpstan.neon for static analysis, plus a phpunit.xml for the test suite. The tests/ directory holds the tests, and local-tests/ is a separate directory for tests that are not part of the main run. The vendor-bin/ directory is the Composer mechanism for per-package binary dependencies, which is how a project pins its own analysis tools without putting them in the main dependency set.

The scratches/ directory is worth a second look, because it says something about how the library is developed. A scratches directory is where you put the throwaway scripts you use to reproduce a customer's malformed PDF and work out what the parser is choking on. For a library whose job is reading documents produced by dozens of tools, having a place for those reproductions is a real part of the workflow.

The src/ directory is the library itself, and the LICENSE.txt and SECURITY.md files at the top are the usual pair for a package with a disclosure process. The .gitattributes and .github/ directories handle the repository plumbing.

The .env.sample file is not in this repository, but the top level does include a scratch area and separate local test directory, which together suggest the test suite covers both the public API and the edge cases that show up in real files. Combined with the README's claim of unit, functional and visual tests, the visual test type is the interesting one, since a PDF library's output is a rendering and a rendering has to be compared to something.

Editorial conclusion

FPDI suits a PHP developer who has to produce a PDF on top of a pre-existing document, an invoice on company letterhead for instance, and who wants the import to happen in pure PHP rather than through a binary. It is the wrong tool if you need to edit the content of the imported page, since FPDI places the original page as a template and does not change its content, and it is the wrong tool if you need to produce the PDF from scratch. Before adopting it, check whether the template PDF you have is one the importer can read, since the library has documented limits around malformed files and page boundary definitions, and read the version 2 migration notes because several method signatures and return shapes changed.

Frequently asked questions

What is FPDI?

It is a collection of PHP classes that lets developers read pages from existing PDF documents and use them as templates in FPDF, which was developed by Olivier Plathey. It can also be used as an extension for TCPDF or tFPDF, and apart from a copy of FPDF it requires no special PHP extensions.

How do I install FPDI with Composer?

You declare both the PDF generator and FPDI yourself, because the project has not added a fixed dependency. For FPDF the require block is setasign/fpdf 1.9.* together with setasign/fpdi ^2.6, and there are equivalent blocks for tecnickcom/tcpdf 6.11.* and setasign/tfpdf 1.33.*.

How do I import a PDF page as a template?

Set the source file with setSourceFile, call importPage with the page number to get a template id, then place it with useTemplate at an x and y coordinate with a width, and call Output. The template id is a handle you can reuse to place the same page more than once.

What changed between FPDI version 1 and version 2?

Version 2 is a complete rewrite with namespaced code under setasign\Fpdi and PSR-4 autoloading. The incompatible changes include loading src/autoload.php instead of classes/FPDI.php, no longer supporting page boundaries that begin with a slash, an InvalidArgumentException when width or height is passed as 0, and getTemplateSize() returning width and height keys instead of w and h.

Does FPDI need a special PHP extension or an external program?

No. Apart from a copy of FPDF, FPDI does not require any special PHP extensions, so there is no rendering binary to install on the host. The current release is 2.6.9 from 2026-10-13 and the licence is MIT.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. Setasign/FPDI on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/setasign-fpdi.svg)](https://hysenlabs.com/projects/setasign-fpdi)

Was this useful?