Skip to content

Flatten form fields into static page content

Use this recipe to flatten an interactive AcroForm. NextPDF draws each field’s current value into the page content stream as ordinary graphics, then removes the AcroForm dictionary. The result is a non-interactive form: a static representation of the fields that looks the same everywhere, even in a reader without form support (ISO 32000-2 §12.7). The recipe builds the form from examples/30-form-fields.php, then calls flattenForms().

Terminal window
composer require nextpdf/core:^3

Flattening “prints” each widget onto its page. Text fields become BT … Tj … ET text. Checkboxes and radio buttons become drawn paths. Choice fields render their selected item. Push buttons render as a static button-like box. This matches the spec model for a statically defined appearance when the field contents are known in advance (ISO 32000-2 §12.7). Because NextPDF bakes the values into the page, the deprecated NeedAppearances mechanism is not needed.

The profile is structural. The document carries a trailer /ID, which the post-pass normalises before it compares two runs.

NextPDF\Core\Concerns\HasFormFields::flattenForms(): static flattens every field created on the document into static page content and drops the AcroForm. When no fields are present, it does nothing. Internally, it delegates the work to NextPDF\Form\FormFlattener.

Build the form with the field constructors. For detailed steps, see Build and pre-fill a PDF form. Then call flattenForms() before save().

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Flattened Form');
$doc->addPage();
$doc->textField(name: 'full_name', x: 20, y: 30, w: 90, h: 8, default: 'Ada Lovelace');
$doc->checkBox(name: 'agree', x: 20, y: 45, size: 5, checked: true);
// Bake the field values into the page; the AcroForm is removed.
$doc->flattenForms();
$doc->save(__DIR__ . '/flattened.pdf');
echo "Wrote flattened.pdf (no interactive fields)\n";

The full example below builds the multi-section form from examples/30-form-fields.php. It pre-fills the form, flattens it, and writes to NEXTPDF_COOKBOOK_OUTPUT for the harness.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Customer Registration — Flattened');
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Customer Registration (read-only copy)', newLine: true);
$doc->ln(4);
$leftMargin = 15.0;
$fieldX = 70.0;
$fieldW = 120.0;
$fieldH = 8.0;
$rowSpacing = 12.0;
$prefill = [
'full_name' => 'Ada Lovelace',
'email' => '[email protected]',
'phone' => '+44 20 7946 0000',
'company' => 'Analytical Engines Ltd',
];
$y = 40.0;
$doc->setFont('helvetica', '', 10);
foreach ($prefill as $name => $value) {
$doc->setXY($leftMargin, $y);
$doc->cell(50, $fieldH, ucwords(str_replace('_', ' ', $name)) . ':');
$doc->textField(name: $name, x: $fieldX, y: $y, w: $fieldW, h: $fieldH, default: $value);
$y += $rowSpacing;
}
$y += 6;
$doc->setXY($leftMargin, $y);
$doc->cell(0, 7, 'Newsletter');
$doc->checkBox(name: 'newsletter', x: $leftMargin + 70, y: $y, size: 5, checked: true);
// Flatten: widgets become static page content; the AcroForm is dropped.
$doc->flattenForms();
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/registration-flattened.pdf');
echo "Wrote flattened registration form\n";

Expected output:

Wrote flattened registration form

The output shows the same values, with no interactive fields. A reader without form support renders it the same way.

  • Flattening is irreversible. Once you call save(), the interactive fields are gone. Keep the un-flattened source if you might need to edit the values later.
  • Call order. Run flattenForms() after you create the fields and before save(). Calling it with no fields does nothing, which is safe.
  • Signature fields are not flattened. A /Sig field’s visual surface is the appearance produced from its Cryptographic Message Syntax (CMS) SignedData, not a re-renderable value. Re-rasterising it would create a static “ghost” graphic that no longer matches any verifiable signature. For that reason, the flattener skips signature fields on purpose. Flatten the form before you sign, never after.
  • Checkbox truthiness. A checkbox renders its checkmark when its value is Yes/On/1/true. An empty or Off value renders the box only.
  • Font for flattened text. Flattened text uses the current font. With no font set, it falls back to Helvetica. For Chinese, Japanese, and Korean (CJK) or custom-font field values, set the font you want before flattenForms().

Flattening scales linearly with the field count. For each field, NextPDF appends a bounded content block, then removes the AcroForm object. Typical forms stay well inside the 1500 ms / 64 MB budget.

Flattening makes field values non-editable in normal readers. This is a presentation change, not access control. The values stay visible in the page content, and any text tool can extract them. Do not treat flattening as redaction or as protection for sensitive values. For confidentiality, use Encrypt with permissions. Read the reader-cooperative caveat there too, because permission bits do not enforce read restrictions either. Never flatten a signed document. Flatten first, then sign.

StatementSpecClausereference_id
A flattened form is a non-interactive (static) representation of the fields.ISO 32000-2§12.7
Field appearance is statically defined when its contents are known in advance.ISO 32000-2§12.7
Baked-in appearances make the deprecated NeedAppearances flag unnecessary.ISO 32000-2§12.7

NextPDF produces the static structure described by the cited clauses. It does not claim blanket ISO 32000-2 conformance.