跳到內容

建立並預先填入 PDF 表單

AcroForm 是 PDF 內的互動式表單。這則 recipe(範例)會建立一份表單,並為各欄位預先填入初始值。核心的 HasFormFields trait 是表單編寫(authoring)API:你會在同一次呼叫中建立每個欄位,並提供它的值。文字欄位使用 default 引數,選項欄位使用 selected,核取方塊則使用 checked。開啟 PDF 時,各欄位都已經填好值,而且在符合規範的閱讀器中仍可編輯。任何開啟檔案的人仍然可以更改這些值。這則 recipe 取自 examples/30-form-fields.php

適用範圍界線。 核心在建立文件時會建立並填入表單欄位, 而不是事後另行處理。它不會讀取已存在於第三方 PDF 中的表單, 也不會把一份值對映表合併進去。在這裡,「填入」指的是帶著值編寫表單, 而不是載入並填寫一份外部 PDF。對外部表單做來回轉換(round-trip)屬於 Premium 與伺服器端的能力,而非核心的公開 API。

Terminal window
composer require nextpdf/core:^3

AcroForm 欄位會將目前值存放在欄位字典的 V 項目中(ISO 32000-2 §12.7)。它也可以在 DV 中存放一個選用的預設值;執行重設表單動作時,欄位就會回到這個值。NextPDF 會用你傳給每個欄位建構式的值來設定 V。文字的繪製會使用一個預設外觀字串(DA)。

這個設定檔之所以是 structural,是因為文件帶有一個 trailer /ID 陣列。後處理階段會在比較前先正規化那個易變的識別碼。

NextPDF\Core\Concerns\HasFormFields(混入 Document):

  • textField(string $name, float $x, float $y, float $w, float $h, string $default = '', array $options = []): static
  • checkBox(string $name, float $x, float $y, float $size, bool $checked = false): static
  • radioButton(string $name, float $x, float $y, float $size, string $value, string $group): static
  • comboBox(string $name, float $x, float $y, float $w, float $h, array $items, string $selected = ''): static
  • listBox(string $name, float $x, float $y, float $w, float $h, array $items, string $selected = ''): static
  • button(string $name, float $x, float $y, float $w, float $h, string $caption, string $action = ''): static

其中 defaultcheckedselected 引數會帶入預先填入的值。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Pre-filled Form');
$doc->addPage();
// Text field pre-filled with a value (sets the field dictionary /V entry).
$doc->textField(name: 'full_name', x: 20, y: 30, w: 90, h: 8, default: 'Ada Lovelace');
// Choice field pre-selected.
$doc->comboBox(
name: 'country',
x: 20, y: 45, w: 90, h: 8,
items: ['United Kingdom', 'Taiwan', 'Japan'],
selected: 'Taiwan',
);
// Checkbox pre-checked.
$doc->checkBox(name: 'newsletter', x: 20, y: 60, size: 5, checked: true);
$doc->save(__DIR__ . '/prefilled-form.pdf');
echo "Wrote prefilled-form.pdf\n";

以下完整範例對應 examples/30-form-fields.php,是一份含有數個區段的註冊表單。它會寫入 NEXTPDF_COOKBOOK_OUTPUT,供測試載具使用。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Customer Registration — Pre-filled');
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Customer Registration Form', newLine: true);
$doc->ln(4);
$leftMargin = 15.0;
$fieldX = 70.0;
$fieldW = 120.0;
$fieldH = 8.0;
$rowSpacing = 12.0;
// --- Personal information, pre-filled ---
$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,
options: ['maxLen' => 80],
);
$y += $rowSpacing;
}
// --- Choice field, pre-selected ---
$y += 6;
$doc->setXY($leftMargin, $y);
$doc->cell(50, $fieldH, 'Country:');
$doc->comboBox(
name: 'country',
x: $fieldX,
y: $y,
w: $fieldW,
h: $fieldH,
items: ['United States', 'United Kingdom', 'Germany', 'Japan', 'Taiwan'],
selected: 'United Kingdom',
);
// --- Checkboxes, pre-set ---
$y += $rowSpacing + 6;
$doc->setXY($leftMargin, $y);
$doc->cell(0, 7, 'Subscribe to newsletter');
$doc->checkBox(name: 'newsletter', x: $leftMargin + 70, y: $y, size: 5, checked: true);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/registration-prefilled.pdf');
echo "Wrote pre-filled registration form\n";

預期輸出:

Wrote pre-filled registration form

當你開啟 PDF 時,每個欄位都已經填好值,而且仍可編輯。

  • 欄位名稱必須唯一。 兩個共用同一名稱的欄位,在符合規範的閱讀器中會變成一個共享值的邏輯欄位。對連動欄位來說,這正是你要的結果,但在其他情況下會是意外。
  • 單選群組語意。 radioButton() 會把每個選項繫結到一個 group。被選取的選項,就是其 value 與群組值相符的那一個。同一時間每個群組只會有一個選項處於開啟狀態。
  • maxLen 只是一個提示。 maxLen 選項會在符合規範的閱讀器中限制輸入長度。它並不會限制你預先填入的儲存值。
  • API 中的座標以左上角為基準。 這個 trait 會替你轉換成 PDF 的左下角原點,所以請依範例所示傳入左上角座標。
  • 不支援填寫外部 PDF。 核心不提供任何方法來載入既有的第三方表單並套用值對映表。請參見上方的適用範圍界線。

表單編寫的效能會隨欄位數量線性成長。每個欄位會新增一個 widget 註解與一份外觀。數百個欄位仍遠在 1500 ms / 64 MB 的預算之內。

預先填入的值會原封不動地寫進欄位字典。在把任何來自不受信任來源的值放進你要散布的文件之前,必須先對它進行逸出或驗證。預先填入的表單並未受到保護:任何能開啟該 PDF 的人都可以讀取並更改其中的值。當表單內容屬於敏感資訊時,請把這則 recipe 與 使用權限加密搭配使用,並留意該處關於閱讀器需配合的告誡。

陳述規範條款參考 ID
欄位的目前值儲存在欄位字典的 V 項目中。ISO 32000-2§12.7
預設值儲存在 DV 項目中,並會在表單重設時還原。ISO 32000-2§12.7
欄位文字格式化使用預設外觀字串 DAISO 32000-2§12.7

NextPDF 會輸出所引用條款所描述的 AcroForm 結構。它並未聲稱完全符合 ISO 32000-2。