跳到內容

從語意內容輸出帶標記的 PDF/UA-2 結構樹

這則 recipe(範例)會產生一份以 ISO 14289-2(PDF/UA-2)為目標的帶標記 PDF。NextPDF 會輸出邏輯結構樹、標記內容序列、目錄語言設定,以及文件層級的識別中繼資料。這套結構支援無障礙撰寫,但符合性仍由獨立檢查工具判定。這則範例依循 examples/31-pdfua2-tagged.php

Terminal window
composer require nextpdf/core:^3

驗證步驟需要在 PATH 上找到 PDF/UA-2 檢查工具。本範例使用搭配 ua2 設定檔的 veraPDF。你不需要 Pro 或 Enterprise 套件,就能輸出帶標記的結構。

帶標記的 PDF 會在視覺內容串流之外,並行攜帶一棵 邏輯結構樹。輔助科技讀取的是這棵樹,而不是像素佈局,因此結構會決定對外揭露的閱讀順序。ISO 14289-2 在此定義了四項要求:真實(非 artifact)內容必須能透過這棵樹觸及(§8.2.2);結構元素必須依已定義的順序巢狀(§8.2.3);每個元素都必須 resolve(解析)到已知的結構命名空間,可以直接對應,也可以透過角色對映(§8.2.4);內容的自然語言必須在文件層級宣告,並在語言不同的結構元素上逐一細化(§8.4.4)。

NextPDF 以具型別的 ConformanceMode 建模這項行為。enableTaggedPdf() 會設定 ConformanceMode::PdfUa2,並且(a)讓 HTML 管線在 parser(剖析器)建構時接上 TaggedContentEmitter、(b)啟用代表帶標記 PDF 的目錄 MarkInfoMarked 旗標(ISO 32000-2 §14.7),以及(c)在目錄 Lang 項目記錄 BCP-47 語言。writer 也會輸出每頁的 Tabs 項目,讓 Tab 順序跟隨結構順序(ISO 32000-2 §14.8)。

嚴格的 UA-2 不變量只套用於 ConformanceMode::PdfUa2。在任何其他模式下建構嚴格的 ConformancePolicy,依設計都會擲出 InvalidConfigException

API 介面由 PHPDoc 產生。核心功能的進入點如下:

  • \NextPDF\Core\Document::createStandalone(): Document
  • Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static
  • Document::setLanguage(string $lang): static
  • \NextPDF\Conformance\ConformancePolicy::strictUa2(): self
  • \NextPDF\Conformance\ConformanceMode::PdfUa2(由 enableTaggedPdf() 設定的模式)
  • Document::beginTag(string $type): static / Document::endTag(): static(為非 HTML 內容手動加標記)
examples/31-pdfua2-tagged.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// Enable tagged mode BEFORE writeHtml(). The HTML pipeline detects the
// mode at parser construction time and wires the tagged-content emitter.
$doc->enableTaggedPdf(lang: 'en');
$doc->setTitle('Quarterly Accessibility Report');
$doc->setLanguage('en');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Quarterly Accessibility Report</h1>
<p>This document opts into tagged PDF so assistive technology can expose
a meaningful reading order.</p>
<ul>
<li>Headings carry semantic roles.</li>
<li>Lists keep their item structure.</li>
</ul>
HTML);
$doc->save(__DIR__ . '/output/31-pdfua2-tagged.pdf');
echo "Created: output/31-pdfua2-tagged.pdf\n";

