บทความเทคนิค

อธิบายเกี่ยวกับเมทาดาตา เค้าโครง และคำอธิบายประกอบของ PDF

หากตัดส่วนคำอธิบายหน้าเว็บออกไป สิ่งที่เหลืออยู่คือชั้นโครงสร้างบางๆ ที่ไม่มีใครสั่งพิมพ์ออกมา แต่โปรแกรมอ่าน โปรแกรมทำดัชนี และระบบจัดเก็บถาวรทุกตัวต้องพึ่งพามัน ออบเจ็กต์หน้าเว็บไม่รับรู้เลยว่าตัวเองอยู่ในบทไหน ใครเป็นคนเขียน หรือมีเชิงอรรถที่ลิงก์ไปที่ใด ความรู้เหล่านี้จะอยู่ในระดับที่สูงขึ้นไปอีกขั้น ภายในโครงสร้างสามส่วนที่แนบมากับ document catalog ได้แก่ สตรีมเมทาดาตา (metadata streams), โครงสร้างต้นไม้เค้าโครง (outline tree) และอาร์เรย์คำอธิบายประกอบในแต่ละหน้า (per-page annotation arrays) ทั้งสามส่วนนี้มีจุดเด่นร่วมกันอย่างหนึ่งที่ทำให้มักจะเกิดข้อผิดพลาดได้ง่าย นั่นคือพวกมันไม่แสดงร่องรอยใดๆ บนหน้าเว็บ ดังนั้นไฟล์จึงสามารถเรนเดอร์ออกมาได้อย่างสมบูรณ์แบบ ทั้งที่บุ๊กมาร์กหายไป ขัดแย้งกับช่องผู้เขียนของตัวมันเอง หรือลิงก์ไปยังออบเจ็กต์หน้าเว็บที่ไม่มีอยู่จริงแล้วก็ตาม

นี่คือชั้นของข้อมูลที่ไลบรารี PDF เผยแพร่ออกมาในรูปแบบของคุณสมบัติเอกสาร (document properties), API สำหรับบุ๊กมาร์ก และการเรียกใช้ลิงก์หรือคำอธิบายประกอบ และยังเป็นชั้นที่โปรแกรมรวบรวมข้อมูลการค้นหา (search crawler) อ่านเพื่อพิจารณาว่าเอกสารของคุณเกี่ยวกับอะไร โมเดลออบเจ็กต์ที่อยู่ข้างใต้นั้นได้อธิบายไว้แล้วใน การเจาะลึกโครงสร้างเอกสาร PDF ส่วนในที่นี้จะเน้นไปที่สิ่งที่พ่วงติดมากับ catalog เป็นหลัก

แผนภาพ document catalog ของ PDF ที่ลิงก์ไปยัง page tree, outline root, name dictionary และ stream ข้อมูลเมตา XMP ในฐานะระบบย่อยเสริมสี่ระบบ
catalog ของเอกสารเดินสายระบบย่อยอิสระสี่ระบบ แต่ละระบบเข้าถึงได้ผ่านทางเข้าของตัวเอง ตัวใดตัวหนึ่งขาดหายไปได้ในขณะที่หน้ายังเรนเดอร์ตามปกติ

โครงสร้างทั้งสามส่วนเชื่อมต่อกันที่ catalog ซึ่ง catalog ที่สมบูรณ์แบบที่เชื่อมโยงสิ่งเหล่านี้เข้าด้วยกันจะมีลักษณะดังนี้:

1 0 obj
<< /Type /Catalog
   /Pages 2 0 R
   /Outlines 3 0 R
   /Names << /EmbeddedFiles 4 0 R >>
   /Metadata 5 0 R
>>
endobj

