Skip to content

Commit

Permalink
chore: use pdfinfo as fallback to get page dimension
Browse files Browse the repository at this point in the history
Signed-off-by: Vitor Mattos <[email protected]>
  • Loading branch information
vitormattos committed Jan 25, 2025
1 parent 82d217f commit f0cffa5
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 16 deletions.
3 changes: 3 additions & 0 deletions lib/Helper/ConfigureCheckHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
use JsonSerializable;

/**
* @method ConfigureCheckHelper setSuccessMessage(string $value)
* @method ConfigureCheckHelper setInfoMessage(string $value)
* @method ConfigureCheckHelper setErrorMessage(string $value)
* @method ConfigureCheckHelper setStatus(string $value)
* @method string getStatus()
* @method ConfigureCheckHelper setMessage(string $value)
Expand Down
51 changes: 46 additions & 5 deletions lib/Service/Install/ConfigureCheckService.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,17 @@ public function checkSign(): array {
}

public function checkPoppler(): array {
$return = $this->checkPdfSig();
$return = array_merge($return, $this->checkPdfinfo());
return $return;
}

public function checkPdfSig(): array {
if (shell_exec('which pdfsig') === null) {
return [
(new ConfigureCheckHelper())
->setInfoMessage('Poppler utils not installed')
->setResource('poppler-utils')
->setResource('pdfsig')
->setTip('Install the package poppler-utils at your operational system to be possible get more details about validation of signatures.'),
];
}
Expand All @@ -96,17 +102,17 @@ public function checkPoppler(): array {
if (!$version) {
return [
(new ConfigureCheckHelper())
->setInfoMessage('Fail to retrieve pdfsig version')
->setResource('poppler-utils')
->setErrorMessage('Fail to retrieve pdfsig version')
->setResource('pdfsig')
->setTip("The command <pdfsig -v> executed by PHP haven't any output."),
];
}
$version = preg_match('/pdfsig version (?<version>.*)/', $version, $matches);
if (!$version) {
return [
(new ConfigureCheckHelper())
->setInfoMessage('Fail to retrieve pdfsig version')
->setResource('poppler-utils')
->setErrorMessage('Fail to retrieve pdfsig version')
->setResource('pdfsig')
->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfsig -v"),
];
}
Expand All @@ -116,6 +122,41 @@ public function checkPoppler(): array {
];
}

public function checkPdfinfo(): array {
if (shell_exec('which pdfinfo') === null) {
return [
(new ConfigureCheckHelper())
->setInfoMessage('Poppler utils not installed')
->setResource('pdfinfo')
->setTip('Install the package poppler-utils at your operational system have a fallback to fetch page dimensions.'),
];
}
// The output of this command go to STDERR and shell_exec get the STDOUT
// With 2>&1 the STRERR is redirected to STDOUT
$version = shell_exec('pdfinfo -v 2>&1');
if (!$version) {
return [
(new ConfigureCheckHelper())
->setErrorMessage('Fail to retrieve pdfinfo version')
->setResource('pdfinfo')
->setTip("The command <pdfinfo -v> executed by PHP haven't any output."),
];
}
$version = preg_match('/pdfinfo version (?<version>.*)/', $version, $matches);
if (!$version) {
return [
(new ConfigureCheckHelper())
->setErrorMessage('Fail to retrieve pdfinfo version')
->setResource('pdfinfo')
->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfinfo -v"),
];
}
return [(new ConfigureCheckHelper())
->setSuccessMessage('pdfinfo version: ' . $matches['version'])
->setResource('pdfinfo')
];
}

/**
* Check all requirements to use JSignPdf
*
Expand Down
66 changes: 56 additions & 10 deletions lib/Service/PdfParserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,19 @@ public function setFile(File|string $file): self {
return $this;
}

private function getContent(): string {
if (!$this->content) {
throw new LibresignException('File not defined to be parsed.');
}
return $this->content;
}

private function getDocument(): Document {
if (!$this->document) {
if (!$this->content) {
throw new LibresignException('File not defined to be parsed.');
}
$content = $this->getContent();
try {
$parser = new \Smalot\PdfParser\Parser();
$this->document = $parser->parseContent($this->content);
$this->document = $parser->parseContent($content);
return $this->document;
} catch (\Throwable $th) {
if ($th->getMessage() === 'Secured pdf file are currently not supported.') {
Expand All @@ -83,6 +88,13 @@ private function getDocument(): Document {
* @psalm-return array{p: int, d?: non-empty-list<array{w: mixed, h: mixed}>}
*/
public function getPageDimensions(): array {
if ($return = $this->getPageDimensionsWithPdfInfo()) {
return $return;
}
return $this->getPageDimensionsWithSmalotPdfParser();
}