這是一支自成一體、可由測試載具執行的程式。正式環境的呼叫端會在語言標籤格式不正確時快速失敗,而不是等到外部檢查工具執行時才發現。傳入 ConformancePolicy::strictUa2() 後,就能在 API 邊界拒絕無效的 BCP-47 標籤,再以檢查工具的判定把關建置。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Conformance\ConformancePolicy;
use NextPDF\Core\Document;
use NextPDF\Exception\InvalidConfigException;
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: (__DIR__ . '/accessible.pdf');
try {
$doc = Document::createStandalone();
// Strict UA-2: a malformed BCP 47 tag throws here, not silently at
// write time. strictUa2() also forces the §8.4.4 Lang validation.
$doc->enableTaggedPdf(lang: 'en-GB', policy: ConformancePolicy::strictUa2());
$doc->setTitle('Accessible Annual Report 2026');
$doc->setLanguage('en-GB');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Annual Report 2026</h1>
<p>Audited results for the financial year ending March 2026.</p>
<h2>Segment performance</h2>
<table>
<tr><th>Segment</th><th>Revenue</th></tr>
<tr><td>Cloud</td><td>42.1</td></tr>
<tr><td>Services</td><td>18.7</td></tr>
</table>
HTML);
$doc->save($out);
} catch (InvalidConfigException $e) {
fwrite(STDERR, "Tagged PDF/UA-2 setup rejected: {$e->getMessage()}\n");
exit(1);
}
// The gate is the checker, not the library.
$exitCode = 0;
$report = [];
exec('verapdf --flavour ua2 ' . escapeshellarg($out), $report, $exitCode);
if ($exitCode !== 0) {
fwrite(STDERR, "veraPDF FAILED — output is not PDF/UA-2 conforming\n");
fwrite(STDERR, implode("\n", $report) . "\n");
exit(1);
}
echo "veraPDF PASS — accessible.pdf carries a conforming UA-2 structure\n";

verapdf --flavour ua2 回報檔案符合的主機上,預期 STDOUT 為:

veraPDF PASS — accessible.pdf carries a conforming UA-2 structure

如果 enableTaggedPdf() 拒絕該語言標籤,程式會在 STDERR 印出 Tagged PDF/UA-2 setup rejected: … 後,以非零碼結束。如果檢查工具回報問題,程式會在印出 veraPDF FAILED — output is not PDF/UA-2 conforming 後,以非零碼結束。判定權在檢查工具:NextPDF 只輸出結構,不宣稱符合性。

  • 呼叫順序。 如果在 writeHtml() 之後才呼叫 enableTaggedPdf(),它不會追溯地為已寫入的內容加標記。請先啟用帶標記模式。
  • 嚴格語言把關。 沒有政策時,無法剖析的 BCP-47 標籤會被靜默丟棄,直到檢查工具執行時才浮現。加上 ConformancePolicy::strictUa2() 後,同一個標籤會在 enableTaggedPdf() 邊界擲出 InvalidConfigException(ISO 14289-2 §8.4.4 嚴格路徑)。
  • 可重複啟用。 呼叫 enableTaggedPdf() 兩次會更新語言,但不會重建已填入的結構樹。
  • 手動加標記。 對非 HTML 內容,請用 beginTag() / endTag() 將項目包起來。容器角色(TableTRLLI)會成為沒有標記內容的分組元素。葉節點角色(PH1H6TD)則會取得 MCID。
  • 模式互斥。 嚴格的 ConformancePolicy 只在 ConformanceMode::PdfUa2 下有效。將嚴格 UA-2 旗標與 PDF/A 模式組合在一起會擲出 InvalidConfigException。若要產生帶標記的 PDF/A 交付物,請分別啟用帶標記模式與 PDF/A 設定檔。

結構樹會額外加入一棵由輕量字典組成的並行樹,並為每段文字執行 BDC/EMC 運算子。以典型報告來說,這項額外開銷只佔輸出大小的幾個百分比,且穩定落在 2000 ms / 128 MB 預算之內。語意可重現性設定檔在這裡適用,因為以檢查工具為導向的交付物,是用結構抽象語法樹(AST)加上中繼資料來比對,而不是用原始位元組。請參閱「符合性」一節。

結構樹攜帶的文字與可見內容相同。如果來源 HTML 含有個人資料,那些資料也能透過這棵樹,以及透過 ActualText/Alt 屬性被觸及。請在撰寫前,套用與可見內容相同的遮蔽與最小化處理。加標記不會新增外洩管道,但依設計會讓文字可被程式擷取。

這則範例只會向 STDOUT 寫出一行固定的進度訊息。它會將 PDF 導向測試載具的旁路通道(NEXTPDF_COOKBOOK_OUTPUT),或導向呼叫端指定的路徑。文件文字絕不會寫入日誌。可能回傳內容片段的檢查工具輸出,請排除在共用日誌之外。