รายการสี่รายการ ระบบย่อยที่เป็นอิสระสี่ระบบ /Pages คือเอกสารที่มองเห็นได้; /Outlines คือโครงสร้างต้นไม้ของบุ๊กมาร์ก; /Metadata ชี้ไปที่สตรีม XMP; /Names ไปถึงดิกชันนารีชื่อที่ครอบคลุมทั้งเอกสาร ซึ่งใช้สำหรับเก็บไฟล์แนบที่ฝังไว้ เป็นต้น แต่ละส่วนเป็นทางเลือก (optional) และโปรแกรมอ่านที่ไม่พบส่วนใดๆ เหล่านี้ก็ยังคงแสดงหน้าเว็บได้ ความเป็นทางเลือกนี่เองคือเหตุผลที่ชั้นการนำทาง (navigation layer) มักจะพังเป็นส่วนแรกเมื่อไฟล์ถูกแก้ไขด้วยเครื่องมือที่เข้าใจเฉพาะหน้าเว็บเท่านั้น

ที่เก็บเมทาดาตาสองแห่งที่ไม่ตรงกัน

PDF จัดเก็บเมทาดาตาของเอกสารไว้ในสองที่พร้อมกัน และปัญหาจะเริ่มขึ้นเมื่อทั้งสองที่ให้ข้อมูลไม่ตรงกัน กลไกแบบดั้งเดิมคือ document information dictionary (ดิกชันนารีข้อมูลเอกสาร) ซึ่งอ้างอิงโดย /Info ใน trailer: เป็นชุดของคู่คีย์-ค่าแบบแบนๆ สำหรับ /Title, /Author, /Subject, /Keywords, /Creator, /Producer และวันที่สองค่า มันเรียบง่ายและโปรแกรมดูทุกตัวก็สามารถอ่านได้ แต่ PDF 2.0 ได้ยกเลิกการใช้งานส่วนใหญ่เพื่อเปลี่ยนไปใช้กลไกที่สอง นั่นคือสตรีมเมทาดาตา XMP

แผนภาพ PDF เปรียบเทียบ Info dictionary ของ PDF กับ stream ข้อมูลเมตา XMP สองที่เก็บที่แบกฟิลด์ที่ทับซ้อนกัน ซึ่งต้องคงความพ้องกันโดยให้ XMP เป็นแหล่งความจริง
dictionary หน้า Info และสตรีม XMP ตอบคำถามชุดเดียวกันไปพร้อมกัน รักษาให้ซิงก์กันโดยสร้างทั้งสองใหม่จากชุดค่าเดียว

XMP เป็นเอกสาร XML ที่สมบูรณ์ในตัวเอง เขียนด้วย RDF เก็บเป็นสตรีมที่ catalog เข้าถึงผ่าน /Metadata และถูกทำเครื่องหมายเป็น /Type /Metadata /Subtype /XML ต่างจาก Info dictionary ที่ถูกฝังอยู่ภายในโครงสร้างออบเจ็กต์ PDF แพ็กเก็ต 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

รายละเอียดสามจุดในบล็อกนั้นเป็นตัวกำหนดว่าเมทาดาตาจะอยู่รอดเมื่อเจอเครื่องมือใช้งานจริงหรือไม่ คำสั่งประมวลผล xpacket ไม่ใช่แค่ของตกแต่ง: มันตีกรอบแพ็กเก็ตเพื่อให้โปรแกรมสกัด (extractor) สามารถค้นหามันได้ในสตรีมไบต์ขนาดใหญ่ และโปรแกรมเขียนที่ละเลย <?xpacket end="w"?> ปิดท้ายจะสร้างไฟล์ที่เปิดได้ปกติแต่ไม่ผ่านตัวตรวจสอบ (validators) ที่เข้มงวด ประเภทข้อมูลของคุณสมบัติก็สำคัญเช่นกัน dc:title เป็นภาษาทางเลือกที่ห่อหุ้มด้วย rdf:Alt ในขณะที่ dc:creator เป็นรายการที่มีลำดับและใช้ rdf:Seq; การปล่อยอย่างใดอย่างหนึ่งออกมาเป็นแค่ข้อความ (text node) เพียวๆ คือข้อผิดพลาดเกี่ยวกับ XMP ที่พบบ่อยที่สุด ซึ่งโปรแกรมดูส่วนใหญ่ยอมรับได้ จนกระทั่งเจอกับโปรแกรมที่ไม่ยอมรับ คำนำหน้าเนมสเปซเป็นเพียงธรรมเนียมปฏิบัติ แต่ URI ที่พวกมันผูกติดด้วยเป็นมาตรฐานที่ต้องปฏิบัติตาม: พาร์สเซอร์จะค้นหาจาก URI ไม่ใช่คำนำหน้า

