อยากพาร์ส 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 หน้า)
5 ความคิดเห็น
สรุปครั้งนี้พูดถึงเพียงการพาร์สบางส่วนของสเปก PDF (22 หน้าจากทั้งหมด 1300 หน้า) <-... 1300 หน้านี่มหาศาลจริง ๆ...
ว้าว..
PDF พูดตามตรงคือฟอร์แมตที่อ่านง่ายสำหรับมนุษย์ซึ่งออกแบบมาเพื่อคงรูปแบบที่มนุษย์สร้างไว้ให้มากที่สุด และเข้ากับเครื่องได้แย่ที่สุดเลย
เห็นด้วยเลยครับ จริง ๆ ก็ไม่แน่ใจเหมือนกันว่าอ่านง่ายไหม.. ทั้งหนักและใช้งานไม่สะดวก
ความคิดเห็นจาก Hacker News
คำตอบชัดเจนมาก
ในโลกความเป็นจริง ผมชื่อ Geoff แต่ resume parser ครึ่งหนึ่งกลับแยกชื่อผมเป็น "Geo" กับ "ff" คนละส่วน
นี่เป็นเพราะวิธีที่ข้อความถูกเก็บอยู่ใน PDF และเป็นปัญหาที่เกิดซ้ำกับแอปต้นทางหลายตัว
การ parse PDF กับการ parse เนื้อหาข้างใน PDF เป็นคนละเรื่องกันโดยสิ้นเชิง
การ parse ตัวไฟล์ PDF เองก็ปวดหัวอยู่แล้ว แต่ PDF นั้นตั้งอยู่บนแนวคิดแบบ "วางอะไรบางอย่างลงในตำแหน่งที่กำหนด" จึงไม่เหมือนข้อความที่นิยามไว้อย่างชัดเจนภายใน bounding box ทำให้เวลาจะดึงคำออกมาต้องเดาว่าตัวอักษรไหนอยู่ด้วยกัน
ถ้าอยากช่วย resume parser ก็ควรลองดู accessibility tree
ไม่ใช่ PDF renderer ทุกตัวจะส่งออก accessibility PDF แต่ accessibility PDF อย่างน้อยก็ช่วยให้อ่านสิ่งอย่างชื่อคนได้ถูกต้องขึ้น
ปัญหา "ff" น่าจะเป็นกรณีที่ตัววิเคราะห์เรซูเม่จัดการอักขระ non-ASCII ไม่ได้ เช่น ligature อย่าง ff
สามารถตั้งค่าไม่ให้ PDF renderer สร้าง ligature ได้ แต่ข้อความอาจดูไม่สวย
รู้สึกว่าไปคาดหวังกับคำว่า "should" มากเกินไป
ถ้าการใช้ PDF ในทางปฏิบัติค่อนข้างเป็นศัตรูกับผู้ใช้ คนก็คงไม่ได้คิดไกลขนาดนั้น
การส่งเรซูเม่เป็น PDF เองก็มีจุดประสงค์เพื่อไม่ให้คนกลางมาแก้ไขได้ และคำว่า "การแก้ไข" ก็มีได้หลายแบบ ตั้งแต่เอากล่องไปปิดทับบนรูปภาพ ไปจนถึงทำตารางเป็น PDF แทน CSV เพื่อให้วิเคราะห์ได้ยากขึ้น
ในทางปฏิบัติ วิธีนี้ก็ใช้ได้ผลดีอยู่เหมือนกัน และบางแอปก็ใช้แนวทางนี้
เพียงแต่ยังเหลือปัญหาว่าการแสดงผลสองแบบนี้ (เนื้อหา/เมตะดาต้า) อาจไม่ตรงกันจริง
แล้วกรณีสแกนลายมือหรือเอกสารสแกนอื่น ๆ จะทำอย่างไร ถ้าสแกนเนอร์และคอมพิวเตอร์ตามบ้านทั่วไปไม่ได้รองรับ OCR ได้สมบูรณ์แบบ
น่าจะเป็นปัญหาที่เกิดจากการ render
ffเป็น ligatureผมเป็นผู้ก่อตั้ง Tensorlake
เราสร้าง document parsing API สำหรับนักพัฒนา
นี่คือเหตุผลที่แนวทาง Computer Vision ถึงทำงานได้ดีจริงในงาน parse PDF ภาคสนาม
การพึ่งพาแต่เมตะดาต้าในไฟล์อย่างเดียวไม่สามารถสเกลได้กับแหล่ง PDF ที่หลากหลาย
เราเลยแปลง PDF เป็นภาพก่อน จากนั้นใช้โมเดลรู้จำเลย์เอาต์ แล้วตามด้วยโมเดลเฉพาะทางอย่างการรู้จำข้อความและตาราง ก่อนจะนำชิ้นส่วนทั้งหมดมารวมกัน เพื่อให้ได้ผลลัพธ์ที่ใช้งานได้แม้ในงานที่ความแม่นยำเป็นเรื่องสำคัญ
มองผิวเผินอาจดูตลก แต่จริง ๆ แล้วน่าจะเป็นทางแก้ที่สมจริงที่สุด
PDF โดยเนื้อแท้แล้วเป็นฟอร์แมตที่ออกแบบมาเพื่อแสดงเลย์เอาต์ให้มนุษย์อ่าน ไม่ได้ถูกออกแบบมาให้คอมพิวเตอร์อ่าน แต่เน้นการแสดงผลให้ดูดี
เพราะอย่างนั้น แนวทางที่เลียนแบบวิธีอ่านของมนุษย์จึงฟังดูสมเหตุสมผล
แต่อีกด้านหนึ่งก็เสียดายที่ตลอดเวลากว่า 30 ปี PDF ยังเพิ่มความสามารถในการอ่านโดยเครื่องไม่ได้จริง ๆ
เลยอดสงสัยไม่ได้ว่าขาดแรงจูงใจอะไร ถึงยังทำให้เกิดขึ้นไม่ได้
ถ้าใครมีมุมมองเรื่องนี้ก็อยากฟัง
มีความตลกอยู่หน่อย
เวลาคนพิมพ์ PDF ออกมาแล้วสแกนกลับส่งอีเมล เรามักมองว่าเป็นเรื่องน่าขำ แต่พอเป็นการ parse PDF เรากลับทำอะไรแทบไม่ต่างกัน
ความจริงที่ว่านี่คือแนวทางที่จำเป็นมันชวนหงุดหงิด
โลกไม่ได้ parse HTML กันแบบนี้
ผมเป็นผู้ร่วมก่อตั้ง Nutrient.io ทำงานกับ PDF มานานกว่า 10 ปี
PDF viewer เหมือนเว็บเบราว์เซอร์ตรงที่ต้องรองรับ PDF หลากหลายแบบมาก
PDF มีอายุมากแล้ว ผู้สร้างไฟล์จึงมักแก้กันแบบตามใจ ขอแค่แสดงผลได้ดีใน viewer ที่ตัวเองใช้ก็พอ
ด้วยเหตุนี้ บริษัทเราจึงสร้าง AI document processing SDK (REST API ที่รับ PDF แล้วคืนข้อมูลแบบ structured เป็น JSON)
ไม่ได้ใช้แค่วิธีเชิงภาพเท่านั้น แต่ใช้ประสบการณ์ด้าน preprocessing/postprocessing เชิงโครงสร้างด้วย ทำให้ได้ผลลัพธ์ที่ดีกว่าทั้งด้านประสิทธิภาพและต้นทุนเมื่อเทียบกับ vision ล้วน ๆ
ถ้าคุณไม่อยากปวดหัวกับการจัดการ PDF เองและอยากโฟกัสกับงานหลักของคุณ มันอาจช่วยได้
https://www.nutrient.io/sdk/ai-document-processing
ไหน ๆ ก็มีคนเชี่ยวชาญโครงสร้างภายในของ PDF อยู่ตรงนี้ ขอถามหน่อย
ทำไม mupdf-gl ถึงเร็วกว่าทุกโปรแกรมอื่นอย่างเห็นได้ชัด (อย่างน้อยบน Linux เดสก์ท็อปปกติ)
โดยเฉพาะการค้นหาใน PDF ขนาดใหญ่ที่เร็วกว่าอย่างชัดเจน ผมสงสัยมาตลอดว่าทำไม viewer อื่นถึงทำไม่ได้เร็วแบบนี้
ถ้ามี insight เกี่ยวกับเรื่องนี้ก็อยากฟัง
สุดท้ายแล้วก็คือไป outsource งาน parse ให้ซอฟต์แวร์ที่ใช้ render PDF เป็นภาพนั่นเอง
ผมคิดมานานแล้วว่าเราควรเลิกการสื่อสารเอกสารที่ยึดเลย์เอาต์เป็นหลัก
กล่าวคือ เลย์เอาต์ที่จัดอย่างมืออาชีพนั้นจริง ๆ แล้วเป็นเหมือนจารีตเก่ามากกว่า และแทบไม่เกี่ยวกับการทำความเข้าใจเนื้อหาจริง ๆ
ตัวอย่างเช่น เอกสารยื่นหน่วยงานกำกับดูแลมักหนามาก และการจัดให้ตรงตามกฎเลย์เอาต์ก็ทำให้ต้องเสียเวลาอยู่กับ Microsoft Word นานมาก
เพื่อรับประกันเลย์เอาต์แบบนี้ เราจึงส่งงานเป็น DOCX หรือ PDF แต่ฟอร์แมตพวกนี้ไม่เหมาะอย่างยิ่งกับการให้โปรแกรมดึงหรือแปลงเนื้อหาโดยอัตโนมัติ
LLM เองก็อ่านไฟล์พวกนี้ได้ แต่มีต้นทุนการคำนวณสูงกว่ามากเมื่อเทียบกับไฟล์ที่เป็นมิตรกับเครื่องแบบง่าย ๆ เช่น text, markdown, XML, JSON
อีกทางเลือกหนึ่งคือการทำมาตรฐานฟอร์แมตแบบง่ายที่ "เครื่องมาก่อน" และ "เนื้อหามาก่อน" ไปเลย เช่น อิงกับ JSON, XML, HTML
มีแค่โครงสร้างขั้นต่ำและข้อมูลฝังรูปภาพ แล้วเวลาให้มนุษย์อ่านก็ค่อยให้แอป viewer ประกอบออกมาให้ดูดี
การประมวลผลโดยเครื่องก็จะง่ายขึ้นมาก
ถึงแม้จะมีฟอร์แมตคล้ายกันอย่าง HTML/เบราว์เซอร์ หรือ EPUB อยู่แล้ว แต่ผมก็คิดว่าถึงเวลาที่ต้องแทนที่วิธีแบบเก่าได้แล้ว
หวังว่าการปฏิวัติของ LLM จะพาเราไปในทิศทางนี้ และทำให้การ parse PDF ราคาแพงกลายเป็นเพียงท่อทางแบบดั้งเดิมในอนาคต
ผมยังไม่เคยสร้าง DOCX parser แต่ DOCX เป็น XML base และตราบใดที่ไม่ได้ระบุเลย์เอาต์แบบชัดเจน ทุกอย่างก็ไม่ถูกตรึงด้วย absolute coordinates ทั้งหมด ถ้าให้ JPEG ได้ 0 คะแนน, PDF ได้ 15 คะแนน, markdown ได้ 100 คะแนน DOCX ก็น่าจะประมาณ 80 คะแนนในแง่ความง่ายไหม
คิดว่าเป็นการสรุปที่ยอดเยี่ยม และมีอีกประเด็นที่ผมว่าน่าสนใจ
Incremental-save chain: offset ของ startxref ตัวแรกอาจไม่เป็นไร แต่ /Prev link ที่ Acrobat เติมต่อท้ายทุกครั้งที่มีการแก้ไขหลายรอบ มักชี้สั้นกว่าตำแหน่ง xref ถัดไปอยู่ไม่กี่ไบต์
viewer ส่วนใหญ่ (รวมถึง PDF.js, MuPDF, Adobe Reader) จะใช้วิธีบ้าน ๆ คือไล่หา token
objทั่วทั้งไฟล์แล้วสร้างตารางใหม่ขึ้นมา ส่วน parser ที่ยึดตามสเปกเป๊ะ ๆ จะระเบิดทันทีถ้าคุณอยากจัดการเอกสารที่ผ่านการแก้ไขซ้ำจากหลายแอปในโลกจริง เส้นทางกู้คืนแบบนี้ (salvage path) เป็นสิ่งจำเป็น
บ่อยมากที่ reference ก่อนหน้า หรือสักตัวหนึ่งใน chain ชี้ไปยัง offset นอกไฟล์, offset 0 หรือค่าที่ผิดอื่น ๆ
บทความนี้เกิดขึ้นจากการที่ผมกำลังปรับโครงสร้าง logic การ parse ช่วงต้นของโปรเจกต์ PdfPig
ตอนแรกผมพอร์ตโค้ดมาจาก Java PDFBox แต่ก็อยากทำให้มันเร็วและง่ายขึ้นอีก
logic ใหม่จะสแกนทั้งไฟล์ถ้าพลาด xref table/stream ไปแม้แต่อันเดียว และในเส้นทางกู้คืนจะเชื่อถือแค่ offset นั้นเท่านั้น
แต่มันช้าลงกว่าก่อนอย่างชัดเจน และผมก็ยังไม่แน่ใจว่าการเปลี่ยนแบบนี้โอเคจริงไหม
ตอนนี้กำลังไล่หาสารพัด edge case ด้วยชุดทดสอบ 10,000 ไฟล์
https://github.com/UglyToad/PdfPig/pull/1102
ถ้ามีสมมติฐานที่ใช้ได้และมี PDF object parser ที่เหมาะสมก็ดูเหมือนน่าจะง่าย แต่ในความจริงมันไม่ง่ายแบบนั้นเลย
สถานการณ์นี้เหมือนนรก PDF
PDF ไม่ใช่สเปก แต่มันคือฉันทามติทางสังคม เป็นระดับ "บรรยากาศ" มากกว่า
ยิ่งดิ้นก็ยิ่งจมลึกลงไป และตอนนี้เราทุกคนก็เหมือนอาศัยอยู่ในบึงโคลนที่อยู่ไกลจากสายตาของพระเจ้าแล้ว
ผมหัวเราะกับข้อความนี้จริง ๆ
สำหรับคำถามว่า "อยาก parse PDF ไหม" ผมตอบได้เต็มปากเลยว่าไม่อยากเด็ดขาด
เพราะเหตุผลก็อธิบายไว้ดีมากแล้วในบทความต้นฉบับ
ผมก็หวังว่าธนาคารของผมจะให้เอกสารมาในฟอร์แมตที่อ่านง่ายกว่านี้ แต่ก่อนจะถึงตอนนั้นก็คงเลี่ยงไม่ได้
ผมเคยพลาดทำแบบนั้นมาแล้ว และตั้งใจว่าจะไม่ทำอีก
ในฐานะคนที่เคยเขียน PDF parser มาก่อน ผมรู้สึกว่า PDF เป็นฟอร์แมตที่ประหลาดมาก
ผมคิดว่าความประหลาดนี้เกิดจากการออกแบบแต่กำเนิดที่เป็นการผสมกันระหว่าง binary กับ text
ปัญหา offset ของ xref ที่คลาดเคลื่อนแบบก้ำกึ่งก็น่าจะมาจากบั๊กตอนจัดการการแปลงบรรทัดขึ้นบรรทัดใหม่ LF/CR
อีกอย่างที่บทความไม่ได้พูดถึงคือ PDF รุ่นใหม่ (v1.5+) มักไม่ได้มี xref table แบบข้อความธรรมดา แต่เก็บอยู่ใน "xref stream"
และใน v1.6 ขึ้นไป object เองก็อาจถูกเก็บไว้ใน object stream ได้ด้วย
ตอนแรกทุกอย่างอาจดูไม่มีปัญหา แต่พอ object ที่ต้องการไปอยู่ใน stream และ stream นั้นเองก็ใช้การบีบอัดแบบดัดแปลงจาก PNG หรือ offset ไปอยู่ใน xref stream ที่ถูกบีบอัดด้วย flate ก็เริ่มปวดหัวแล้ว
แถมยังมีหลายเวอร์ชันของเอกสารปนกันอยู่ ทำให้การตัดสินว่าอันไหนคือเวอร์ชันล่าสุดยิ่งซับซ้อนเข้าไปอีก
เอกสาร PDF 1.7 ยังหาได้ง่าย แต่จนถึงเมื่อแค่ 2 ปีก่อน เอกสารสเปก PDF 2.0 ยังติด paywall อยู่เลย
PDF เป็นฟอร์แมตที่ไม่ได้ออกแบบโดยคำนึงถึงการสตรีม
trailer dictionary ที่อยู่ท้ายไฟล์ทำให้ parse ได้ยากจนกว่าจะโหลดทั้งไฟล์ครบ
แต่ก็มีสิ่งที่เรียกว่า "streaming-capable PDF" อยู่เหมือนกัน ถ้าข้อมูลที่จำเป็นอยู่ช่วงต้นไฟล์ ก็ render หน้าแรกได้ทันที (หน้าอื่นอาจยังไม่ได้)
ตอนนี้ผมห่างจากฝั่ง PDF มาสักพักแล้ว ต้องเผื่อใจไว้ด้วย
ถึงจะมี footer อยู่ แต่ถ้าเว็บไซต์รองรับ Range Request และใช้ header
Content-Lengthได้ถูกต้อง PDF ก็สตรีมได้ตัวอ่านแบบสตรีมแค่ส่ง HEAD request ขอข้อมูล, ขอข้อมูลไม่กี่ร้อยไบต์ท้ายไฟล์เพื่อหา pointer กับตาราง แล้วค่อยดึงส่วนที่เหลือต่อก็พอ
ถึงจะไม่เหมาะกับ PDF ที่สร้างแบบเรียลไทม์ แต่ถ้าเป็นเว็บเซิร์ฟเวอร์ทั่วไปที่มีมานาน ก็เพิ่มเวลาแค่ 1-2 รอบการรับส่งข้อมูลเท่านั้น
น่าเสียดายที่ไม่ค่อยมีคนใส่ใจกับ parser แบบใช้ Range รายไฟล์ แต่ผมคิดว่าในเชิงเทคนิคมันไม่ได้เป็นไปไม่ได้
ใช่ มีฟอร์แมตที่เรียกว่า Linearized PDF ซึ่งถูกออกแบบมาเพื่อให้แสดงหน้าแรกได้เร็วโดยไม่ต้องดาวน์โหลดทั้งไฟล์
ในสรุปเขาละเรื่องนี้ไว้เพราะต้องอธิบายประกอบเพิ่มพอสมควร
หนึ่งในโปรเจกต์แรก ๆ ที่ผมทำตอนหัด Python คือ PDF parser
ตอนนั้นหวังจะดึงแผนที่สำหรับแคมเปญ DnD ออกมาแบบอัตโนมัติ แต่สุดท้ายก็ล้มเหลวครับ (ฮา)
ผมเคยเขียน TIFF reader มาก่อน
TIFF ก็ขึ้นชื่อว่าเขียนง่ายแต่อ่านยากมาก
PDF ก็ดูจะอยู่ในกลุ่มเดียวกันนี่แหละ