帶標記的 PDF 不是信任邊界。信任結構樹進行自動化處理的消費端仍必須驗證該檔案,因為惡意產生端可以輸出一棵結構格式正確、但具有誤導性的樹。請把這套結構視為無障礙輔助手段,而不是完整性或真實性的訊號。

這則範例不執行任何密碼學運算。FIPS 模式不會改變它的行為。不涉及任何簽章或加密。

PDF/UA-2 要求NextPDF 輸出的內容條款
真實內容位於結構樹中StructTreeRoot,搭配各區塊的 StructElem 與以 MCID 連結的標記內容ISO 14289-2 §8.2.2
已定義的巢狀關係與閱讀順序區塊元素依文件順序對映到 grouping/leaf 角色ISO 14289-2 §8.2.3
已知的結構命名空間角色位於 PDF 2.0 命名空間;HTML 標籤在需要時會做角色對映ISO 14289-2 §8.2.4
文件與元素的語言目錄 Lang 取自 BCP-47 標籤;每個元素的 Lang 會在語言不同時各自帶上ISO 14289-2 §8.4.4
非文字內容帶有文字替代Alt/ActualText 附在 figure/non-text 結構元素上ISO 14289-2 §8.5.1
表格關係Table/TR/TH/TD 角色,並帶表頭關聯ISO 14289-2 §8.2.5.26
部分識別中繼資料文件層級的識別資訊會排定於儲存時寫入ISO 14289-2 §引言(pdfua2#p17

PDF/UA-2 將無障礙要求疊加在 ISO 32000-2 的帶標記 PDF 機制之上。NextPDF 依賴的對映如下:

NextPDF 輸出ISO 32000-2 §14 機制條款
邏輯結構樹(StructTreeRoot帶標記 PDF 的邏輯結構§14.7 (iso32000_2_sec14#x1.x38.p13)
目錄 MarkInfo << /Marked true >>帶標記 PDF 的標記旗標§14.7 (iso32000_2_sec14#x1.x40.p3)
每頁的 Tabs 項目,依循結構順序結構導覽/Tab 順序§14.8 (iso32000_2_sec14#x1.x50)

PDF/UA-2 是 WCAG 2.2 以格式無關方式陳述的結構要求在 PDF 格式中的具體表達。相關對應如下:

WCAG 2.2 成功準則這則範例產生的 PDF/UA-2 機制
1.3.1 資訊與關係(Level A)結構樹讓標題、清單與表格關係能由程式判定(wcag_2_2#x2.x3.x3.x1.p3)。
1.3.2 有意義的順序(Level A)結構順序定義了不受視覺佈局影響的閱讀順序。
3.1.1 頁面語言(Level A)取自 BCP-47 標籤的目錄 Lang 項目。
1.1.1 非文字內容(Level A)Alt/ActualText 附在非文字結構元素上(ISO 14289-2 §8.5.1)。

這份對映說明輸出的結構在哪些位置支援各項 WCAG 2.2 準則。它不是 WCAG 符合性聲明。WCAG 符合性涵蓋整體使用者體驗,應由無障礙評估判定,而非由產生端決定。

陳述規範條款參考 ID
真實內容需要邏輯結構。ISO 14289-2§8.2.2
結構元素遵循已定義的巢狀與閱讀順序。ISO 14289-2§8.2.3
每個結構元素都會解析到一個已知的命名空間,可直接對應,也可透過角色對映。ISO 14289-2§8.2.4
自然語言會在文件層級與結構元素層級宣告。ISO 14289-2§8.4.4
非文字內容帶有文字替代。ISO 14289-2§8.5.1
表格儲存格帶有 row/header/資料關係。ISO 14289-2§8.2.5.26
帶標記 PDF 的標記由目錄 MarkInfoMarked 旗標表示。ISO 32000-2§14.7
符合性依該部分標準判定,而非由產生端宣稱。ISO 14289-2§8.14.2

NextPDF 會輸出支援無障礙撰寫的帶標記結構。**支援不等於符合。**這則範例並不宣稱 PDF/UA-2 符合性。這項判定由獨立檢查工具(例如 veraPDF)執行。在宣稱某個檔案符合之前,請先執行檢查工具。