อยากพาร์ส PDF ใช่ไหม?
(eliot-jones.com)- การพาร์ส 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 หน้า)
ยังไม่มีความคิดเห็น