跳到內容
getnextpdf.com

PDF 對自己知道些什麼:中介資料與 XMP 封包

Spec: ISO 16684-1:2019Spec: ISO 32000-2

開啟一份現代的 PDF,它可能會把自己描述兩遍。有一份小巧、古老、稱為文件資訊字典的鍵/值清單,而且可能還有一塊 XML——XMP 封包——以更豐富、更具結構的形式說著大致相同的事。本頁談的就是這兩套平行的系統、為何兩者都存續至今,以及為何封存與搜尋管線堅持它們必須一致。

對於標題的簡短回答:一份 PDF 知道自己的標題、作者、主題、關鍵字、製作它的工具,以及製作的時間。它把這份知識同時存放在兩處,而有意思的工程在於讓它們保持誠實。

中介資料是文件中人鮮少看見、而機器幾乎總是讀取的部分。你作業系統的檔案預覽、一套數位資產管理系統、一份圖書館目錄、一個搜尋索引、一個法律證據揭示工具——它們之中有許多會在讀取文件文字之前、或與之並行地讀取中介資料,並信任它所陳述的內容。

因此,當這兩套系統各說各話時——資訊字典說是某位作者、XMP 封包卻說是另一位——下游某處便會挑出一個,而你無法選擇是哪一個。搜尋索引可能浮現錯誤的標題。封存驗證器可能直接拒絕該檔案。這種偏移在被某條管線絆倒之前都是隱形的,而那正是發現它最糟糕的時機。

  • PDF 能以兩套平行的系統攜帶中介資料:舊有的文件資訊字典,以及由 RDF/XML 構成的現代 XMP 封包
  • XMP 封包被包裹在一個 xpacket 處理指令中——一個 begin 標頭與一個 end 尾段——讓工具能夠找到它,甚至無須重新解析整個檔案就能就地改寫它。
  • 屬性住在命名空間裡:dc(Dublin Core)負責標題與建立者,xmp 負責建立與修改日期,pdf 負責製作者,xmpMM 負責文件識別與歷程。
  • PDF/A 要求 XMP 中介資料,而具有 XMP 對應項的 DocInfo 項目必須與之相符——彼此不一致即為一次驗證失敗。
  • NextPDF 把這兩者視為一件工作:它同時寫入兩者、把 XMP 讀回,並為封包的大小把關,寧可失敗封閉也不輸出一個格式錯誤的檔案。

誠實的描述是:這是一個被裝扮成格式問題的同步問題。文件資訊字典(Spec: ISO 32000-2, §14.3.3)由尾段透過 /Info 項目參照,是一份扁平的字串清單:Title、Author、Subject、Keywords、Creator、Producer,外加幾個日期。它很簡單,早於 XML,而且因為太多工具仍會率先讀它,它至今仍幾乎搭乘在每一個檔案之中。

XMP 封包(Spec: ISO 16684-1:2019, §7)是現代的那一半。它是一份 XML 文件——精確地說是 RDF/XML——把檔案建模成一組分群進綱要的具名屬性,而每個綱要都繫結到一個命名空間(Spec: ISO 16684-1:2019, §6)。那種結構正是扁平字典所做不到的:一個值可以是有序清單、可以是帶語言標記的替代值(「英文標題、法文標題」),也可以是一筆結構化記錄。在 PDF 中,那段 XML 住在一個附加於文件目錄的中介資料串流裡(Spec: ISO 32000-2, §14.3.2),那正是當代讀取器查看的標準位置。

有三個細節值得剖析,因為工具正是在這些地方把封包弄錯。

那個包裹。 XMP 封包被一個處理指令所框住,如此一來,在封包以可直接搜尋之位元組形式儲存的那些格式中,程式無須完整解析就能定位中介資料;而在 PDF 中,目錄的中介資料串流才是標準的定位器。begin 標頭攜帶一個宣告文字編碼的位元組順序標記;end 尾段攜帶一個旗標,陳述封包是唯讀的、還是可以就地編輯。當它可寫入時,序列化器會在 XML 之後留下一段空白填補,好讓編輯器能稍微增長內容、而不必位移其後的每一個位元組。那段填補不是裝飾——它正是讓就地中介資料編輯得以成立的東西。