กฎเหล็กของการมีที่เก็บข้อมูลสองแห่งคือพวกมันต้องตรงกัน หาก /Info ระบุว่าผู้เขียนคือคนหนึ่ง และ dc:creator ระบุชื่ออีกคน คุณได้ส่งมอบเอกสารที่ตอบคำถามเดียวกันได้สองแบบ และคำตอบใดจะชนะก็ขึ้นอยู่กับว่าเครื่องมือผู้บริโภคอ่านจากฟิลด์ไหน โดยปกติแล้วไลบรารีจะเขียนให้คุณทั้งสองอย่าง แต่ทันทีที่คุณแก้ไขอันใดอันหนึ่งด้วยตัวเอง หรือรวมไฟล์จากโปรแกรมสร้างที่ต่างกัน ทั้งสองส่วนนี้ก็จะเริ่มคลาดเคลื่อนออกจากกัน ให้ถือว่า Info dictionary เป็นความเข้ากันได้กับระบบเก่า (legacy compatibility) และ XMP เป็นแหล่งข้อมูลความจริง (source of truth) และควรสร้างทั้งคู่ใหม่จากชุดค่าเดียวกันแทนที่จะแก้ไขแยกกัน สำหรับ PDF/A นี่คือข้อกำหนดความสอดคล้อง: ISO 19005 บังคับให้ใช้ XMP และห้ามใช้คุณสมบัติ Info ใดๆ ที่ขัดแย้งกับคู่ขนานของมันใน XMP

โครงสร้างต้นไม้เค้าโครงเบื้องหลังแผงบุ๊กมาร์ก

สิ่งที่โปรแกรมดูแสดงเป็นแผงบุ๊กมาร์ก แท้จริงแล้วในไฟล์คือโครงสร้างต้นไม้ของดิกชันนารีที่เชื่อมโยงกันแบบสองทิศทาง (doubly linked tree) ที่เรียกว่า document outline catalog ชี้ไปที่ดิกชันนารีเค้าโครงราก (root outline dictionary) ผ่าน /Outlines; รากชี้ไปที่รายการระดับบนสุดรายการแรกและรายการสุดท้าย; และทุกรายการจะเชื่อมโยงกับเพื่อนบ้านและพาเรนต์ของมัน ไม่เคยมีอาร์เรย์ของบุ๊กมาร์กอยู่ที่ไหนเลย โครงสร้างทั้งหมดถูกสร้างขึ้นใหม่โดยการตามการอ้างอิงไป ซึ่งนั่นคือเหตุผลอย่างชัดเจนว่าทำไมลิงก์ที่เสียเพียงลิงก์เดียวก็สามารถทำให้กิ่งก้านสาขาทั้งหมดหายไปจากแผงควบคุมได้โดยไม่มีข้อผิดพลาดใดๆ

