Skip to content

Commit cc984c1

Browse files
committed
fix(integration): align rebuilt PR809 with validated best-result branch
1 parent 32b16cf commit cc984c1

5 files changed

Lines changed: 48 additions & 197 deletions

File tree

-871 Bytes
Binary file not shown.

src/Smalot/PdfParser/RawData/RawDataParser.php

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,13 +1427,8 @@ public function parseData(string $data): array
14271427
throw new EmptyPdfException('Empty PDF data given.');
14281428
}
14291429
// find the pdf header starting position
1430-
$trimpos = strpos($data, '%PDF-');
1431-
if (false === $trimpos) {
1432-
if (!$this->hasRecoverablePdfStructureWithoutHeader($data)) {
1433-
throw new MissingPdfHeaderException('Invalid PDF data: Missing `%PDF-` header.');
1434-
}
1435-
1436-
$trimpos = 0;
1430+
if (false === ($trimpos = strpos($data, '%PDF-'))) {
1431+
throw new MissingPdfHeaderException('Invalid PDF data: Missing `%PDF-` header.');
14371432
}
14381433

14391434
// Keep the original byte layout to preserve absolute xref offsets.
@@ -1483,23 +1478,4 @@ public function parseData(string $data): array
14831478

14841479
return [$xref, $objects];
14851480
}
1486-
1487-
private function hasRecoverablePdfStructureWithoutHeader(string $data): bool
1488-
{
1489-
if (
1490-
preg_match('/(?:^|[\r\n])[0-9]+[\x09\x0a\x0c\x0d\x20]+[0-9]+[\x09\x0a\x0c\x0d\x20]+obj\b/i', $data) === 0
1491-
) {
1492-
return false;
1493-
}
1494-
1495-
if (preg_match('/\btrailer\b/i', $data) === 0) {
1496-
return false;
1497-
}
1498-
1499-
if (preg_match('/\bstartxref\b/i', $data) === 0 && preg_match('/\bxref\b/i', $data) === 0) {
1500-
return false;
1501-
}
1502-
1503-
return true;
1504-
}
15051481
}

tests/PHPUnit/Integration/DocumentIssueFocusTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
namespace PHPUnitTests\Integration;
3737

3838
use PHPUnitTests\TestCase;
39+
use Smalot\PdfParser\Config;
3940
use Smalot\PdfParser\Document;
4041
use Smalot\PdfParser\Parser;
4142

@@ -111,6 +112,19 @@ public function testPDFDocEncodingDecode(): void
111112
$testSubject = '•†‡…—–ƒ⁄‹›−‰„“”‘’‚™ŁŒŠŸŽıłœšž';
112113
self::assertStringContainsString($testSubject, $details['Subject']);
113114
}
115+
/**
116+
* @see https://github.com/smalot/pdfparser/pull/795
117+
* @see https://github.com/smalot/pdfparser/blob/master/samples/bugs/PullRequestDuplicateKids.pdf
118+
*/
119+
public function testGetPagesDeduplicatesDuplicateKidsFixture(): void
120+
{
121+
$document = (new Parser())->parseFile($this->rootDir.'/samples/bugs/PullRequestDuplicateKids.pdf');
122+
123+
$pages = $document->getPages();
124+
125+
self::assertCount(1, $pages);
126+
}
127+
114128
public function testRecoverPagesWhenXrefEntriesArePartiallyMissing(): void
115129
{
116130
$document = (new Parser())->parseFile($this->rootDir.'/samples/bugs/PullRequest813-pdf.js.pdf');
@@ -124,4 +138,17 @@ public function testRecoverPagesWhenRootOffsetPointsToInvalidObject(): void
124138

125139
self::assertCount(1, $document->getPages());
126140
}
141+
142+
/**
143+
* @group linux-only
144+
*/
145+
public function testParseFileWithLargeFlateStreams(): void
146+
{
147+
$config = new Config();
148+
$config->setRetainImageContent(false);
149+
$config->setDecodeMemoryLimit(8 * 1024 * 1024);
150+
$document = (new Parser([], $config))->parseFile($this->rootDir.'/samples/bugs/PullRequest457.pdf');
151+
152+
self::assertCount(28, $document->getPages());
153+
}
127154
}

tests/PHPUnit/Integration/DocumentTest.php

Lines changed: 9 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
*
66
* @author Konrad Abicht <k.abicht@gmail.com>
77
*
8+
* @date 2020-06-01
9+
*
10+
* @author Sébastien MALOT <sebastien@malot.fr>
811
*
912
* @date 2017-01-03
10-
11-
self::assertSame([], $document->getPages());
1213
*
1314
* @license LGPLv3
1415
*
@@ -39,7 +40,6 @@
3940
use Smalot\PdfParser\Header;
4041
use Smalot\PdfParser\Page;
4142
use Smalot\PdfParser\Pages;
42-
use Smalot\PdfParser\Parser;
4343
use Smalot\PdfParser\PDFObject;
4444