那些命名空間。 一個屬性唯有對照其命名空間才有意義,而其中少數幾個近乎通用。Dublin Core(dc)保有標題與建立者。XMP 基本綱要(xmp)保有建立與修改日期,以及撰寫工具。PDF 綱要(pdf)保有製作者字串與關鍵字。媒體管理綱要(xmpMM)保有文件的識別與其衍生歷程——那條說著「這個檔案源自那一個」的軌跡。就這些命名空間 URI 與屬性名稱達成共識——通常以慣用前綴呈現——正是這些核心綱要的全部用意所在(Spec: ISO 16684-1:2019, Annex B);兩個都會說 Dublin Core 標題屬性的工具,無須事先約定就能互通。

那條一致性規則。 因為兩套系統都能命名同一個屬性,它們便可能各說各話。封存設定檔拒絕允許這種情況。一份 PDF/A 檔案必須攜帶一個 XMP 中介資料串流,而任何具有 XMP 對應項的文件資訊項目都必須與之相符;彼此不一致即為一次驗證失敗。實務上的結論很直接:在一條封存管線中,這兩者並非各自獨立的欄位,而是被寫了兩遍的同一件事實,它們必須步調一致。

NextPDF 把這三者當成單一一個關注點來處理。中介資料的流程是一條路徑,而非兩條互相競爭的路徑。

  1. Collect the values onceTitle, author, subject, keywords, creator, producer and dates are set on the document a single time, so there is one source of truth, not two.
  2. Write the Info dictionaryThe DocInfo writer emits the legacy Title, Author, Subject, Keywords, Creator, Producer and date entries that older tools read first.
  3. Build the XMP packetThe XMP builder serializes the same values as RDF/XML under dc, xmp, pdf and xmpMM, wrapped in the xpacket header and trailer with writable padding.
  4. Guard the packet sizeBefore serialization is accepted, the engine checks the packet against a size bound and fails closed with a typed exception rather than emit a malformed or oversized stream.
  5. Read XMP back to verifyThe XMP reader parses the packet so a pipeline can confirm the engine wrote the metadata it was given — the kind of check an archival validator builds on.
How NextPDF keeps a document's two metadata systems consistent: one set of values is written to both the Info dictionary and the XMP packet, the packet is size-guarded before it is serialized, and the same packet can be read back so a pipeline can confirm the metadata serialized as intended.

下方的形態把中介資料設定一次,並讓引擎將其散播到兩套系統,接著把 XMP 標題讀回來,好讓管線能加以檢視。那道大小防護,正是把「大概沒問題」轉成「如果不行就可被證明地拒絕」的那一行。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Metadata\Exception\XmpPacketTooLargeException;
$document = Document::createStandalone();
// Set the values ONCE. The engine writes them to both the Info dictionary
// and the XMP packet, so the two systems cannot drift apart at the source.
$document->setTitle('Annual Report 2026');
$document->setAuthor('Records Office');
$document->setSubject('Statutory annual filing');
$document->setKeywords('annual report, statutory, 2026');
try {
// Building the document serializes the XMP packet. The engine guards the
// packet size and fails closed: a packet that exceeds the bound is a
// typed exception, never a silently truncated or malformed stream.
$bytes = $document->getPdfData();
} catch (XmpPacketTooLargeException $e) {
// Decide deliberately: trim the metadata, not the guarantees.
error_log('XMP packet exceeded the size bound: ' . $e->getMessage());
throw $e;
}
// Read the packet back. This confirms the XMP the engine serialized carries the
// value you set — the kind of integrity check an archival pipeline runs before
// it trusts the file. (Both systems are written from one source, so they agree
// by construction; reading the XMP back proves it serialized as intended.)
$xmp = $document->readXmpMetadata($bytes);
$xmpTitle = $xmp->get('dc', 'title'); // 'Annual Report 2026'
if ($xmpTitle !== $document->getTitle()) {
throw new \RuntimeException('XMP title does not match the value that was set.');
}

這裡沒有任何路徑會讓兩套系統悄悄分歧:值只進入一次,兩個寫入器消費的是同一個來源,而讀取器讓管線得以檢查結果。

