Skip to content

Modifying Existing PDFs

Dragon edited this page Jun 11, 2026 · 8 revisions

Modifying Existing PDFs

PdfEditor::open() (or PdfEditor::fromBytes()) opens an existing non-encrypted PDF and lets you update its metadata, append new pages, fill its AcroForm fields, and sign it - all without touching the original bytes. Every change is written as an appended incremental revision, so the original content (including any signatures already present) remains byte-for-byte intact.

Encrypted sources are rejected at open time with a clear PdfException. Decrypt the file first (e.g. with qpdf --decrypt) before opening it here.

Both classic xref tables and cross-reference streams (PDF 1.5+) are supported.

use DragonOfMercy\PhpPdf\PdfEditor;

$pdf = PdfEditor::open('invoice.pdf');       // open from file path
// or
$pdf = PdfEditor::fromBytes($pdfBytes);      // open from a string of bytes

Updating metadata

Use the fluent setters to update the document's information dictionary. When the file already contains an XMP metadata packet, it is refreshed to stay consistent.

$pdf = PdfEditor::open('report.pdf');

$pdf->setTitle('Annual Report 2025')
    ->setAuthor('Acme Corp')
    ->setSubject('Finance')
    ->setKeywords('annual report finance 2025')
    ->setCreator('ReportGenerator 3.0');

$pdf->save('report-updated.pdf');
// or get the bytes directly:
$bytes = $pdf->output();

All five setters are optional - set only what you need. save() writes to a file; output() returns the PDF bytes as a string.

Appending pages

appendPage() appends a blank page and returns a fully functional Page object. You can draw on it with the complete page API: cells, images, tables, barcodes, SVG, Markdown, and so on.

use DragonOfMercy\PhpPdf\PdfEditor;
use DragonOfMercy\PhpPdf\Font;

$pdf = PdfEditor::open('contract.pdf');

$page = $pdf->appendPage();
$page->setFont(Font::helvetica(), 11);
$page->cell(0, 10, 'Addendum - see attachment A');

$pdf->save('contract-with-addendum.pdf');

The appended page is part of the same incremental revision as any metadata edits you make, so a single save() / output() call writes everything at once.

Filling form fields

Open a PDF that contains an AcroForm, inspect its fields, set values, and save. Each filled field gets a generated appearance stream (/AP) so the value is rendered without relying on viewer-side rendering, and /NeedAppearances false is written.

Listing fields

use DragonOfMercy\PhpPdf\PdfEditor;

$pdf = PdfEditor::open('form.pdf');

foreach ($pdf->formFields() as $field) {
    echo $field->name, ' [', $field->type, ']', PHP_EOL;
}

formFields() returns list<FormFieldInfo>. field($name) returns a single FormFieldInfo or throws if the name is not found.

Setting field values

$pdf = PdfEditor::open('application.pdf');

$pdf->setField('FullName', 'Alice Dupont');          // text (single-line or multiline)
$pdf->setField('Subscribed', true);                  // checkbox
$pdf->setField('Gender', 'female');                  // radio (export-name string)
$pdf->setField('Country', 'ch');                     // combobox (export key)
$pdf->setField('Languages', ['en', 'fr']);            // listbox (one or more export keys)

$pdf->save('application-filled.pdf');
Field type Value type Notes
Text (single-line) string Written as-is.
Text (multiline) string Newlines preserved.
Checkbox bool true checks the field.
Radio button string The export name of the button to select.
Combobox string The export key of the chosen option.
Listbox string|list<string> Pass an array to select multiple items.

Limitations. Appearance generation supports Standard-14 /DR fonts only; fields whose default-resource font is embedded or non-standard throw a PdfException. Fields are not flattened - they remain interactive after filling.

Signing

PdfEditor::sign(), addSignature(), addDocumentTimestamp(), and enableLtv() work on an opened PDF with full parity to Document. Each signature, document timestamp, or /DSS is written as a stacked incremental revision that leaves the original bytes intact and cryptographically covers every prior byte - including any metadata edits, appended pages, and field fills, which are written as the first revision.

use DragonOfMercy\PhpPdf\PdfEditor;
use DragonOfMercy\PhpPdf\Signature\SigningCertificate;
use DragonOfMercy\PhpPdf\Signature\Tsa;

$pdf = PdfEditor::open('form.pdf');
$pdf->setField('FullName', 'Alice');

$pdf->sign(
    SigningCertificate::fromPkcs12('cert.p12', 'password'),
    field: 'Approval',
    reason: 'I approve this document',
    timestamp: Tsa::http('https://freetsa.org/tsr'),
)->save('signed.pdf');

See the Digital Signatures page for the full API reference: signature formats (adbe.pkcs7.detached / ETSI.CAdES.detached), multiple signers, document timestamps, long-term validation (LTV / PAdES-B-LT / B-LTA), and visible signature appearance.

Limitations

  • Encrypted PDFs are rejected at open time.
  • Appearance generation (form-fill) uses Standard-14 /DR fonts only.
  • Form fields are not flattened after filling.
  • A source whose /AcroForm is a direct (inline) dictionary is rejected when signing - the /AcroForm must be an indirect reference.
  • The visible signature caption (when using SignatureAppearance) is drawn with Standard-14 Helvetica only.

See also

Clone this wiki locally