แผนภาพ outline tree ของ PDF แสดงลิงก์ Parent, First, Last, Prev, Next และ Count ที่ประกอบแผงบุ๊กมาร์กของโปรแกรมดูขึ้นมาใหม่
พาเนลบุ๊กมาร์กถูกสร้างใหม่จากต้นไม้ outline แบบ doubly linked ที่ร้อยด้วย Parent, First, Last, Prev, Next และ Count ลิงก์เก่าหนึ่งเส้นซ่อนทั้งกิ่งไว้โดยไม่มีข้อผิดพลาด
8 0 obj                                    % root ของ outline
<< /Type /Outlines /Count 4 /First 9 0 R /Last 9 0 R >>
endobj
9 0 obj                                    % top-level: a chapter
<< /Title (Chapter 1: Results)
   /Parent 8 0 R /Count 2
   /First 12 0 R /Last 15 0 R >>
endobj
12 0 obj                                   % first child
<< /Title (Introduction)
   /Parent 9 0 R /Next 15 0 R
   /Dest [3 0 R /XYZ 72 720 0] >>
endobj
15 0 obj                                   % second child, last sibling
<< /Title (Methodology)
   /Parent 9 0 R /Prev 12 0 R
   /Dest [3 0 R /Fit] >>
endobj

เพียงแค่อ่านลิงก์ คุณก็จะเห็นความสม่ำเสมอได้อย่างชัดเจน ทุกรายการจะชี้กลับไปยัง /Parent ของมัน พี่น้องสร้างสายโซ่ผ่าน /Prev และ /Next โดยรายการแรกจะละเว้น /Prev และรายการสุดท้ายจะละเว้น /Next พาเรนต์ระบุชื่อลูกคนแรกและคนสุดท้ายผ่าน /First และ /Last และลูกที่อยู่ตรงกลางจะสามารถเข้าถึงได้โดยการเดินตามสายโซ่พี่น้องเท่านั้น หากทำผิดพลาดไปเพียงหนึ่งจุดก็จะเกิดความล้มเหลวแบบเงียบๆ: /Next ที่ไม่ทำงานจะตัดบทนั้นทิ้ง, พาเรนต์ที่มี /Last ไม่สิ้นสุดสายโซ่จะทำให้รายการนั้นกลายเป็นเด็กกำพร้า และโปรแกรมดูก็จะเรนเดอร์เฉพาะสิ่งที่มันสามารถเข้าถึงได้เท่านั้น

ฟิลด์ /Count เก็บข้อมูลสถานะที่ทำให้คนประหลาดใจ ที่รากและรายการที่ถูกขยาย (expanded item) ฟิลด์นี้จะเก็บจำนวนลูกหลานที่มองเห็นได้ในปัจจุบัน; สำหรับรายการที่ถูกพับเก็บ (collapsed item) จะเป็นตัวเลขติดลบซึ่งค่าสัมบูรณ์ของมันคือจำนวนลูกหลานที่จะปรากฏเมื่อถูกขยาย ดังนั้น /Count จึงไม่ใช่ข้อเท็จจริงเชิงโครงสร้างที่คงที่ของต้นไม้ แต่มันคือสถานะการเปิดหรือปิดของแผงควบคุมที่ถูกบันทึกไว้ และโปรแกรมสร้าง (generator) ที่ฮาร์ดโค้ดให้มันเป็นผลรวมเชิงบวก จะเป็นการเปิดทุกสาขาที่ผู้เขียนตั้งใจจะปิดไว้ขึ้นมาใหม่ทั้งหมด