常見的信念是,資訊字典已經過時,你可以忽略它。你不能——至少現在還不能。大量已安裝的軟體,包括某些作業系統的檔案預覽與較舊的封存工具,仍然率先、甚至只讀取資訊字典。丟掉它並不會讓檔案變得現代化;它只會讓那份檔案在任何尚未採用 XMP 的東西眼中看起來像是無標題。

與之鏡像相反的錯誤,是把這兩者當成你各自分別填寫的獨立欄位。那正是它們發生偏移的方式。它們是同一件事實的兩種序列化。要嘛從同一個來源寫入它們,要嘛接受下游某處將挑出你並不打算採用的那個版本。

  • NextPDF 同時寫入兩套系統並把 XMP 讀回。 它的範圍是 XMP 中介資料建構器、DocInfo 寫入器,以及一個 XMP 讀取器。它並不承諾要成為一個通用的 RDF/XML 查詢引擎。
  • XMP 封包受大小防護並失敗封閉。 一個超出引擎界限的封包會引發一個型別化例外。引擎不會為了讓一個過大的請求「塞得下」,而輸出一個被截斷、填補超出界限、或以其他方式格式錯誤的封包。
  • 一致性是在寫入時、由單一來源強制達成的;它不是對你並未產生之檔案的某種魔法調和。 如果你匯入一份其兩套系統早已各說各話的文件,要怎麼解決那是你的決定,而非一次隱含的改寫。
  • PDF/A 的中介資料要求是封存設定檔的一部分。 本頁解釋規則;標準符合性的裁決則一如封存工作的慣例,歸屬於驗證器。關於那條邊界,參見封存頁面。

中介資料建構器、DocInfo 寫入器,以及 XMP 讀取器都是核心能力。那句誠實的但書,隨那道大小防護一同陳述於下。

Metadata: Info dictionary and XMP packet — edition availability
EditionAvailability
Core

The XMP metadata builder, the Document Information dictionary writer, and the XMP reader ship in Core, with the size guard that fails closed on an oversized packet — including the PDF/A conformance output and validation that make the XMP metadata stream mandatory and require DocInfo to match it.

Pro

Adds batch and templated metadata workflows over the same Core writer and reader.

Enterprise

Adds the broader conformance policy and reporting surface across a document estate.

  • 封存與 PDF/A — XMP 封包在何處不再可有可無,而 DocInfo 與 XMP 的一致性規則在何處變成一項過或不過的要求。
  • 可以交給稽核員的法規遵循 — 為何能往返且自洽的中介資料,是一份可稽核產物的一部分。
  • PDF 檔案剖析 — 尾段、目錄與中介資料串流在檔案結構中坐落於何處。
  • 串流與篩選器 — 中介資料串流就是一個串流;這就是 PDF 串流被編碼並保持確定性的方式。
  • 文件資訊字典 — 由尾段透過 /Info 項目參照的舊有、扁平的鍵/值中介資料:Title、Author、Subject、Keywords、Creator、Producer,以及日期。早於 XMP;至今仍被廣泛讀取。
  • XMP — 可擴充中介資料平台(Extensible Metadata Platform),一種以具名屬性(分群進綱要)描述資源的 XML(RDF/XML)格式。標準化為 ISO 16684-1。
  • xpacket — 包裹一個 XMP 封包的處理指令,帶有一個 begin 標頭(編碼標記)與一個 end 尾段(唯讀或可寫入旗標),讓工具無須完整解析就能定位並編輯封包。
  • 命名空間/綱要 — 一套繫結到某個 URI 的具名屬性詞彙。常見前綴:dc(Dublin Core)、xmp(XMP 基本)、pdf(PDF 專屬)、xmpMM(媒體管理/文件識別)。
  • 中介資料串流 — 附加於文件目錄、攜帶 XMP 封包的那個 PDF 物件。現代讀取器最先查看的標準位置。
  • PDF/A — 封存 PDF 的設定檔家族(ISO 19005)。它要求一個 XMP 中介資料串流,並要求任何具有 XMP 對應項的文件資訊項目都須與之相符,否則驗證失敗。