• การพาร์ส PDF ควรทำงานบนพื้นฐานของลำดับและโครงสร้างที่ชัดเจน แต่ไฟล์จริงมักไม่ทำตามข้อกำหนดนี้เสมอไป
  • เกิดข้อผิดพลาดและความไม่สอดคล้องหลากหลายรูปแบบในการหา cross-reference (xref) pointer และออฟเซ็ต
  • ในทางปฏิบัติ ปัญหาจำนวนมากเกิดจาก ข้อมูลส่วนเกินก่อน PDF header รวมถึงตำแหน่งของ pointer และออฟเซ็ตที่ผิดพลาด
  • ยังมีหลายกรณีที่ ตาราง xref ของ PDF เองไม่ชัดเจนหรือจัดรูปแบบผิด
  • ด้วยเหตุนี้ โปรแกรมดูไฟล์หลัก ๆ จึงต้อง เพิ่มตรรกะเพื่อรองรับไฟล์ PDF ที่ไม่เป็นมาตรฐาน

แนวทางในอุดมคติสำหรับการพาร์ส PDF

  • ในทางทฤษฎี การพาร์ส PDF จะดำเนินไปตามขั้นตอนที่ค่อนข้างตายตัว
    • ค้นหา comment ของ version header ที่ส่วนต้นของไฟล์
    • ค้นหา cross-reference (xref) pointer
    • รวบรวม object offset ทั้งหมด
    • ค้นหา trailer dictionary เพื่อเข้าถึงโครงสร้างแค็ตตาล็อกทั้งหมด

แนะนำ PDF object

  • PDF object คือหน่วยที่ใช้ห่อและเก็บ องค์ประกอบต่าง ๆ ของ PDF เช่น number, string, dictionary
  • แต่ละ object จะอยู่ระหว่างมาร์กเกอร์ "obj/endobj"
  • object ต่าง ๆ เชื่อมโยงกันด้วย indirect reference (เช่น "16 0 R")
  • วิธีแบ่ง object ภายในไฟล์ทำได้อย่างอิสระ แต่ object บางประเภทจำเป็นต้องเป็น indirect reference เท่านั้น

การหา cross-reference offset

  • ในเชิงโครงสร้าง PDF จะมี ตาราง cross-reference (xref) ซึ่งทำหน้าที่เป็นดัชนีตำแหน่งของ object
  • ที่ท้ายไฟล์จะมีคำสั่ง "startxref" เพื่อระบุตำแหน่งไบต์เฉพาะเป็น pointer
  • pointer นี้ใช้ชี้ไปยังตำแหน่งของ xref แต่ สเปกกับไฟล์จริงมีความแตกต่างกัน ตัวอย่างเช่น เดิมทีมาร์กเกอร์ "%EOF" ควรอยู่บรรทัดสุดท้าย แต่ใน PDF จริงอาจอยู่ตรงไหนก็ได้ภายใน 1,024 ไบต์ท้ายสุด
  • ในไฟล์จริงยังพบความแปรผันอีกหลายแบบ เช่น รูปแบบ pointer ผิด (startref เป็นต้น) หรือไม่มีการขึ้นบรรทัดใหม่

การหา object offset

  • ตาราง xref จะประกอบด้วย "xref", หมายเลขเริ่มต้นของ object และจำนวน object ต่อกันตามลำดับ โดย offset / generation number / state (n หรือ f) ของแต่ละ object จะถูกบันทึกไว้ทีละบรรทัด
  • ตาราง xref อาจมีหลายชุด หรือเชื่อมต่อกันผ่านเอนทรี /Prev ก็ได้

การค้นหาตำแหน่ง trailer dictionary

  • เหนือมาร์กเกอร์ startxref จะมี trailer dictionary อยู่ ซึ่งบรรจุเมทาดาทาที่จำเป็นสำหรับการหา root object
  • เมื่อได้ root object แล้ว ก็สามารถเริ่มตีความโครงสร้างทั้งหมดได้