แต่ละรายการมีตำแหน่งของมันได้จากการชี้ไปที่ใดที่หนึ่ง /Title คือสิ่งที่แผงแสดง; /Dest คือจุดที่การคลิกจะไปถึง ปลายทางสามารถอยู่ภายในรายการ (inline) ได้ตามด้านบน หรืออาจเป็นชื่อที่แก้ไขผ่านดิกชันนารีชื่อของเอกสาร ซึ่งเป็นตัวเลือกที่ดีกว่าเมื่อบุ๊กมาร์กและลิงก์จำนวนมากกำหนดเป้าหมายไปที่จุดเดียวกัน เพราะคุณสามารถแก้ไขเป้าหมายที่ถูกย้ายได้ในที่เดียว ไลบรารีทั่วไปมักจะซ่อนโครงสร้างต้นไม้นี้ไว้ด้านหลัง outline-root handle และใช้เมธอดในการเพิ่มรายการลูก; ใน HotPDF เอกสารจะเปิดเผย OutlineRoot ประเภท THPDFDocOutlineObject และจะร้อยเรียงลิงก์ /Prev, /Next, /Parent และ /Count ให้คุณขณะที่คุณผนวกรายการต่างๆ การใช้ประโยชน์จากสิ่งนี้นับว่าคุ้มค่า เพราะการดูแลความสม่ำเสมอเหล่านี้ด้วยมือผ่านการแก้ไข คือจุดที่มักจะทำให้โครงสร้างพังทลาย

ปลายทาง: ไวยากรณ์ของตำแหน่งที่คลิกไป

ทั้งบุ๊กมาร์กและลิงก์คำอธิบายประกอบจะชี้ไปยังปลายทาง (destinations) และปลายทางก็มีมากกว่าแค่หมายเลขหน้า มันคืออาร์เรย์ที่ระบุชื่อออบเจ็กต์หน้าเว็บ และจากนั้นจะระบุว่าโปรแกรมดูควรตีกรอบหน้าเว็บนั้นอย่างไรผ่านคำกริยาในสล็อตที่สอง สิ่งที่พบบ่อยและถูกใช้ผิดๆ มากที่สุดคือ /XYZ ซึ่งอยู่ในรูปแบบ [page /XYZ left top zoom] ตัวถูกดำเนินการทั้งสามเป็นอิสระต่อกัน และตัวใดตัวหนึ่งสามารถเป็น null เพื่อหมายถึง "ปล่อยไว้เหมือนที่ผู้อ่านได้เห็น" ดังนั้น [page /XYZ null null null] จึงข้ามไปยังหน้าเว็บโดยไม่แตะต้องตำแหน่งการเลื่อนหรือการซูม ซึ่งมักจะเป็นสิ่งที่คุณต้องการจากลิงก์ "ไปที่หน้า" ตัวเลขเหล่านี้อยู่ใน default user space (พื้นที่ผู้ใช้เริ่มต้น) วัดจากด้านล่างซ้ายโดยค่า y เพิ่มขึ้นขึ้นไปด้านบน ซึ่งเป็นระบบพิกัดเดียวกันกับที่ใช้ในเนื้อหาหน้าเว็บ ผู้เขียนที่คุ้นเคยกับเค้าโครงหน้าจอมักจะวัดจากด้านบน และส่งผู้อ่านไปยังส่วนท้ายหน้าเว็บแบบผิดๆ เสมอ

ตระกูล /Fit แลกการวางตำแหน่งที่แม่นยำกับความยืดหยุ่น [page /Fit] จะปรับขนาดทั้งหน้าให้พอดีกับหน้าต่าง, [page /FitH top] จะปรับความกว้างของหน้าให้พอดีกับขอบด้านบนที่กำหนด, และ [page /FitR l b r t] จะซูมสี่เหลี่ยมผืนผ้าให้เต็มมุมมอง เนื่องจากวิธีการเหล่านี้คำนวณมาตราส่วนจากรูปทรงของหน้าเว็บแทนที่จะเป็นพิกัดคงที่ ปลายทางแบบ /Fit จึงยังคงทำงานได้อย่างสมเหตุสมผลหลังจากที่หน้าเว็บถูกปรับขนาด ในขณะที่ปลายทางแบบ /XYZ ที่มีการซูมตายตัวอาจทำให้ผู้อ่านจ้องไปที่ขอบหน้าได้ สำหรับสารบัญ การใช้ /FitH กับพิกัดด้านบนของส่วนเนื้อหา จะมีอายุการใช้งานที่ยาวนานกว่า /XYZ ที่ใช้การเดาการซูม

