การวินิจฉัยตัวแยกวิเคราะห์ PDF ขั้นสูง
ภาพรวมโดยสรุป
หัวข้อที่มีชื่อว่า “ภาพรวมโดยสรุป”เส้นทางการนำเข้าของ Artisan จะอ่านไฟล์ Portable Document Format (PDF) ที่สร้างโดย Chrome แล้วนำหน้าหนึ่งหน้าเข้าสู่เอกสาร NextPDF เมื่ออินพุตที่ซับซ้อนทำให้การนำเข้าล้มเหลว ให้ตรวจสอบส่วนที่อยู่ใต้ PageImporter::import() ลงไปจนถึงคลาสตัวแยกวิเคราะห์ที่อ่านไฟล์ทีละไบต์
คู่มือนี้ครอบคลุมพื้นผิวตัวแยกวิเคราะห์ระดับล่างในเนมสเปซ NextPDF\Parser ได้แก่ PdfReader, PdfTokenizer, CrossRefParser, StreamDecoder, ResourceCollector, RevisionExtractor รวมถึงอ็อบเจกต์ค่า PdfObject และ RevisionXRefTable สัญลักษณ์ทุกตัวที่แสดงในที่นี้มีอยู่ใน nextpdf/artisan คู่มือนี้อธิบายตัวแยกวิเคราะห์ตามที่ถูกสร้างขึ้นจริง ไม่ใช่อินเทอร์เฟซในอุดมคติ
ใช้คู่มือนี้ได้ทั้งเป็นคำอธิบายและคู่มือปฏิบัติ คู่มือนี้แสดงให้เห็นว่าส่วนประกอบแต่ละส่วนทำงานร่วมกันอย่างไร จากนั้นจึงพาคุณตรวจสอบรีวิชันแบบอัปเดตทีละส่วน สำหรับขอบเขตการนำเข้าที่อยู่เหนือเลเยอร์นี้ โปรดดู คู่มือนักพัฒนาของ Artisan
เมื่อใดที่คุณต้องใช้สิ่งนี้
หัวข้อที่มีชื่อว่า “เมื่อใดที่คุณต้องใช้สิ่งนี้”ใช้พื้นผิวตัวแยกวิเคราะห์เฉพาะเมื่อเส้นทางการนำเข้าปกติล้มเหลวแล้ว และคุณต้องการค้นหาสาเหตุ กรณีที่พบได้บ่อยได้แก่:
PageImporter::import()โยนNextPDF\Artisan\Exception\PdfParseExceptionและคุณจำเป็นต้องทราบว่าตารางอ้างอิงไขว้ ตัวกรองสตรีม หรือ page tree เป็นต้นเหตุ- การอัปเกรด Chrome เปลี่ยนรูปแบบเอาต์พุต เช่น เมื่อตารางอ้างอิงไขว้แบบดั้งเดิมกลายเป็นสตรีมอ้างอิงไขว้ หรือในทางกลับกัน และฟิกซ์เจอร์ของคุณไม่ตรงกันอีก
- คุณได้รับ PDF จากภายนอกที่ Chrome ไม่ได้สร้างขึ้น และต้องการยืนยันว่าตัวแยกวิเคราะห์อ่านได้หรือไม่
- คุณกำลังวิเคราะห์เอกสารที่ถูกอัปเดตทีละส่วน และต้องการช่วงไบต์ต่อรีวิชันหรือการมองเห็นอ็อบเจกต์
หากคุณกำลังเขียนการผสานรวมกับเรนเดอเรอร์ตามปกติ คุณไม่จำเป็นต้องใช้พื้นผิวนี้ ตัวแยกวิเคราะห์เป็นเครื่องมือวินิจฉัยภายใน ไม่ใช่ไลบรารี PDF เอนกประสงค์ ตัวแยกวิเคราะห์ไม่รองรับ PDF ที่เข้ารหัสลับ ตาราง hint แบบ linearized หรือการอัปเดตทีละส่วนที่นิยามอ็อบเจกต์ซ้ำจนขัดแย้งกัน
พื้นผิวตัวแยกวิเคราะห์
หัวข้อที่มีชื่อว่า “พื้นผิวตัวแยกวิเคราะห์”ตัวแยกวิเคราะห์เป็นชุดคลาสขนาดเล็กที่แต่ละคลาสมีความรับผิดชอบเดียว PdfReader คือจุดเริ่มต้น ส่วนคลาสอื่นๆเป็นคลาสร่วมงานที่ตัวแยกวิเคราะห์นี้สร้างหรือเรียกใช้
| คลาส | ความรับผิดชอบ | เมธอดหลัก |
|---|---|---|
PdfReader | อ่านโครงสร้างไฟล์ แก้ไขอ็อบเจกต์ และท่องไปใน page tree | parse(), getObject(), getTrailer(), getObjectNumbers(), getPage(), getPageContentStream(), getPageResources(), getPageMediaBox(), resolveRef(), collectPageResources(), getRevisionCount(), getRevisionXRef(), getRevisions() |
PdfTokenizer | วิเคราะห์ไวยากรณ์เชิงคำศัพท์ตาม ISO 32000-2:2020 §7.2 ได้แก่ names, strings, numbers, dictionaries, arrays และ references | readToken(), readValue(), readName(), readNumber(), readDictionary(), readArray(), readStreamData(), peek(), skipWhitespace(), getOffset(), setOffset() |
CrossRefParser | แยกวิเคราะห์ตารางอ้างอิงไขว้แบบดั้งเดิมและสตรีมอ้างอิงไขว้ | parseXRefTable(), parseXRefStream() |
StreamDecoder | ถอดรหัสไบต์ของสตรีมตาม /Filter | decode() (static) |
ResourceCollector | ท่องไปใน Resources tree แบบเรียกซ้ำและรวบรวมอ็อบเจกต์ทางอ้อมทุกตัวที่เข้าถึงได้ | traverse(), getCollected() |
RevisionExtractor | แบ่งไฟล์ที่ถูกอัปเดตทีละส่วนออกเป็นช่วงไบต์ต่อรีวิชัน | extractRevision() (static), getRevisionBoundaries() (static) |
PdfObject | อ็อบเจกต์ทางอ้อมที่แยกวิเคราะห์แล้วแบบเปลี่ยนแปลงไม่ได้ (dictionary พร้อมสตรีมเสริม) | get(), getRef(), getArray(), getType(), getSubtype(), hasStream(), getDictionary(), getRawStreamData(), getRawDictionaryBytes() |
RevisionXRefTable | สแนปช็อตการอ้างอิงไขว้ต่อรีวิชันแบบเปลี่ยนแปลงไม่ได้ | getObjectNumbers(), getActiveObjectCount(), hasRootUpdate(), getSize() |
PdfReader — จุดเริ่มต้น
หัวข้อที่มีชื่อว่า “PdfReader — จุดเริ่มต้น”สร้าง \NextPDF\Parser\PdfReader ด้วยไบต์ PDF ดิบ แล้วเรียก parse() ก่อนเรียกเมธอดอื่นใด parse() ตรวจสอบส่วนหัว %PDF- ค้นหา startxref ในส่วนท้ายของไฟล์ และไล่ตามห่วงโซ่การอ้างอิงไขว้โดยติดตามลิงก์ /Prev
หลังจาก parse() แล้ว ตัวอ่านจะเปิดเผยเมธอดเป็นสามกลุ่ม:
- การเข้าถึงอ็อบเจกต์
getObject(int $objNum)คืนค่าPdfObjectโดยแก้ไขรายการ Type 2 (อ็อบเจกต์ที่เก็บอยู่ภายใน object stream) ให้โดยอัตโนมัติgetObjectNumbers()คืนค่าlist<int>ที่เรียงลำดับของหมายเลขอ็อบเจกต์ที่ไม่ใช่อิสระทุกตัวresolveRef(mixed $value)ติดตามการอ้างอิงทางอ้อมหนึ่งรายการ ค่าโดยตรงจะผ่านไปโดยไม่เปลี่ยนแปลง - การเข้าถึงหน้า
getPage(int $pageIndex)แก้ไข catalog ท่องไปใน/Pagesและคืนค่าหน้าตามดัชนีฐานศูนย์getPageContentStream(),getPageResources()และgetPageMediaBox()ดึงส่วนที่PageImporterต้องการcollectPageResources()คืนค่าarray<int, PdfObject>สำหรับอ็อบเจกต์ทุกตัวที่เข้าถึงได้จาก Resources และ Contents ของหน้า - การเข้าถึงรีวิชัน
getRevisionCount()คืนค่าจำนวนรีวิชันแบบอัปเดตทีละส่วน ไฟล์ที่มีรีวิชันเดียวจะคืนค่า1getRevisionXRef(int $index)คืนค่าRevisionXRefTableหนึ่งรายการ (ดัชนี0คือรายการที่ใหม่ที่สุด)getRevisions()คืนค่าlist<RevisionXRefTable>ฉบับเต็ม
PdfTokenizer — การวิเคราะห์เชิงคำศัพท์
หัวข้อที่มีชื่อว่า “PdfTokenizer — การวิเคราะห์เชิงคำศัพท์”PdfTokenizer อ่านสตรีมไบต์ โดยทั่วไปคุณแทบไม่ต้องสร้างอินสแตนซ์เอง เพราะ PdfReader และ CrossRefParser มีอินสแตนซ์ของตนอยู่แล้ว ให้ตรวจสอบเลเยอร์นี้เมื่อการแยกวิเคราะห์ล้มเหลวเพราะโทเคนผิดรูปแบบ มีพฤติกรรมสองอย่างที่สำคัญต่อการวินิจฉัย:
- ขีดจำกัดด้านความปลอดภัยคือค่าคงที่ ไม่ใช่การกำหนดค่า ตัวแบ่งโทเคนจำกัดการซ้อนกันของ literal string การซ้อนกันของ dictionary และ array ความยาวคีย์เวิร์ด และจำนวนสมาชิกใน array เมื่ออินพุตเกินขีดจำกัด ตัวแบ่งโทเคนจะโยน
PdfParseExceptionและระบุชื่อขีดจำกัดไว้ในข้อความ อินพุตที่ถูกประดิษฐ์ขึ้นซึ่งไปสะดุดขีดจำกัดเหล่านี้คือการป้องกันที่ทำงานตามที่ออกแบบไว้ ไม่ใช่ข้อบกพร่องของตัวแยกวิเคราะห์ readValue()จัดเส้นทางการแยกวิเคราะห์ เมธอดนี้ตรวจสอบไบต์ถัดไปและมอบหมายงานให้readName(),readLiteralString(),readHexString(),readArray(),readDictionary()หรือตัวอ่าน number/reference การอ้างอิงทางอ้อมN G Rจะถูกคืนค่าเป็นรูปทรง array['type' => 'ref', 'num' => N, 'gen' => G]PdfObject::getRef()และPdfReader::resolveRef()รู้จักรูปทรงนี้
CrossRefParser — การแก้ไขการอ้างอิงไขว้
หัวข้อที่มีชื่อว่า “CrossRefParser — การแก้ไขการอ้างอิงไขว้”CrossRefParser แยกวิเคราะห์รูปแบบทั้งสองที่ Chrome สามารถสร้างได้:
parseXRefTable()อ่านตารางxrefแบบดั้งเดิม (รูปแบบ PDF 1.x) ได้แก่ ส่วนหัวของ subsection รายการขนาดคงที่ 20 ไบต์ และ dictionarytrailerที่ตามมาparseXRefStream()อ่านสตรีมอ้างอิงไขว้ (PDF 2.0, ISO 32000-2:2020 §7.5.8) ได้แก่ อ็อบเจกต์ทางอ้อมที่มี/Type /XRefarray ความกว้างฟิลด์/Wและสตรีมไบนารีของรายการ
ทั้งสองคืนค่ารูปทรงเดียวกัน: array{xref: array<int, ...>, trailer: array<string, mixed>, prevOffset: int|null} PdfReader::parse() ตัดสินใจว่าจะเรียกตัวแยกวิเคราะห์ตัวใดโดยตรวจดูสี่ไบต์ที่ออฟเซ็ตของการอ้างอิงไขว้: xref เลือกตัวแยกวิเคราะห์ตาราง ส่วนค่าอื่นๆจะถูกถือว่าเป็นอ็อบเจกต์สตรีม ตัวแยกวิเคราะห์ทั้งสองบังคับเพดานหนึ่งล้านรายการต่อ subsection เพื่อปฏิเสธจำนวนที่ถูกปลอมแปลงซึ่งมิฉะนั้นจะทำให้ตัวแยกวิเคราะห์ทำงานมากเกินไป
StreamDecoder — ตัวกรองสตรีม
หัวข้อที่มีชื่อว่า “StreamDecoder — ตัวกรองสตรีม”StreamDecoder::decode(string $data, string|array $filter) เป็น static และรับตัวกรองหนึ่งตัวหรือรายการตัวกรองที่เชื่อมต่อกัน รองรับเฉพาะตัวกรองที่ printToPDF ของ Chrome สร้างขึ้นเท่านั้น:
FlateDecode(zlib พร้อม raw-deflate fallback)ASCIIHexDecodeASCII85Decode
ชื่อตัวกรองอื่นใดจะโยน PdfParseException พร้อม Unsupported stream filter ตัวถอดรหัสจำกัดเอาต์พุตที่คลายการบีบอัดไว้ที่ 16 MiB เพื่อลดความเสี่ยงจาก decompression bomb เอาต์พุตที่มีขนาดเกินจะโยนข้อยกเว้นแทนการจัดสรรหน่วยความจำโดยไม่มีขีดจำกัด เมื่อ PdfReader อ่านสตรีมแล้วการถอดรหัสโยนข้อยกเว้น ตัวอ่านจะถอยกลับไปใช้ไบต์สตรีมดิบ ดังนั้นตัวกรองที่ไม่ถูกต้องหนึ่งตัวจะไม่ทำให้การแยกวิเคราะห์ทั้งหมดถูกยกเลิก
ResourceCollector — การท่องทรัพยากรเชิงลึก
หัวข้อที่มีชื่อว่า “ResourceCollector — การท่องทรัพยากรเชิงลึก”ResourceCollector ถูกสร้างด้วย PdfReader และถูกเรียกผ่าน PdfReader::collectPageResources() เมธอด traverse() ของคลาสนี้ท่องไปในค่าแบบเรียกซ้ำ ติดตามการอ้างอิง ['type' => 'ref'] ทุกตัวผ่าน getObject() และบันทึกอ็อบเจกต์ที่แก้ไขแล้วแต่ละตัวหนึ่งครั้งใน array<int, PdfObject> ที่ใช้หมายเลขอ็อบเจกต์เป็นคีย์ คลาสนี้จำกัดความลึกของการเรียกซ้ำและข้ามการอ้างอิงที่แก้ไขไม่ได้อย่างเงียบๆ ดังนั้นการอ้างอิงค้างอยู่หนึ่งรายการจึงให้ผลเป็นการรวบรวมบางส่วนแทนที่จะล้มเหลวอย่างรุนแรง
RevisionExtractor — การอัปเดตทีละส่วนและรีวิชัน
หัวข้อที่มีชื่อว่า “RevisionExtractor — การอัปเดตทีละส่วนและรีวิชัน”PDF ที่ถูกลงนาม ใส่คำอธิบายประกอบ หรือแก้ไขด้วยวิธีอื่นหลังการสร้างจะมีการอัปเดตทีละส่วน การแก้ไขแต่ละครั้งจะผนวกส่วนการอ้างอิงไขว้และ trailer ใหม่ แล้วจบด้วยเครื่องหมาย %%EOF RevisionExtractor ทำงานทั้งหมดผ่านเมธอด static บน PdfReader ที่แยกวิเคราะห์แล้ว:
extractRevision(string $pdfData, PdfReader $reader, int $revision)คืนค่าไฟล์ที่ตัดที่ขอบเขต%%EOFของรีวิชันที่ร้องขอ รีวิชัน0(ใหม่ที่สุด) คืนค่าไฟล์ทั้งหมด ดัชนีที่สูงขึ้นคืนค่าสแนปช็อตที่เก่าลงเรื่อยๆgetRevisionBoundaries(string $pdfData, PdfReader $reader)คืนค่าlist<array{revision, startByte, endByte, sizeBytes}>ที่อธิบายช่วงไบต์ที่แต่ละรีวิชันมีส่วนร่วม
การแยกส่วนนี้เป็นไปโดยตั้งใจ การดึงรีวิชันที่เก่ากว่าจะเปิดเผยเฉพาะอ็อบเจกต์ที่มองเห็นได้จนถึงจุดนั้น ซึ่งช่วยสกัดกั้นการโจมตีแบบ hybrid cross-reference ที่รีวิชันถัดๆไปนิยามอ็อบเจกต์ก่อนหน้าซ้ำ
คู่มือปฏิบัติ: การตรวจสอบรีวิชัน
หัวข้อที่มีชื่อว่า “คู่มือปฏิบัติ: การตรวจสอบรีวิชัน”ขั้นตอนนี้ใช้ตรวจสอบประวัติรีวิชันของ PDF ที่อาจถูกแก้ไขหลังจาก Chrome สร้างขึ้น ตัวอย่างนี้จัดรูปสำหรับการใช้งานจริง: ประกาศ strict types ใช้ type hint ครบถ้วน ตรวจสอบความถูกต้องของอินพุต และจับข้อยกเว้นที่เฉพาะเจาะจงที่สุด
- อ่านไบต์ PDF เข้าสู่หน่วยความจำ และปฏิเสธอินพุตที่ว่างเปล่าก่อนสร้างตัวอ่าน
- สร้าง
\NextPDF\Parser\PdfReaderและเรียกparse() - อ่าน
getRevisionCount()ค่า1หมายถึงไฟล์ที่มีรีวิชันเดียวโดยไม่มีการอัปเดตทีละส่วน - สำหรับแต่ละรีวิชัน ให้อ่าน
RevisionXRefTableของรีวิชันนั้น แล้วตรวจสอบgetActiveObjectCount(),hasRootUpdate()และgetSize() - คำนวณช่วงไบต์ต่อรีวิชันด้วย
RevisionExtractor::getRevisionBoundaries() - จับ
PdfParseExceptionซึ่งเป็นข้อยกเว้นที่เฉพาะเจาะจงที่สุดที่ตัวแยกวิเคราะห์โยน และแสดงข้อความวินิจฉัย
<?php
declare(strict_types=1);
namespace App\Pdf\Diagnostics;
use NextPDF\Artisan\Exception\PdfParseException;use NextPDF\Parser\PdfReader;use NextPDF\Parser\RevisionExtractor;use NextPDF\Parser\RevisionXRefTable;
/** * Inspect the incremental-update history of a PDF file. * * @return list<array{revision: int, activeObjects: int, rootUpdate: bool, size: int, startByte: int, endByte: int, sizeBytes: int}> * * @throws PdfParseException If the file is not a readable PDF. */function inspectRevisions(string $path): array{ $pdfData = \file_get_contents($path);
if ($pdfData === false || $pdfData === '') { throw new PdfParseException("Cannot read PDF bytes from path: {$path}"); }
$reader = new PdfReader($pdfData); $reader->parse();
$boundaries = RevisionExtractor::getRevisionBoundaries($pdfData, $reader); $report = [];
foreach ($reader->getRevisions() as $table) { \assert($table instanceof RevisionXRefTable);
$index = $table->index; $boundary = $boundaries[$index];
$report[] = [ 'revision' => $index, 'activeObjects' => $table->getActiveObjectCount(), 'rootUpdate' => $table->hasRootUpdate(), 'size' => $table->getSize(), 'startByte' => $boundary['startByte'], 'endByte' => $boundary['endByte'], 'sizeBytes' => $boundary['sizeBytes'], ]; }
return $report;}ตัวอ่านจัดเรียงรีวิชันจากใหม่ที่สุด (index0) ไปยังเก่าที่สุด หากต้องการดึงสแนปช็อตที่เก่ากว่าหนึ่งรายการออกมาเป็นไบต์เดี่ยว เช่น เพื่อเปรียบเทียบความแตกต่างของสิ่งที่การแก้ไขเปลี่ยนไป ให้เรียกตัวดึงโดยตรง:
<?php
declare(strict_types=1);
namespace App\Pdf\Diagnostics;
use NextPDF\Artisan\Exception\PdfParseException;use NextPDF\Parser\PdfReader;use NextPDF\Parser\RevisionExtractor;
/** * Extract one revision of a PDF as standalone bytes. * * @throws PdfParseException If the file is unreadable or the revision index is out of range. */function extractRevision(string $pdfData, int $revision): string{ if ($pdfData === '') { throw new PdfParseException('Empty PDF input'); }
$reader = new PdfReader($pdfData); $reader->parse();
// Throws PdfParseException with an "out of range" message for an invalid index. return RevisionExtractor::extractRevision($pdfData, $reader, $revision);}การจัดการความล้มเหลว
หัวข้อที่มีชื่อว่า “การจัดการความล้มเหลว”ความล้มเหลวของตัวแยกวิเคราะห์ทุกครั้งจะปรากฏเป็น NextPDF\Artisan\Exception\PdfParseException ข้อความจะระบุสาเหตุ ใช้ตารางด้านล่างเพื่อจับคู่ส่วนย่อยของข้อความกับขั้นตอนที่ทำให้เกิดขึ้น
| ส่วนย่อยของข้อความ | ขั้นตอน | ความหมาย |
|---|---|---|
missing %PDF- header | PdfReader::parse() | ไบต์เหล่านี้ไม่ใช่ PDF หรืออินพุตถูกตัดทอนที่ตอนต้น |
Cannot find startxref marker / Invalid startxref offset | PdfReader::parse() | ส่วนท้ายของไฟล์เสียหาย หรือพอยน์เตอร์การอ้างอิงไขว้อยู่นอกขอบเขต |
Expected 'xref' keyword / Invalid xref subsection header | CrossRefParser::parseXRefTable() | ตารางอ้างอิงไขว้แบบดั้งเดิมผิดรูปแบบ |
XRef stream ... /Type /XRef / invalid /W array | CrossRefParser::parseXRefStream() | สตรีมอ้างอิงไขว้ขาดรายการ dictionary ที่จำเป็น |
exceeds limit of (จำนวน xref หรือ object-stream) | CrossRefParser / PdfReader | จำนวนที่ถูกปลอมแปลงไปสะดุดการป้องกันการปฏิเสธบริการ |
Unsupported stream filter | StreamDecoder::decode() | สตรีมใช้ตัวกรองที่อยู่นอกชุดที่รองรับ FlateDecode / ASCIIHexDecode / ASCII85Decode |
FlateDecode decompression failed / output exceeds ... bytes limit | StreamDecoder | ข้อมูลที่บีบอัดไม่ถูกต้องหรือขยายเกินเพดาน 16 MiB |
Maximum nesting depth ... exceeded / Keyword exceeds maximum length | PdfTokenizer | โครงสร้างที่ถูกประดิษฐ์ขึ้นหรือผิดปกติไปสะดุดขีดจำกัดของตัวแบ่งโทเคน |
Page index ... not found / out of range in subtree | PdfReader::getPage() | ดัชนีหน้าที่ร้องขอไม่มีอยู่ใน page tree |
Revision index ... out of range | PdfReader / RevisionExtractor | ดัชนีรีวิชันอยู่นอกช่วง 0 ถึง getRevisionCount() - 1 |
เมื่อคุณจับข้อยกเว้น ให้บันทึกข้อความและพาธต้นทาง แล้วจึงโยนซ้ำหรือคืนค่าข้อผิดพลาดที่กำหนดไว้ อย่าทิ้งข้อยกเว้นนั้นไปอย่างเงียบๆ บล็อก catch ที่ว่างเปล่าจะซ่อนข้อมูลสำคัญที่ตัวแยกวิเคราะห์สร้างไว้ให้
<?php
declare(strict_types=1);
namespace App\Pdf\Diagnostics;
use NextPDF\Artisan\Exception\PdfParseException;use NextPDF\Parser\PdfReader;use Psr\Log\LoggerInterface;
/** * Parse a PDF, logging the precise parser-stage message on failure. * * @throws PdfParseException Rethrown after logging so the caller can decide policy. */function parseWithDiagnostics(string $pdfData, LoggerInterface $logger): PdfReader{ if ($pdfData === '') { throw new PdfParseException('Empty PDF input'); }
$reader = new PdfReader($pdfData);
try { $reader->parse(); } catch (PdfParseException $exception) { $logger->error('PDF parse failed', [ 'reason' => $exception->getMessage(), 'bytes' => \strlen($pdfData), ]);
throw $exception; }
return $reader;}ค่าเริ่มต้นที่ปลอดภัย
หัวข้อที่มีชื่อว่า “ค่าเริ่มต้นที่ปลอดภัย”- เรียก
parse()ก่อนเสมอ ตัวเข้าถึงทุกตัวบนPdfReaderสันนิษฐานว่าห่วงโซ่การอ้างอิงไขว้ถูกโหลดแล้ว การเรียกgetObject()หรือgetPage()ก่อนparse()จะคืนค่าที่ไม่มีประโยชน์ - ปฏิบัติต่อตัวแยกวิเคราะห์ในฐานะแบบอ่านอย่างเดียวและถูกกำหนดรูปโดย Chrome มันมุ่งเป้าไปที่ชุดย่อยของไวยากรณ์ PDF ที่
printToPDFของ Chrome สร้างขึ้น PDF ที่เข้ารหัสลับ ตาราง hint แบบ linearized และการอัปเดตทีละส่วนที่ขัดแย้งกันอยู่นอกขอบเขตโดยการออกแบบ อย่าขยายมันให้กลายเป็นเครื่องมือซ่อม PDF เอนกประสงค์ - คงขีดจำกัดด้านความปลอดภัยไว้ เพดานการซ้อนกัน ความยาวคีย์เวิร์ด ขนาด array จำนวนการอ้างอิงไขว้ และการคลายการบีบอัด จำกัดการใช้ทรัพยากรกับอินพุตที่เป็นอันตราย
PdfParseExceptionที่มาจากขีดจำกัดคือผลลัพธ์ที่ถูกต้องสำหรับไฟล์ที่ถูกประดิษฐ์ขึ้น การยกระดับขีดจำกัดเพื่อยอมรับไฟล์เช่นนั้นจะเป็นการขยายพื้นผิวการโจมตี - ตั้งค่าเริ่มต้นเป็นหน้า
0getPage()และPageImporter::import()ตั้งค่าเริ่มต้นเป็นหน้าแรก เลือกดัชนีอื่นเฉพาะเมื่อเวิร์กโฟลว์จำเป็นต้องใช้โดยตั้งใจ - ตรวจสอบความถูกต้องของอินพุตก่อนสร้างตัวอ่าน ปฏิเสธไบต์ที่ว่างเปล่าหรืออ่านไม่ได้ตั้งแต่เนิ่นๆ เช่นเดียวกับตัวอย่างข้างต้น เพื่อให้ข้อผิดพลาดระดับแอปพลิเคชันที่ชัดเจนปรากฏก่อนข้อยกเว้นใดๆของตัวแยกวิเคราะห์
- จับ
PdfParseExceptionไม่ใช่\Exceptionเปล่าๆ เป็นชนิดเดียวที่เฉพาะเจาะจงซึ่งตัวแยกวิเคราะห์โยน การจับชนิดนี้ช่วยป้องกันไม่ให้ความล้มเหลวที่ไม่เกี่ยวข้องถูกบดบัง
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- คู่มือนักพัฒนาของ Artisan — ขอบเขตการนำเข้าที่อยู่เหนือตัวแยกวิเคราะห์ รวมถึง
ChromeHtmlRenderer,PageImporterและเลเยอร์สถาปัตยกรรม - เอกสารอ้างอิง API ของ Artisan — ตารางเมธอดที่เผยแพร่สำหรับพื้นผิวสาธารณะของแพ็กเกจ
- การแก้ไขปัญหาของ Artisan — แนวทางแบบเริ่มจากอาการสำหรับความล้มเหลวของเรนเดอเรอร์และการนำเข้า
- การตั้งค่าเรนเดอเรอร์ Chrome — การกำหนดค่าเรนเดอเรอร์ที่สร้าง PDF ที่ตัวแยกวิเคราะห์นี้อ่าน
- ISO 32000-2:2020 §7.5 (โครงสร้างไฟล์ การอ้างอิงไขว้ การอัปเดตทีละส่วน) และ §7.2 (ข้อกำหนดเชิงคำศัพท์) — ข้อกำหนดที่ตัวแบ่งโทเคนและตัวแยกวิเคราะห์การอ้างอิงไขว้นำมาใช้ โปรดศึกษามาตรฐานที่เผยแพร่เพื่อดูรูปแบบระดับไบต์ที่เป็นแหล่งอ้างอิงที่เชื่อถือได้