4545
/**
@@ -225,12 +225,15 @@ public function testGetPages(): void
225225

226226
public function testGetPagesMissingCatalog(): void
227227
{
228+
$this->expectException(\Exception::class);
229+
$this->expectExceptionMessage('Missing catalog.');
230+
228231
// Missing catalog
229232
$document = $this->getDocumentInstance();
230-
$this->assertSame([], $document->getPages());
233+
$document->getPages();
231234
}
232235

233-
public function testGetPagesPreservesDuplicateKidsReferences(): void
236+
public function testGetPagesDeduplicatesDuplicateKidsReferences(): void
234237
{
235238
$document = $this->getDocumentInstance();
236239

@@ -254,118 +257,8 @@ public function testGetPagesPreservesDuplicateKidsReferences(): void
254257

255258
$pages = $document->getPages();
256259

257-
$this->assertCount(2, $pages);
258-
$this->assertSame($page, $pages[0]);
259-
$this->assertSame($page, $pages[1]);
260-
}
261-
262-
/**
263-
* Synthetic fixture created in-repo to reproduce duplicate /Kids references.
264-
*/
265-
public function testGetPagesPreservesDuplicateKidsFixture(): void
266-
{
267-
$document = (new Parser())->parseFile($this->rootDir.'/samples/bugs/PullRequestDuplicateKids.pdf');
268-
269-
$pages = $document->getPages();
270-
271-
$this->assertCount(2, $pages);
272-
}
273-
274-
public function testGetPagesRecoversWhenCatalogPagesReferenceIsMissing(): void
275-
{
276-
$document = $this->getDocumentInstance();
277-
278-
$catalogHeader = Header::parse('<</Type/Catalog/Pages 56 0 R>>', $document);
279-
$catalog = $this->getPDFObjectInstance($document, $catalogHeader);
280-
281-
$document->setObjects([
282-
'1_0' => $catalog,
283-
]);
284-
285-
$pages = $document->getPages();
286-
287-
$this->assertCount(1, $pages);
288-
$this->assertTrue($pages[0] instanceof Page);
289-
}
290-
291-
public function testGetPagesRecoversWhenCatalogPagesNodeIsNotPagesType(): void
292-
{
293-
$document = $this->getDocumentInstance();
294-
295-
$catalogHeader = Header::parse('<</Type/Catalog/Pages 2 0 R>>', $document);
296-
$catalog = $this->getPDFObjectInstance($document, $catalogHeader);
297-
298-
// Referenced node exists but is not a Pages object.
299-
$invalidPagesHeader = Header::parse('<</Length 10>>', $document);
300-
$invalidPages = $this->getPDFObjectInstance($document, $invalidPagesHeader);
301-
302-
$document->setObjects([
303-
'1_0' => $catalog,
304-
'2_0' => $invalidPages,
305-
]);
306-
307-
$pages = $document->getPages();
308-
309-
$this->assertCount(1, $pages);
310-
$this->assertTrue($pages[0] instanceof Page);
311-
}
312-
313-
public function testGetPagesRecoversBrokenPagesTreeWithPositiveCount(): void
314-
{
315-
$document = $this->getDocumentInstance();
316-
317-
$pagesHeader = Header::parse('<</Type/Pages/Kids[99 0 R]/Count 5>>', $document);
318-
$pagesNode = $this->getPagesInstance($document, $pagesHeader);
319-
320-
$catalogHeader = Header::parse('<</Type/Catalog/Pages 2 0 R>>', $document);
321-
$catalog = $this->getPDFObjectInstance($document, $catalogHeader);
322-
323-
$document->setObjects([
324-
'1_0' => $catalog,
325-
'2_0' => $pagesNode,
326-
]);
327-
328-
$pages = $document->getPages();
329-
330-
$this->assertCount(1, $pages);
331-
$this->assertTrue($pages[0] instanceof Page);
332-
}
333-
334-
public function testGetPagesRecoversMinimalHeaderlessStructure(): void
335-
{
336-
$document = $this->getDocumentInstance();
337-
338-
$object1 = $this->getPDFObjectInstance($document, Header::parse('<<>>', $document));
339-
$object2 = $this->getPDFObjectInstance($document, Header::parse('<<>>', $document));
340-
341-
$document->setObjects([
342-
'1_0' => $object1,
343-
'2_0' => $object2,
344-
]);
345-
346-
$pages = $document->getPages();
347-
348260
$this->assertCount(1, $pages);
349-
$this->assertTrue($pages[0] instanceof Page);
350-
}
351-
352-
public function testGetPagesDoesNotRecoverWhenMinimalStructureIsNotHeaderless(): void
353-
{
354-
$document = $this->getDocumentInstance();
355-
356-
// Guard case similar to corrupted-xref files: no catalog, but headers do
357-
// contain meaningful keys, so minimal fallback must not trigger.
358-
$object1 = $this->getPDFObjectInstance($document, Header::parse('<</Pages 2 0 R/Contents 3 0 R>>', $document));
359-
$object2 = $this->getPDFObjectInstance($document, Header::parse('<</Length 10>>', $document));
360-
$object3 = $this->getPDFObjectInstance($document, Header::parse('<</Length 20>>', $document));
361-
362-
$document->setObjects([
363-
'1_0' => $object1,
364-
'2_0' => $object2,
365-
'3_0' => $object3,
366-
]);
367-
368-
$this->assertSame([], $document->getPages());
261+
$this->assertSame($page, $pages[0]);
369262
}
370263

