plusforta / pdf-forms
Pure PHP library for filling AcroForm fields in PDF documents
Requires
- php: ^8.1
- ext-zlib: *
Requires (Dev)
- phpunit/phpunit: ^10.0
README
A pure PHP library for filling AcroForm fields in PDF documents. No external binaries required — no pdftk, no Java, no command-line tools.
Requirements
- PHP 8.1 or higher
ext-zlib(included in most PHP installations)
Installation
composer require plusforta/pdf-forms
Usage
Fill form fields and save
use PlusForta\PdfForms\PdfForm;
PdfForm::open('template.pdf')
->fill([
'name' => 'Max Mustermann',
'email' => 'max@example.com',
'date' => '2024-01-15',
])
->save('output.pdf');
Set fields individually
PdfForm::open('template.pdf')
->set('name', 'Max Mustermann')
->set('date', '2024-01-15')
->save('output.pdf');
Return PDF as string
Useful for HTTP responses or storing in S3:
$pdfBytes = PdfForm::open('template.pdf')
->fill(['name' => 'Max Mustermann'])
->toString();
// e.g. in Laravel
return response($pdfBytes, 200, [
'Content-Type' => 'application/pdf',
]);
Load from bytes
$pdfBytes = Storage::get('templates/form.pdf');
PdfForm::load($pdfBytes)
->fill(['name' => 'Max Mustermann'])
->save('output.pdf');
Inspect available fields
$fields = PdfForm::open('template.pdf')->getFields();
foreach ($fields as $name => $field) {
echo "$name ({$field->type->value})\n";
}
Each field is a PlusForta\PdfForms\Form\Field object with properties: name, type, value, options.
Flatten fields
Flattening bakes field values into the page as static content. The resulting PDF has no editable form fields.
// Flatten all fields
PdfForm::open('template.pdf')
->fill(['name' => 'Max Mustermann'])
->flatten()
->save('output.pdf');
// Flatten specific fields only
PdfForm::open('template.pdf')
->fill(['name' => 'Max', 'date' => '2024-01-15'])
->flatten(['name']) // only 'name' is flattened; 'date' stays editable
->save('output.pdf');
Appearance streams and /NeedAppearances
An appearance stream is a small drawing program embedded in the PDF that tells viewers exactly how to render a form field — its text, font, size, color, and position. Without one, a viewer has no instructions for displaying the field's value.
When a PDF sets /NeedAppearances true, it tells the viewer: "Ignore any embedded appearance streams and regenerate the visual appearance of every field yourself." This is a safety net — it guarantees fields display their values even if appearance streams are missing or outdated, but it hands control of rendering to the viewer.
This library generates its own appearance streams for text fields, so you have two choices:
/NeedAppearances true (default) | /NeedAppearances false | |
|---|---|---|
| How it works | Viewer regenerates field appearances from scratch | Viewer uses the library's embedded appearance streams |
| Compatibility | Works in all viewers, even if appearance streams are missing | Requires valid appearance streams (this library generates them) |
| Visual consistency | Rendering varies by viewer (fonts, spacing may differ) | Looks the same across viewers |
| Side effects | Adobe Acrobat may prompt "Do you want to save?" on open | No prompts or regeneration |
For most use cases, the default (true) is the safest option. If you need consistent rendering across viewers or want to avoid Acrobat's save prompt, disable it:
PdfForm::open('template.pdf')
->fill(['name' => 'Max Mustermann'])
->needAppearances(false)
->save('output.pdf');
Supported field types
| Type | Read | Fill | Appearance stream |
|---|---|---|---|
Text (/Tx) | Yes | Yes | Yes |
Checkbox (/Btn) | Yes | Yes | No (uses existing) |
Radio button (/Btn) | Yes | Yes | No (uses existing) |
Dropdown (/Ch) | Yes | Yes | No (uses existing) |
Listbox (/Ch) | Yes | Yes | No (uses existing) |
Signature (/Sig) | Yes | No | No |
Error handling
All errors throw exceptions extending PlusForta\PdfForms\Exception\PdfFormsException:
| Exception | When |
|---|---|
FileNotFoundException | Input file doesn't exist |
InvalidPdfException | PDF can't be parsed or is encrypted |
NoFormFieldsException | PDF has no AcroForm dictionary |
FieldNotFoundException | Referencing a field name that doesn't exist |
WriteException | Can't write to output path |
Limitations
- Encrypted PDFs are not supported.
- XFA forms (XML-based) are not supported. Only classic AcroForm fields.
- Cross-reference streams (PDF 1.5+ compressed xref) are not yet supported. Classic xref tables work fine.
- Right-to-left text is not supported in appearance streams.
- Text that overflows a field rectangle is clipped, not auto-sized.
Running tests
composer install
vendor/bin/phpunit
License
MIT