進階表單處理¶
PHP Compatibility
This example uses PHP 8.5 syntax. If your environment runs PHP 8.1 or 7.4, use NextPDF Backport for a backward-compatible build.
PDF 表單生態系包含三種主要格式:AcroForm(PDF 1.2 引入,最普遍)、XFA(XML Forms Architecture,Adobe 專有,靜態與動態兩種)、XFDF(XML Forms Data Format,XFA 的資料交換格式)。NextPDF Pro 提供對三種格式的完整讀寫支援。
格式支援矩陣¶
| 功能 | AcroForm | XFA 靜態 | XFA 動態 | XFDF |
|---|---|---|---|---|
| 欄位讀取 | ✓ | ✓ | ✓ | ✓ |
| 欄位填充 | ✓ | ✓ | 部分 | ✓ |
| 資料匯出 | ✓ | ✓ | ✓ | ✓ |
| 批次填充 | ✓ | ✓ | ✓ | ✓ |
| 欄位驗證規則 | ✓ | ✓ | ✓ | N/A |
| 計算欄位 | ✓ | ✓ | ✓ | N/A |
| 數位簽章欄位 | ✓ | ✓ | 部分 | N/A |
XFA 動態表單的部分功能(如動態新增重複列)需要 JavaScript 執行環境,Pro 套件提供靜態解析,不執行動態邏輯。
FormDataBinder — 表單資料綁定¶
FormDataBinder 負責將外部資料來源(陣列、PHP 物件、JSON)注入 PDF 表單欄位:
基礎用法¶
use NextPDF\Pro\Forms\FormDataBinder;
use NextPDF\Pro\Forms\FormFillResult;
$templatePdf = file_get_contents('/templates/employee-onboarding.pdf');
$binder = new FormDataBinder();
/** @var FormFillResult $result */
$result = $binder->fill(
pdfBytes: $templatePdf,
data: [
'employee_name' => '王大明',
'employee_id' => 'EMP-001234',
'department' => '工程部',
'start_date' => '2026-03-15',
'contract_type' => 'full_time', // 對應單選按鈕值
'benefits' => ['health', 'dental', 'vision'], // 多選核取方塊
'signature_date' => date('Y-m-d'),
],
flatten: false, // true = 鎖定欄位為不可編輯;false = 保留可編輯性
);
file_put_contents('/output/onboarding-filled.pdf', $result->getPdf());
// 取得填充報告
$report = $result->getReport();
echo 'Fields filled: ' . $report->getFilledCount();
echo 'Fields not found: ' . implode(', ', $report->getUnmatchedKeys());
echo 'Fields skipped (read-only): ' . $report->getSkippedCount();
強型別資料物件¶
use NextPDF\Pro\Forms\FormDataBinder;
use NextPDF\Pro\Forms\Attribute\FormField;
// 使用 PHP 8.5 readonly + 屬性標注,實現強型別表單資料物件
final readonly class EmployeeFormData
{
public function __construct(
#[FormField('employee_name')]
public string $name,
#[FormField('employee_id')]
public string $employeeId,
#[FormField('department')]
public string $department,
#[FormField('start_date', format: 'Y-m-d')]
public \DateTimeImmutable $startDate,
/** @var list<string> */
#[FormField('benefits', type: 'checkbox_group')]
public array $benefits,
) {}
}
$data = new EmployeeFormData(
name: '王大明',
employeeId: 'EMP-001234',
department: '工程部',
startDate: new \DateTimeImmutable('2026-03-15'),
benefits: ['health', 'dental'],
);
$binder = new FormDataBinder();
$result = $binder->fillFromObject($templatePdf, $data);
XFA 解析器¶
use NextPDF\Pro\Forms\XFA\XfaParser;
use NextPDF\Pro\Forms\XFA\XfaDocument;
use NextPDF\Pro\Forms\XFA\XfaField;
$pdf = file_get_contents('/forms/xfa-invoice.pdf');
$parser = new XfaParser();
/** @var XfaDocument $xfaDoc */
$xfaDoc = $parser->parse($pdf);
echo 'XFA Type: ' . $xfaDoc->getType()->value; // 'static' | 'dynamic'
echo 'XFA Version: ' . $xfaDoc->getVersion();
// 遍歷所有欄位
foreach ($xfaDoc->getFields() as $field) {
/** @var XfaField $field */
printf(
"Field: %-30s | Type: %-15s | Value: %s\n",
$field->getName(),
$field->getFieldType()->value,
$field->getValue() ?? '(empty)',
);
}
// 填充 XFA 表單資料
$filledPdf = $xfaDoc->fill([
'invoice.header.invoiceNumber' => 'INV-2026-001',
'invoice.header.date' => '2026-03-15',
'invoice.customer.name' => 'Acme Corporation',
'invoice.items.0.description' => 'PDF Processing License',
'invoice.items.0.quantity' => 1,
'invoice.items.0.unitPrice' => 9900,
])->toPdf();
XFDF 格式支援¶
XFDF(XML Forms Data Format)是 ISO 19444-1 定義的表單資料交換格式,常用於與其他系統的資料交換:
use NextPDF\Pro\Forms\XFDF\XfdfParser;
use NextPDF\Pro\Forms\XFDF\XfdfBuilder;
// 解析 XFDF 資料並注入 PDF
$xfdfContent = file_get_contents('/data/form-submission.xfdf');
$parser = new XfdfParser();
$formData = $parser->parse($xfdfContent);
$binder = new FormDataBinder();
$result = $binder->fillFromXfdf($templatePdf, $formData);
// 從已填充的 PDF 匯出 XFDF 資料
$extractor = new FormDataExtractor();
$extractedData = $extractor->extract($result->getPdf());
$builder = new XfdfBuilder();
$xfdfOutput = $builder->build(
formData: $extractedData,
pdfPath: 'https://forms.example.com/templates/onboarding.pdf',
);
file_put_contents('/exports/submission.xfdf', $xfdfOutput);
FormDataExtractor — 表單資料萃取¶
use NextPDF\Pro\Forms\FormDataExtractor;
use NextPDF\Pro\Forms\ExtractedFormData;
use NextPDF\Pro\Forms\FormFieldValue;
$filledPdf = file_get_contents('/submissions/completed-form.pdf');
$extractor = new FormDataExtractor();
/** @var ExtractedFormData $formData */
$formData = $extractor->extract($filledPdf);
// 取得所有欄位值
foreach ($formData->getFields() as $field) {
/** @var FormFieldValue $field */
printf(
"%-30s = %s\n",
$field->getName(),
$field->getDisplayValue(),
);
}
// 轉換為陣列(適合存入資料庫)
$dataArray = $formData->toArray();
// 轉換為 JSON(適合 API 回應)
$json = $formData->toJson(pretty: true);
// 轉換為 CSV(適合批次匯入)
$formData->toCsv(path: '/exports/form-data.csv');
批次表單填充¶
use NextPDF\Pro\Forms\FormDataBinder;
use NextPDF\Pro\Forms\BatchFormFiller;
use NextPDF\Pro\Forms\BatchFillResult;
$template = file_get_contents('/templates/certificate.pdf');
$records = [
['recipient_name' => '王大明', 'course' => 'PDF Engineering', 'date' => '2026-03-01', 'score' => '92'],
['recipient_name' => '李小華', 'course' => 'PDF Engineering', 'date' => '2026-03-01', 'score' => '88'],
['recipient_name' => '張美玲', 'course' => 'PDF Engineering', 'date' => '2026-03-01', 'score' => '95'],
];
$filler = new BatchFormFiller(
binder: new FormDataBinder(),
template: $template,
);
/** @var BatchFillResult $result */
$result = $filler->fill(
records: $records,
outputDirectory: '/output/certificates/',
filenameTemplate: 'certificate-{recipient_name}.pdf',
flatten: true, // 填充後鎖定
);
printf(
"Filled: %d / %d | Failed: %d\n",
$result->getSuccessCount(),
count($records),
$result->getFailureCount(),
);
欄位結構探索¶
在填充前,可先探索 PDF 表單的欄位結構:
use NextPDF\Pro\Forms\FormInspector;
use NextPDF\Pro\Forms\FormField;
$inspector = new FormInspector();
$fields = $inspector->inspect($templatePdf);
foreach ($fields as $field) {
/** @var FormField $field */
printf(
"%-30s | %-15s | Required: %s | Default: %s\n",
$field->getName(),
$field->getType()->value, // text/checkbox/radio/dropdown/signature/date
$field->isRequired() ? 'Yes' : 'No',
$field->getDefaultValue() ?? '(none)',
);
// 下拉選單的選項清單
if ($field->hasOptions()) {
foreach ($field->getOptions() as $option) {
printf(" Option: %s => %s\n", $option->getValue(), $option->getDisplayText());
}
}
}
Bates 編號¶
Bates 編號是法律文件管理的標準做法,在每頁特定位置印上唯一序號:
use NextPDF\Pro\Bates\BatesNumberer;
use NextPDF\Pro\Bates\BatesOptions;
use NextPDF\Pro\Bates\BatesPosition;
$batesNumberer = new BatesNumberer(
BatesOptions::create(
prefix: 'ACME-LEGAL-',
startNumber: 1,
padLength: 6, // ACME-LEGAL-000001
position: BatesPosition::BottomRight,
fontSize: 8.0,
fontColor: '#374151',
margin: 10.0,
)
);
$documents = [
file_get_contents('/docs/contract.pdf'),
file_get_contents('/docs/exhibits.pdf'),
file_get_contents('/docs/correspondence.pdf'),
];
$numberedDocs = $batesNumberer->numberBatch($documents);
// 三份文件的頁碼連續:ACME-LEGAL-000001 起始,跨文件累計
foreach ($numberedDocs as $index => $doc) {
file_put_contents(sprintf('/output/bates-%02d.pdf', $index + 1), $doc);
}
相關資源¶
- 文字萃取 — 從已填充表單萃取資料
- WCAG 表單無障礙 — 確保表單符合無障礙標準
- 案例研究:政府表單數位化
- FormDataBinder API 參考
- FormDataExtractor API 參考
- XfaParser API 參考