คำอธิบายประกอบ: ทุกสิ่งที่โต้ตอบได้และไม่ใช่เนื้อหาหน้าเว็บ

คำอธิบายประกอบคือออบเจ็กต์ที่วางทับซ้อนหน้าเว็บโดยไม่ได้เป็นส่วนหนึ่งของสตรีมเนื้อหา ลิงก์, กระดาษโน้ต (sticky notes), ไฮไลต์, วิดเจ็ตฟอร์ม, ไอคอนแนบไฟล์, ตราประทับ (stamps): ทั้งหมดนี้ล้วนเป็นคำอธิบายประกอบ ซึ่งแสดงรายการอยู่ในอาร์เรย์ /Annots ของหน้าเว็บที่พวกมันตั้งอยู่ การลบคำอธิบายประกอบออกจากอาร์เรย์นั้น เป็นการลบมันออกจากหน้าเว็บ แม้ว่าเนื้อหาข้างใต้จะไม่ถูกแตะต้องเลยก็ตาม นั่นคือประเด็นสำคัญ: คำอธิบายประกอบคือชั้นของการแก้ไข ที่แยกส่วนต่างหากจากรอยต่างๆ ที่พวกมันวางทับอยู่

คำอธิบายประกอบทุกอันจะมีแกนร่วมกันเล็กๆ /Subtype บอกถึงชนิด, /Rect บอก bounding box ในพิกัดของหน้า, และ /Contents เก็บข้อความที่ทำหน้าที่เป็นคำอธิบายที่เข้าถึงได้ด้วย (accessible description) คำอธิบายประกอบแบบลิงก์เป็นกรณีที่น่าศึกษา เพราะมันมีอยู่สองรูปแบบ: ปลายทางแบบเปลือยๆ และแอ็กชัน (action)

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                                    % link that runs an action
<< /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] มีหน้าที่สำคัญ คือการซ่อนสี่เหลี่ยมผืนผ้าเริ่มต้นที่น่าเกลียดซึ่งโปรแกรมดูมักจะวาดรอบลิงก์ รูปแบบที่สองจะเปลี่ยน /Dest เปลือยๆ เป็นแอ็กชัน /A โดย subtype ของมัน /S จะเลือกพฤติกรรม: /GoTo ภายในไฟล์นี้, /GoToR สำหรับไฟล์อื่น, /URI สำหรับที่อยู่เว็บ, /Launch เพื่อรันโปรแกรมภายนอก อย่างหลังสุดนี้เป็นสิ่งที่ควรระวังเป็นพิเศษ /Launch ที่เริ่มต้นไฟล์สั่งการ (executable) เป็นพฤติกรรมที่ทำให้ PDF กลายเป็นเวกเตอร์แพร่กระจายมัลแวร์ โปรแกรมดูที่ได้มาตรฐานจึงมักจะบล็อกมันหรือแจ้งเตือนอย่างชัดเจน ทำให้ลิงก์ใช้งานไม่ได้สำหรับผู้อ่านส่วนใหญ่ ดังนั้นควรเลือกใช้ /URI และ /GoTo แล้วปล่อย /Launch ไว้ตามเดิมจะดีที่สุด