private function getPageDimensionsWithSmalotPdfParser(): array {
$document = $this->getDocument();
$pages = $document->getPages();
$output = [
Expand All @@ -94,15 +106,49 @@ public function getPageDimensions(): array {
$pages = $document->getObjectsByType('Pages');
$details = reset($pages)->getHeader()->getDetails();
}
$widthAndHeight = [
'w' => $details['MediaBox'][2],
'h' => $details['MediaBox'][3]
];
if (!is_numeric($widthAndHeight['w']) || !is_numeric($widthAndHeight['h'])) {
if (!isset($details['MediaBox']) || !is_numeric($details['MediaBox'][2]) || !is_numeric($details['MediaBox'][3])) {
$this->logger->error('Impossible get metadata from this file: Error to get page width and height. If possible, open an issue at github.com/libresign/libresign with the file that you used.');
throw new LibresignException('Impossible get metadata from this file.');
}
$output['d'][] = $widthAndHeight;
$output['d'][] = [
'w' => $details['MediaBox'][2],
'h' => $details['MediaBox'][3],
];
}
$pending = $output['p'] - count($output['d']);
if ($pending) {
for ($i = 0; $i < $pending; $i++) {
$output['d'][] = $output['d'][0];
}
}
return $output;
}

private function getPageDimensionsWithPdfInfo(): array {
if (shell_exec('which pdfinfo') === null) {
return [];
}
$content = $this->getContent();
$filename = $this->tempManager->getTemporaryFile('.pdf');
file_put_contents($filename, $content);

// The output of this command go to STDERR and shell_exec get the STDOUT
// With 2>&1 the STRERR is redirected to STDOUT
$pdfinfo = shell_exec('pdfinfo ' . $filename . ' -l -1 2>&1');
if (!$pdfinfo) {
return [];
}
if (!preg_match_all('/Page +\d+ +size: +(\d+\.?\d*) x (\d+\.?\d*)/', $pdfinfo, $pages)) {
return [];
}
$output = [
'p' => count($pages[1]),
];
foreach ($pages[1] as $page => $width) {
$output['d'][] = [
'w' => (float)$width,
'h' => (float)$pages[2][$page],
];
}
return $output;
}
Expand Down
38 changes: 37 additions & 1 deletion tests/Unit/Service/PdfParseServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

declare(strict_types=1);

namespace OCA\Libresign\Service;

/**
* Overwrite shell_exec in the OCA\Libresign\Service namespace.
*/
function shell_exec($command) {
if (\OCA\Libresign\Tests\Unit\Service\PdfParseServiceTest::$disablePdfInfo) {
return null;
}
return \shell_exec($command);
}

namespace OCA\Libresign\Tests\Unit\Service;

use OCA\Libresign\Exception\LibresignException;
Expand All @@ -17,6 +29,7 @@
final class PdfParseServiceTest extends \OCA\Libresign\Tests\Unit\TestCase {
private ITempManager $tempManager;
private LoggerInterface|MockObject $loggerInterface;
public static $disablePdfInfo = false;

public function setUp(): void {
parent::setUp();
Expand Down Expand Up @@ -58,7 +71,8 @@ public function dataGetMetadataWithFail(): array {
/**
* @dataProvider providerGetMetadataWithSuccess
*/
public function testGetMetadataWithSuccess(string $path, array $expected): void {
public function testGetMetadataWithSuccess(bool $disablePdfInfo, string $path, array $expected): void {
self::$disablePdfInfo = $disablePdfInfo;
/** @var File|MockObject */
$file = $this->createMock(File::class);
$file->method('getContent')
Expand All @@ -72,6 +86,7 @@ public function testGetMetadataWithSuccess(string $path, array $expected): void
public function providerGetMetadataWithSuccess(): array {
return [
[
'disablePdfInfo' => true,
'tests/fixtures/small_valid.pdf',
[
'p' => 1,
Expand All @@ -81,6 +96,7 @@ public function providerGetMetadataWithSuccess(): array {
]
],
[
'disablePdfInfo' => true,
'tests/fixtures/small_valid-signed.pdf',
[
'p' => 1,
Expand All @@ -89,6 +105,26 @@ public function providerGetMetadataWithSuccess(): array {
],
]
],
[
'disablePdfInfo' => false,
'tests/fixtures/small_valid.pdf',
[
'p' => 1,
'd' => [
['w' => 595.276, 'h' => 841.89],
],
]
],
[
'disablePdfInfo' => false,
'tests/fixtures/small_valid-signed.pdf',
[
'p' => 1,
'd' => [
['w' => 595.276, 'h' => 841.89],
],
]
],
];
}
}

0 comments on commit f0cffa5

Please sign in to comment.