Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryKey\DictionaryKey;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Integer\IntegerValue;
use PrinsFrank\PdfParser\Document\Document;
use PrinsFrank\PdfParser\Document\Filter\Decode\ASCII85Decode;
use PrinsFrank\PdfParser\Document\Filter\Decode\CCITTFaxDecode;
use PrinsFrank\PdfParser\Document\Filter\Decode\FlateDecode;
use PrinsFrank\PdfParser\Document\Filter\Decode\LZWFlatePredictorValue;
Expand Down Expand Up @@ -54,6 +55,7 @@ public function decodeBinary(string $content, ?Dictionary $dictionary, ?Document
$decodeParams->getValueForKey(DictionaryKey::K, IntegerValue::class)->value
?? throw new ParseFailureException('Missing K'),
),
self::ASCII_85_DECODE => ASCII85Decode::decodeBinary($content),
default => throw new ParseFailureException(sprintf('Content "%.100s..." cannot be decoded for filter "%s"', $content, $this->name))
};
}
Expand Down
13 changes: 11 additions & 2 deletions src/Document/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PrinsFrank\PdfParser\Document\Object\Item\UncompressedObject\UncompressedObjectParser;
use PrinsFrank\PdfParser\Document\Security\StandardSecurity;
use PrinsFrank\PdfParser\Document\Version\Version;
use PrinsFrank\PdfParser\Exception\AuthenticationFailedException;
use PrinsFrank\PdfParser\Exception\NotImplementedException;
use PrinsFrank\PdfParser\Exception\ParseFailureException;
use PrinsFrank\PdfParser\Exception\PdfParserException;
Expand All @@ -40,8 +41,16 @@ public function __construct(
public readonly CrossReferenceSource $crossReferenceSource,
public ?StandardSecurity $security,
) {
if ($this->getEncryptDictionary() !== null) {
throw new NotImplementedException('Encrypted documents are not supported yet');
if (($encryptDictionary = $this->getEncryptDictionary()) !== null) {
if ($encryptDictionary->getSecurityHandler() === null) {
throw new NotImplementedException('Empty security handler is not supported');
}

$this->security ??= new StandardSecurity();
if ($this->security->isUserPasswordValid($encryptDictionary, $crossReferenceSource->getFirstId()) === false
&& $this->security->isOwnerPasswordValid($encryptDictionary, $crossReferenceSource->getFirstId()) === false) {
throw new AuthenticationFailedException($security === null ? 'Document could not be decrypted using default credentials, please supply an owner or user password' : 'User and owner password are invalid, please supply valid credentials');
}
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/Document/Filter/Decode/ASCII85Decode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php declare(strict_types=1);

namespace PrinsFrank\PdfParser\Document\Filter\Decode;

use PrinsFrank\PdfParser\Exception\RuntimeException;

class ASCII85Decode {
/** @throws RuntimeException */
public static function decodeBinary(string $string): string {
file_put_contents(__DIR__ . '/ascii85.txt', $string);
$string = trim($string);
if (str_starts_with($string, '<~') && str_ends_with($string, '~>')) {
$string = substr($string, 2, -2);
}

$string = preg_replace('/\s+/', '', $string)
?? throw new RuntimeException('An unexpected error occurred while sanitizing ASCII85 string');
$length = strlen($string);
$decoded = $block = '';
for ($i = 0; $i < $length; ++$i) {
$char = $string[$i];
if ($char === 'z') {
$decoded .= "\0\0\0\0";
continue;
}

$block .= $char;
if (strlen($block) === 5) {
$value = 0;
for ($j = 0; $j < 5; ++$j) {
$value = $value * 85 + (ord($block[$j]) - 33);
}

$decoded .= pack('N', $value);
$block = '';
}
}

if ($block !== '') {
$padding = 5 - strlen($block);
$block = str_pad($block, 5, 'u');
$value = 0;
for ($i = 0; $i < 5; ++$i) {
$value = $value * 85 + (ord($block[$i]) - 33);
}

$binaryData = pack('N', $value);
$decoded .= substr($binaryData, 0, 4 - $padding);
}

return $decoded;
}
}
3 changes: 2 additions & 1 deletion tests/Samples/Info/FileInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class FileInfo {
public function __construct(
public readonly string $pdfPath,
public readonly int $version,
public readonly ?string $password,
public readonly ?string $userPassword,
public readonly ?string $ownerPassword,
public readonly ?string $title,
public readonly ?string $producer,
public readonly ?string $author,
Expand Down
9 changes: 3 additions & 6 deletions tests/Samples/Info/SampleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,14 @@ public static function samples(): iterable {
throw new RuntimeException();
}

/** @var object{version: float, password: ?string, title: ?string, producer: ?string, author: ?string, creator: ?string, creationDate: ?\DateTimeImmutable, modificationDate: ?\DateTimeImmutable, pages: list<object{content: string, images?: string[]}>} $content */
/** @var object{version: float, userPassword: ?string, ownerPassword: ?string, title: ?string, producer: ?string, author: ?string, creator: ?string, creationDate: ?\DateTimeImmutable, modificationDate: ?\DateTimeImmutable, pages: list<object{content: string, images?: string[]}>} $content */
$content = Yaml::parseFile($contentsPath, Yaml::PARSE_OBJECT_FOR_MAP | Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE | Yaml::PARSE_DATETIME);
if ($content->password !== null) {
continue;
}

yield $sampleName => [
new FileInfo(
$pdfPath,
(int) ($content->version * 10),
$content->password,
$content->userPassword,
$content->ownerPassword,
$content->title,
$content->producer,
$content->author,
Expand Down
7 changes: 5 additions & 2 deletions tests/Samples/SamplesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProviderExternal;
use PHPUnit\Framework\TestCase;
use PrinsFrank\PdfParser\Document\Security\StandardSecurity;
use PrinsFrank\PdfParser\Document\Version\Version;
use PrinsFrank\PdfParser\PdfParser;
use PrinsFrank\PdfParser\Tests\Samples\Info\FileInfo;
Expand All @@ -18,8 +19,10 @@
class SamplesTest extends TestCase {
/** @throws TypeError|ValueError|RuntimeException */
#[DataProviderExternal(SampleProvider::class, 'samples')]
public function testExternalSourcePDFs(FileInfo $fileInfo): void {
$document = (new PdfParser())->parseFile($fileInfo->pdfPath);
public function testSamples(FileInfo $fileInfo): void {
$document = (new PdfParser())
->parseFile($fileInfo->pdfPath, security: new StandardSecurity($fileInfo->userPassword, $fileInfo->ownerPassword));

static::assertSame(Version::from(number_format($fileInfo->version / 10, 1)), $document->version);
static::assertSame($fileInfo->title, $document->getInformationDictionary()?->getTitle());
static::assertSame($fileInfo->producer, $document->getInformationDictionary()?->getProducer());
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/gdocs-hello-world-simple/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.4'
password: null
userPassword: null
ownerPassword: null
title: 'Untitled document'
producer: 'Skia/PDF m133 Google Docs Renderer'
author: null
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/gdocs-image-simple/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.4'
password: null
userPassword: null
ownerPassword: null
title: 'File with image'
producer: 'Skia/PDF m138 Google Docs Renderer'
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.4'
password: null
userPassword: null
ownerPassword: null
title: 'lorem ipsum'
producer: 'Skia/PDF m133 Google Docs Renderer'
author: null
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/gdocs-scripts/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.4'
password: null
userPassword: null
ownerPassword: null
title: Scripts
producer: 'Skia/PDF m134 Google Docs Renderer'
author: null
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-127/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '2.0'
password: null
userPassword: null
ownerPassword: null
title: !!binary /v8AMQAuADIAIABUAGEAYgBlAGwAbABlAF8AMQBfAEsAQQBfADEAOQAtADAANwAwADUAOQ==
producer: 'Kofax Power PDF'
author: 'Rehm, Martin (LSN)'
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-149/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: '(Dossier représentation Mme BEGOT-3.pdf)'
producer: 'GPL Ghostscript 10.03.1'
author: mgomes03
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-150/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.3'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'RICOH M C251FW (6577f3)'
author: null
Expand Down
81 changes: 81 additions & 0 deletions tests/Samples/files/issue-153/contents.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# yaml-language-server: $schema=./.yml-schema.json
version: '1.4'
password: null
title: reception0658@reseau-intersport.fr
producer: 'Oracle PDF driver'
author: 'Oracle Reports'
creator: 'Oracle12c AS Reports Services'
creationDate: 2025-07-22T10:00:03+00:00
modificationDate: 2025-07-22T10:00:03+00:00
pages:
-
content: |-
CUSTOMER IE802719
INTERSPORT PAIMPOL
SASU C.E.F SPORTS - 17 T RUE RAYMOND PELLIER
22500 PAIMPOL France
CHAMPION PRODUCTS EUROPE UNLIMITED COMPANY
Blanchardstown Corporate Park 2 D15 Blanchardstown - Dublin Ireland
Tel. Acc.
+353 (0)1 8864217 +353 (0)1 8864217Fax +353 (0)1 8665927
Tel. CS
IE802719
DESTINATION
INTERSPORT PAIMPOL
ZONE DE KERPUNS CARREFOUR ILN Code: 3025810065800
22500 PAIMPOL France
DELIVERY NOTE
DC REF.
5204/165/2025/DN/5204 21/07/2025
FORWARDER
Label
UPS - United Parcel Service S.r.l 2.0
VIA G. FANTOLI, 15/2
REASON
20138 MILANO Milano
Italy
Subcontractor
TRANSPORTATION COST
Delivery Note N
CHH/2025/70BO/058510DATE 21/07/2025
COMMENTS
SALES REP VORACE SAS François RAMOND
ARTICLE DESCRIPTION COLOUR SIZE TOTAL
ORD. NUM.
35/38 39/42 43/46
8 19 13
4402026619
3pk Sneaker Socks
U20100-CHA
WW001 WHT
40
40
35/38 39/42 43/46
8 17 14
4402026619
3pk Crew Socks
U24558-CHA
KK001 NBK
39
8 17 14
4402026619
WW001 WHT
39
78
Tot. qty 118
Cartons Summary
250570819669 250570830162
Picking List-N.
CHH-2025-VE-430761
Packing list W2025/PL/CHH282537
Leave from
PACKING Cartons Nr 2 Net Wght 16,790 Gross Wght 18,490
LABEL NOTES
Label
FORWARDER DESTINATION
CIE
Signature Date Time Signature Date Time Signature Date Time
1 of
Copy for Customer Page 1
images:
- page_0_image_0.jpg
Binary file added tests/Samples/files/issue-153/file.pdf
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-214/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'PStill Engine 1.90'
author: null
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-235/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.6'
password: null
userPassword: null
ownerPassword: null
title: ''
producer: 'Adobe PDF Library 20.1.50'
author: null
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-254/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: 'Publications Office'
producer: 'PDFlib+PDI 9.1.2p4 (C++/Win64)'
author: 'Publications Office'
Expand Down
5 changes: 3 additions & 2 deletions tests/Samples/files/issue-255/contents.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'PStill Engine 1.90'
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'LibreOffice 24.2'
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: 1.7
password: Hello
userPassword: hello
ownerPassword: null
title: null
producer: LibreOffice 24.2
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'LibreOffice 24.2'
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'LibreOffice 24.2'
author: null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=./.yml-schema.json
# yaml-language-server: $schema=../../schema.json
version: '1.7'
password: null
userPassword: null
ownerPassword: null
title: null
producer: 'LibreOffice 24.2'
author: null
Expand Down
Loading
Loading