คำอธิบายประกอบแบบมาร์กอัป (Markup annotations) เช่น ไฮไลต์และกระดาษโน้ต รวมถึงคำอธิบายประกอบรูปทรงอย่าง /Square มีสิ่งที่ต้องระวังเพิ่มเติม: รูปลักษณ์บนหน้าจอของพวกมันไม่ได้ถูกบอกเป็นนัยโดยประเภทของมัน โปรแกรมดูจะเรนเดอร์เวอร์ชันของตัวเอง เว้นแต่คุณจะกำหนดรูปลักษณ์ด้วย appearance stream คือรายการ /AP ซึ่งจะอ้างอิงไปยังฟอร์ม XObject ที่เก็บตัวดำเนินการวาดรูป (drawing operators) เอาไว้ หากข้ามส่วนนี้ไป ไฮไลต์แบบเดียวกันก็อาจดูแตกต่างกันได้ในโปรแกรมอ่านสองโปรแกรม หรือก่อนและหลังการแก้ไขไปและกลับ (round-trip) ในโปรแกรมแก้ไขเอกสาร สำหรับสิ่งใดก็ตามที่รูปลักษณ์ที่แน่นอนเป็นส่วนหนึ่งของเอกสาร ให้ระบุ /AP เสมอ อนึ่ง ไฟล์แนบ (File attachments) ก็ใช้กลไกแบบเดียวกันนี้: สตรีมไฟล์ที่ฝังไว้ (embedded file stream) และดิกชันนารีข้อกำหนดไฟล์ (file specification dictionary) ซึ่งปรากฏเป็นคำอธิบายประกอบ /FileAttachment หรือผ่านโครงสร้างต้นไม้ชื่อ /EmbeddedFiles ภายใต้ /Names ของ catalog

จุดที่ชั้นนี้มักจะพัง และวิธีตรวจจับ

ความล้มเหลวที่เกิดขึ้นบ่อยครั้งที่สุดสำหรับเรื่องนี้ทั้งหมด คือการอ้างอิงที่ห้อยต่องแต่ง (dangling reference) บุ๊กมาร์กหยุดแสดงผลเมื่อ catalog ไม่มีรายการ /Outlines หรือสายโซ่พี่น้องขาดตอนกลางโครงสร้างต้นไม้; เมทาดาตาจะถูกละเลยเมื่อสตรีม XMP ขาดเครื่องหมาย /Type /Metadata /Subtype /XML หรือ wrapper ของ xpacket มีรูปแบบที่ผิดเพี้ยน ในทุกกรณีเนื้อหาหน้าเว็บยังคงใช้งานได้ตามปกติ การเปิดดูแบบคร่าวๆ จึงดูเหมือนว่าถูกต้อง และข้อบกพร่องจะปรากฏก็ต่อเมื่อดูในแผงควบคุมที่ไม่มีใครตรวจสอบเท่านั้น

นิสัยง่ายๆ สองข้อสามารถช่วยตรวจจับปัญหาส่วนใหญ่ได้ เปิดไฟล์ที่เสร็จแล้วในโปรแกรมดูของจริง แล้วคลิกเข้าไปในแผงบุ๊กมาร์กและลิงก์ตัวอย่าง เพื่อทดสอบกราฟอ้างอิงแบบเดียวกับที่ผู้อ่านจะทำ จากนั้นใช้เครื่องมือแยกต่างหากอ่านเมทาดาตากลับมา เพื่อยืนยันว่า Info dictionary และ XMP สอดคล้องกัน ซึ่งเป็นความขัดแย้งจุดเดียวที่ไม่มีการคลิกใดๆ สามารถเปิดเผยได้ การสร้างชั้นข้อมูลนี้ผ่านไลบรารีที่จัดการการเก็บบันทึกลิงก์ จะช่วยให้คุณรอดพ้นจากกับดักเหล่านี้ได้ HotPDF Delphi Component สำหรับ Delphi และ C++Builder เผยแพร่โครงสร้างเค้าโครง คำอธิบายประกอบ และเมทาดาตาผ่าน API ระดับเอกสาร ดังนั้นคุณเพียงแค่อธิบายลำดับชั้นบุ๊กมาร์กและลิงก์ แล้วปล่อยให้มันร้อยเรียงการอ้างอิงให้เอง สำหรับโมเดลออบเจ็กต์ที่โครงสร้างเหล่านี้แนบมาด้วย ภาพรวมทางเทคนิคของโครงสร้างไฟล์ PDF ได้ครอบคลุมเรื่อง catalog และ cross-reference table ที่พวกมันต้องพึ่งพาเอาไว้แล้ว