371264
public static function provideRealWorldPageRecoveryCases(): array
@@ -393,46 +286,6 @@ public function testGetPagesOnRealWorldMalformedFixtures(string $fixture, int $e
393286
$this->assertCount($expectedPages, $document->getPages());
394287
}
395288

396-
public function testGetPagesDeduplicatesDuplicateKidsReferences(): void
397-
{
398-
$document = $this->getDocumentInstance();
399-
400-
$content = '<</Type/Page>>';
401-
$header = Header::parse($content, $document);
402-
$page = $this->getPageInstance($document, $header);
403-
404-
$content = '<</Type/Pages/Kids[10 0 R 10 0 R]>>';
405-
$header = Header::parse($content, $document);
406-
$pagesNode = $this->getPagesInstance($document, $header);
407-
408-
$content = '<</Type/Catalog/Pages 20 0 R>>';
409-
$header = Header::parse($content, $document);
410-
$catalog = $this->getPDFObjectInstance($document, $header);
411-
412-
$document->setObjects([
413-
'10_0' => $page,
414-
'20_0' => $pagesNode,
415-
'30_0' => $catalog,
416-
]);
417-
418-
$pages = $document->getPages();
419-
420-
$this->assertCount(1, $pages);
421-
$this->assertSame($page, $pages[0]);
422-
}
423-
424-
/**
425-
* Synthetic fixture created in-repo to reproduce duplicate /Kids references.
426-
*/
427-
public function testGetPagesDeduplicatesDuplicateKidsFixture(): void
428-
{
429-
$document = (new Parser())->parseFile($this->rootDir.'/samples/bugs/PullRequestDuplicateKids.pdf');
430-
431-
$pages = $document->getPages();
432-
433-
$this->assertCount(1, $pages);
434-
}
435-
436289
/**
437290
* @see https://github.com/smalot/pdfparser/issues/721
438291
*/

tests/PHPUnit/Integration/ParserTest.php

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,14 @@ public function testRetainImageContentImpact(): void
413413
public function testNoIgnoreEncryption(): void
414414
{
415415
$filename = $this->rootDir.'/samples/not_really_encrypted.pdf';
416-
417-
$this->assertTrue((new Parser([]))->parseFile($filename) instanceof Document);
416+
$threw = false;
417+
try {
418+
(new Parser([]))->parseFile($filename);
419+
} catch (\Exception $e) {
420+
// we expect an exception to be thrown if an encrypted PDF is encountered.
421+
$threw = true;
422+
}
423+
$this->assertTrue($threw);
418424
}
419425

420426
/**
@@ -430,6 +436,8 @@ public function testIgnoreEncryption(): void
430436
$filename = $this->rootDir.'/samples/not_really_encrypted.pdf';
431437

432438
$this->assertTrue((new Parser([], $config))->parseFile($filename) instanceof Document);
439+
440+
// without the configuration option set, an exception would be thrown.
433441
}
434442

435443
/**
@@ -443,19 +451,6 @@ public function testPullRequest793ChrDeprecationFix(): void
443451

444452
$this->assertEquals('ASCII85 last-tuple overflow test', $document->getText());
445453
}
446-
447-
/**
448-
* @group linux-only
449-
*/
450-
public function testParseFileWithLargeFlateStreams(): void
451-
{
452-
$config = new Config();
453-
$config->setRetainImageContent(false);
454-
$config->setDecodeMemoryLimit(8 * 1024 * 1024);
455-
$document = (new Parser([], $config))->parseFile($this->rootDir.'/samples/bugs/PullRequest457.pdf');
456-
457-
self::assertCount(28, $document->getPages());
458-
}
459454
}
460455

461456
class ParserSub extends Parser

0 commit comments

Comments
 (0)