ページ記述を取り除くと、誰も印刷しないものの、すべてのリーダー、インデクサー、およびアーカイブシステムが依存する構造の薄い層が残ります。ページオブジェクトは、自身が属する章、それを書いた著者、または別の場所へのリンクである脚注については何も知りません。その知識は、ドキュメントカタログに添付された 3 つの構造(メタデータストリーム、アウトラインツリー、およびページごとの注釈配列)という 1 つ上のレベルに存在します。これらは、間違いを犯しやすいという特徴を共有しています。いずれもページ上に目に見えるマークを持たないため、ファイルが完全にレンダリングされていても、ブックマークが欠落していたり、独自の著者フィールドと矛盾していたり、もはや存在しないページオブジェクトをリンクが指し示していたりする可能性があります
これは、PDF ライブラリがドキュメントプロパティ、ブックマーク API、およびリンクまたは注釈呼び出しとして公開するレイヤーであり、検索クローラーがドキュメントの内容を判断するために読み取るレイヤーです。その下にあるオブジェクトモデルについては、PDF ドキュメント構造のウォークスルーで説明されています。ここでは、カタログにぶら下がっているものに焦点を当てます
3 つの構造はすべてカタログに添付されています。それらを接続する完全なカタログは次のようになります
1 0 obj
<< /Type /Catalog
/Pages 2 0 R
/Outlines 3 0 R
/Names << /EmbeddedFiles 4 0 R >>
/Metadata 5 0 R
>>
endobj
4 つのエントリ、4 つの独立したサブシステムです。/Pages は表示されるドキュメント、/Outlines はブックマークツリー、/Metadata は XMP ストリームを指し、/Names はドキュメント全体の中の名前辞書(とりわけ、埋め込まれた添付ファイルを保持します)に到達します。それぞれはオプションであり、それらのいずれも見つからないリーダーでもページは表示されます。このオプション性こそが、ページしか理解できないツールによってファイルが編集されたときにナビゲーションレイヤーが最初に腐敗する理由です
矛盾する 2 つのメタデータストア
PDF はドキュメントのメタデータを一度に 2 つの場所に保持しており、これらが異なることを言うと問題が始まります。元のメカニズムはドキュメント情報辞書(トレーラーの /Info によって参照される)であり、/Title、/Author、/Subject、/Keywords、/Creator、/Producer、および 2 つの日付のためのフラットなキーと値のペアのセットです。これはシンプルで、すべてのビューアが読み取ります。PDF 2.0 では、2 番目のメカニズムである XMP メタデータストリームを優先して、そのほとんどが非推奨になっています
XMP は RDF で記述された自己完結型の XML ドキュメントであり、カタログが /Metadata を通じて到達し、/Type /Metadata /Subtype /XML とマークされたストリームとして保存されます。PDF オブジェクト構造の内部に埋もれている Info 辞書とは異なり、XMP パケットは、PDF について何も知らないツールによって単独で抽出およびパースされるように設計されています。典型的なパケットは次のとおりです
5 0 obj
<< /Type /Metadata /Subtype /XML /Length 1235 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
<dc:title><rdf:Alt><rdf:li xml:lang="x-default">Quarterly Report</rdf:li></rdf:Alt></dc:title>
<dc:creator><rdf:Seq><rdf:li>A. Author</rdf:li></rdf:Seq></dc:creator>
<xmp:CreateDate>2026-06-16T10:46:27+08:00</xmp:CreateDate>
<xmp:CreatorTool>Reporting Service 4.2</xmp:CreatorTool>
<pdf:Producer>losLab PDF Library</pdf:Producer>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj
そのブロック内の 3 つの詳細が、メタデータが実際のツールとの接触を生き延びるかどうかを決定します。xpacket 処理命令は飾りではありません。これらはパケットをフレーム化し、エクストラクターがより大きなバイトストリーム内でそれを見つけられるようにします。閉じる <?xpacket end="w"?> を省略するライターは、開くことはできますが、厳密なバリデーターにつまずくファイルを生成します。プロパティのデータ型も重要です。dc:title は rdf:Alt でラップされた言語の代替ですが、dc:creator は順序付けられたリストであり rdf:Seq を受け取ります。どちらかを単なるテキストノードとして出力することは、最も一般的な XMP の間違いであり、それを受け入れないビューアに当たるまで、ほとんどのビューアで許容されます。名前空間のプレフィックスは慣例的ですが、それらがバインドする URI は規範的です。パーサーはプレフィックスではなく URI をキーにします
2 つのストアがある場合の厳格なルールは、それらが一致しなければならないということです。/Info が著者をある人とし、dc:creator が別の人を指名している場合、同じ質問に 2 つの異なる方法で答えるドキュメントを出荷したことになり、消費するツールがどのフィールドを読み取るかによってどちらの回答が勝つかが決まります。通常、ライブラリは両方を書き込みますが、手作業でどちらか一方を編集したり、異なるジェネレーターからのファイルをマージしたりした瞬間、2 つは離れていきます。Info 辞書はレガシー互換性として、XMP は信頼できる情報源として扱い、個別にパッチを当てるのではなく、1 つの値のセットから両方を再生成してください。PDF/A ではこれが準拠要件になります。ISO 19005 は XMP を義務付け、対応する XMP と矛盾する Info プロパティを禁じています
ブックマークパネルの背後にあるアウトラインツリー
ビューアがブックマークパネルとして表示するものは、ファイル内ではドキュメントアウトラインと呼ばれる辞書の二重リンクリスト(ツリー)です。カタログは /Outlines を通じてルートアウトライン辞書を指し、ルートはその最初と最後のトップレベルアイテムを指し、すべてのアイテムはその隣接アイテムと親にスレッド化されています。ブックマークの配列はどこにもありません。構造全体は参照をたどることで再構築されます。これが、単一の壊れたリンクによって、エラーなしにパネルからブランチ全体が消滅する可能性がある理由です
8 0 obj % アウトラインのルート
<< /Type /Outlines /Count 4 /First 9 0 R /Last 9 0 R >>
endobj
9 0 obj % トップレベル:章
<< /Title (Chapter 1: Results)
/Parent 8 0 R /Count 2
/First 12 0 R /Last 15 0 R >>
endobj
12 0 obj % 最初の子
<< /Title (Introduction)
/Parent 9 0 R /Next 15 0 R
/Dest [3 0 R /XYZ 72 720 0] >>
endobj
15 0 obj % 2番目の子、最後の兄弟
<< /Title (Methodology)
/Parent 9 0 R /Prev 12 0 R
/Dest [3 0 R /Fit] >>
endobj
リンクを読めば、不変性が明らかになります。すべてのアイテムは /Parent を指し示します。兄弟は /Prev と /Next を通じてチェーンを形成し、最初のアイテムは /Prev を省略し、最後のアイテムは /Next を省略します。親は /First と /Last を通じて最初と最後の子を指定し、その間にある子は兄弟チェーンを歩くことによってのみ到達可能です。1 つでも間違えると失敗は静かです。古い /Next は章を切り捨て、/Last がチェーンを終了しない親はアイテムを孤児のままにし、ビューアは到達できるものは何でもレンダリングします
/Count フィールドは、人々を驚かせる状態の断片を保持しています。ルートおよび展開されたアイテムでは、現在表示されている子孫の数を保持します。折りたたまれたアイテムでは、展開時に表示される子孫の数がどれくらいかを示す負の数になります。したがって、/Count はツリーに関する固定された構造的事実ではなく、パネルの保存された開閉状態であり、これを正の合計としてハードコーディングするジェネレーターは、作成者が閉じたままにしようとしたすべてのブランチを再び開いてしまいます
各アイテムは、どこかを指し示すことによってその場所を獲得します。/Title はパネルが表示するものであり、/Dest はクリックが到達する場所です。宛先は、上記のようにアイテム内にインラインで記述することも、ドキュメントの名前辞書を介して解決される名前にすることもできます。多くのブックマークとリンクが同じ場所をターゲットにしている場合、移動したターゲットを 1 か所で修正できるため、後者の方が適しています。ライブラリは通常、アウトラインルートハンドルと子エントリを追加するメソッドの背後にこのツリーを隠します。HotPDF では、ドキュメントは THPDFDocOutlineObject 型の OutlineRoot を公開し、アイテムを追加するときに /Prev、/Next、/Parent、および /Count リンクを自動的にスレッド化します。これは利用する価値があります。編集全体でこれらの不変条件を手作業で維持することは、アウトラインが壊れる原因になるからです
宛先:クリックの行き先の文法
ブックマークとリンク注釈はどちらも宛先を指し示し、宛先はページ番号以上のものです。それはページオブジェクトを名前付けし、2 番目のスロットの動詞を通じて、ビューアがそれをどのようにフレーム化するかを指定する配列です。最も一般的で最も乱用されるのは /XYZ で、[page /XYZ left top zoom] の形式です。その 3 つのオペランドは独立しており、いずれも「リーダーが持っていた通りにしておく」ことを意味する null にすることができます。したがって、[page /XYZ null null null] は、スクロール位置やズームに触れることなくページにジャンプし、通常は「ページに移動」リンクに求められるものです。数値はデフォルトのユーザー空間にあり、左下から測定され、y は上に向かって増加します。これはページコンテンツが使用するのと同じ座標系です。画面レイアウトから来た著者は、反射的に上から測定し、リーダーをページの間違った端に送ってしまいます
/Fit ファミリは、正確な配置を弾力性と引き換えにします。[page /Fit] はページ全体をウィンドウにスケーリングし、[page /FitH top] は指定された上端でページの幅に合わせ、[page /FitR l b r t] は長方形がビューを満たすようにズームします。これらは固定された座標ではなくページのジオメトリからスケールを計算するため、/Fit の宛先はページのサイズが変更された後でも意味のある動作をしますが、ズームが組み込まれた /XYZ 宛先は、リーダーが余白を凝視する状態のままになる可能性があります。目次の場合、セクションの上部座標を持つ /FitH は、推測されたズームを持つ /XYZ よりも古くなりにくいです
注釈:ページコンテンツではないすべてのインタラクティブなもの
注釈とは、ページのコンテンツストリームの一部になることなく、ページをオーバーレイするオブジェクトです。リンク、付箋、ハイライト、フォームウィジェット、ファイル添付アイコン、スタンプなど、すべてが注釈であり、それらが配置されているページの /Annots 配列にリストされています。その配列から注釈を削除すると、基になるコンテンツが手付かずであっても、ページから注釈が削除されます。これが重要なポイントです。注釈は、それが覆うマークから分離された編集レイヤーなのです
すべての注釈は小さな背骨を共有しています。/Subtype はその種類を指定し、/Rect はページ座標での境界ボックスを提供し、/Contents はアクセス可能な説明としても機能するテキストを保持します。リンク注釈は、裸の宛先とアクションの 2 つの形式で提供されるため、研究する価値のあるケースです
12 0 obj % 宛先へのリンク
<< /Type /Annot /Subtype /Link
/Rect [100 200 300 250]
/Border [0 0 0]
/Dest [5 0 R /XYZ null null null] >>
endobj
13 0 obj % アクションを実行するリンク
<< /Type /Annot /Subtype /Link
/Rect [50 50 200 100]
/Border [0 0 0]
/A << /Type /Action /S /URI /URI (https://www.example.com) >> >>
endobj
/Rect はホットスポットです。その内側をクリックすると、アウトラインが使用するのと同じ文法を再利用して、リーダーが宛先に送信されます。/Border [0 0 0] は実際の仕事をしており、ビューアがリンクの周囲に描画する醜いデフォルトの長方形を抑制しています。2 番目の形式は、裸の /Dest を /A アクションと交換します。その /S サブタイプが動作を選択します。このファイル内の /GoTo、別のファイルへの /GoToR、Web アドレスへの /URI、外部プログラムを実行するための /Launch などです。最後の 1 つは疑いを持つ価値があります。実行可能ファイルを起動する /Launch は PDF をマルウェアのベクターにする動作であるため、準拠するビューアはそれをブロックするか、大音量でプロンプトを表示し、リンクはほとんどのリーダーで失敗します。/URI と /GoTo に手を伸ばし、/Launch はそのままにしておいてください
ハイライトや付箋などのマークアップ注釈、および /Square などの図形注釈には、しわが追加されています。画面上の外観はタイプによって暗示されているわけではありません。アピアランスストリーム(描画演算子を保持するフォーム XObject を参照する /AP エントリ)で外観を固定しない限り、ビューアは独自のバージョンをレンダリングします。これをスキップすると、同じハイライトが 2 つのリーダーで異なって見えたり、エディターでのラウンドトリップの前後で異なって見えたりする可能性があります。正確な外観がドキュメントの一部であるものについては、/AP を提供してください。ちなみに、ファイルの添付はこの同じメカニズムを再利用しています。埋め込まれたファイルストリームとファイル指定辞書であり、/FileAttachment 注釈として、またはカタログの /Names の下にある /EmbeddedFiles 名前ツリーを介して表面化されます
このレイヤーが壊れる場所、そしてそれを捕まえる方法
これらすべてに共通する繰り返しの失敗は、ぶら下がっている参照です。カタログに /Outlines エントリがないか、兄弟チェーンがツリーの中間で途切れていると、ブックマークが表示されなくなります。XMP ストリームに /Type /Metadata /Subtype /XML マーキングがないか、xpacket ラッパーの形式が正しくない場合、メタデータは無視されます。いずれの場合もページコンテンツは問題ないため、何気なく開くと正しく見え、誰もチェックしていないパネルにのみ欠陥が表面化します
2 つの安上がりな習慣で、そのほとんどを捕まえることができます。完成したファイルを実際のビューアで開き、ブックマークパネルとリンクのサンプルをクリックして、リーダーと同じように参照グラフを練習します。次に、別のツールでメタデータを読み戻し、Info 辞書と XMP が一致していることを確認します。これは、いくらクリックしても明らかにならない 1 つの不一致です。このレイヤーをリンクのブックキーピングを所有するライブラリを通じて生成すれば、これらのトラップのほとんどは開くことはありません。Delphi および C++Builder 向けの HotPDF Component は、ドキュメントレベルの API を通じてアウトライン、注釈、およびメタデータ構造を公開するため、ブックマークの階層とリンクを記述し、それに参照をスレッド化させます。これらの構造が添付されるオブジェクトモデルについては、PDF ファイル構造の技術的概要で、それらが依存するカタログと相互参照テーブルについて説明しています