สภาพแวดล้อมจริง: ปัญหาที่คาดไม่ถึง

  • มีไฟล์จำนวนมากที่ไม่ทำตามสเปก PDF ทำให้ parser ทั่วไป จัดการได้ยาก

  • กรณีที่มักล้มเหลวในการ ค้นหา cross-reference pointer

    • pointer ไม่ได้อยู่ที่ท้ายไฟล์หรือในช่วง 1,024 ไบต์สุดท้าย
    • มีการพิมพ์ผิด (startref เป็นต้น)
    • อยู่ในรูปแบบผิดปกติ
  • จากการสำรวจตัวอย่าง PDF จริง 3,977 ไฟล์ พบว่าประมาณ 0.5% มีข้อผิดพลาดในการประกาศ xref

เนื้อหา PDF เริ่มต้นที่ออฟเซ็ตซึ่งไม่ใช่ 0

  • หากมี ข้อมูลขยะ (junk) อยู่ก่อน header ออฟเซ็ตของไบต์ทั้งหมดจะเลื่อน ทำให้ตำแหน่ง startxref คลาดเคลื่อน
  • จำเป็นต้องคำนวณออฟเซ็ตใหม่โดยอิงจากตำแหน่ง header และต้อง ตรวจสอบทั้งสองตำแหน่ง
  • คิดเป็นประมาณ 50% ของข้อผิดพลาดทั้งหมด

xref pointer ชี้ไปกลางตาราง xref

  • ออฟเซ็ตที่ระบุอาจพาไปยังกลางเนื้อหาของตาราง xref ได้
  • พบประมาณ 5 กรณีจากตัวอย่าง 3,977 ไฟล์

pointer อยู่ใกล้ xref

  • บ่อยครั้ง pointer ไม่ได้แม่นยำเป๊ะ แต่คลาดเพียงระดับ ช่องว่างหรืออักขระขึ้นบรรทัดใหม่ ที่อยู่ก่อนหรือหลัง xref โดยตรง

pointer ถูกต้อง แต่ xref offset ผิด

  • ตัวออฟเซ็ตที่บันทึกไว้ในตาราง xref เองก็อาจผิดได้
  • บางกรณี object เพียงบางส่วนเท่านั้นที่ถูกต้อง ส่วนที่เหลือมี offset ผิดพลาด

pointer แรกปกติ แต่ offset ก่อนหน้า (/Prev) ผิดปกติ

  • มีหลายกรณีที่ /Prev pointer ซึ่งเกิดขึ้นระหว่างการแก้ไข PDF ถูกบันทึกค่าไม่ถูกต้อง (เช่น 0)

รูปแบบของตาราง xref ไม่ปกติ

  • มีหลายรูปแบบ เช่น "xref" ติดกับตัวเลขโดยไม่มีการขึ้นบรรทัดใหม่, มีรายการมากกว่าจำนวน object ที่ประกาศไว้, หรือมี ข้อมูลขยะ ปะปนอยู่กลางตาราง
  • กรณีลักษณะนี้ถูกรายงานเป็น issue จำนวนมากใน PdfPig เป็นต้น

บทสรุป

  • ตามข้อกำหนดแล้ว การพาร์ส PDF ควรถูกประมวลผลตามลำดับที่เป็นแบบแผน แต่ในไฟล์จริงจำนวนมากกลับไม่เป็นเช่นนั้น จึงเกิด ปัญหาหลากหลายในการพาร์ส
  • โปรแกรมดู PDF ที่ใช้งานจริงจึงมักมีฟังก์ชัน ขยายการรองรับ PDF ที่ไม่เป็นไปตามมาตรฐาน ติดมาด้วยเป็นพื้นฐาน
  • เนื้อหาสรุปครั้งนี้กล่าวถึงเพียงบางส่วนของการพาร์สตามสเปก PDF เท่านั้น (22 หน้าจากทั้งหมด 1300 หน้า)

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น