Skip to content

Commit ff2abc5

Browse files
authored
Merge pull request #22 from vitormattos/sync/pr811-into-integration
sync: bring PR811 fix into integration/pdfjs-fixes
2 parents fc987b7 + 79662fa commit ff2abc5

3 files changed

Lines changed: 52 additions & 0 deletions

File tree

862 Bytes
Binary file not shown.

src/Smalot/PdfParser/RawData/RawDataParser.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,41 @@ protected function getObjectHeaderLen(array $objRefs): int
585585
return 5 + \strlen($objRefs[0]) + \strlen($objRefs[1]);
586586
}
587587

588+
/**
589+
* Merge missing xref offsets by scanning object headers directly in the PDF body.
590+
*
591+
* This is a recovery path for malformed xref streams where trailer references
592+
* (for example /Root) are present but corresponding xref entries are missing.
593+
*/
594+
private function mergeMissingXrefOffsetsFromObjectHeaders(string $pdfData, array $xref): array
595+
{
596+
if (!isset($xref['xref']) || !\is_array($xref['xref'])) {
597+
$xref['xref'] = [];
598+
}
599+
600+
if (
601+
preg_match_all(
602+
'/(?:^|[\r\n])([0-9]+)[\x09\x0a\x0c\x0d\x20]+([0-9]+)[\x09\x0a\x0c\x0d\x20]+obj(?=[\x09\x0a\x0c\x0d\x20<])/i',
603+
$pdfData,
604+
$matches,
605+
\PREG_OFFSET_CAPTURE
606+
) > 0
607+
) {
608+
foreach ($matches[1] as $idx => $objMatch) {
609+
$objNum = $objMatch[0];
610+
$offset = $objMatch[1];
611+
$genNum = $matches[2][$idx][0];
612+
$objRef = $objNum.'_'.$genNum;
613+
614+
if (!isset($xref['xref'][$objRef])) {
615+
$xref['xref'][$objRef] = $offset;
616+
}
617+
}
618+
}
619+
620+
return $xref;
621+
}
622+
588623
/**
589624
* Get content of indirect object.
590625
*
@@ -1287,6 +1322,11 @@ public function parseData(string $data): array
12871322
$xref = $this->getXrefData($pdfData);
12881323
}
12891324

1325+
$rootObjectRef = $xref['trailer']['root'] ?? null;
1326+
if (\is_string($rootObjectRef) && !isset($xref['xref'][$rootObjectRef])) {
1327+
$xref = $this->mergeMissingXrefOffsetsFromObjectHeaders($pdfData, $xref);
1328+
}
1329+
12901330
// parse all document objects
12911331
$objects = [];
12921332
foreach ($xref['xref'] as $obj => $offset) {

tests/PHPUnit/Integration/DocumentIssueFocusTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,16 @@ public function testParseFileWithLargeFlateStreams(): void
237237

238238
self::assertCount(28, $document->getPages());
239239
}
240+
241+
/**
242+
* Ensures malformed xref streams with missing /Root xref entries still recover pages.
243+
*
244+
* @see https://github.com/mozilla/pdf.js/blob/master/test/pdfs/issue18986.pdf
245+
*/
246+
public function testMalformedXrefStreamMissingRootEntryStillParsesPage(): void
247+
{
248+
$document = (new Parser())->parseFile($this->rootDir.'/samples/bugs/PullRequest812-pdf.js.pdf');
249+
250+
self::assertCount(1, $document->getPages());
251+
}
240252
}

0 commit comments

Comments
 (0)