From 2e8d9eb4d2f624b1e501035b789096875d39f6ff Mon Sep 17 00:00:00 2001 From: Tobias Feijten Date: Tue, 13 Feb 2024 10:45:27 +0100 Subject: [PATCH] Fix PR comments --- README.md | 4 ++-- composer.json | 20 ++++++++++++++++---- src/Pdf.php | 48 +++++++++++++++++++++++++++++++++++------------ tests/PdfTest.php | 12 +++++++----- 4 files changed, 61 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5a25e11..14c2d75 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![StyleCI](https://styleci.io/repos/38419604/shield?branch=master)](https://styleci.io/repos/38419604) [![Total Downloads](https://img.shields.io/packagist/dt/spatie/pdf-to-image.svg?style=flat-square)](https://packagist.org/packages/spatie/pdf-to-image) -This package provides an easy to work with class to convert PDF's to images. +This package provides an easy to work with class to convert PDF's to images. It is based on the [`spatie/pdf-to-image`](https://github.com/spatie/pdf-to-image) library, which relied on Imagick for the conversion. This library relies on GD instead. ## Requirements @@ -50,7 +50,7 @@ $pdf->setOutputFormat(ExportFormatEnum::PNG) ->saveImage($pathToWhereImageShouldBeStored); //the output wil be a png, no matter what ``` -You can set the quality of compression. This depends on the export format, see the GD documentation for more info: +You can set the quality of compression (this depends on the export format, see the GD documentation for more details): ```php $pdf->setCompressionQuality(100); // sets the compression quality to maximum ``` diff --git a/composer.json b/composer.json index 74eb8e7..8e8dea7 100644 --- a/composer.json +++ b/composer.json @@ -13,14 +13,26 @@ { "name": "Freek Van der Herten", "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" + "homepage": "https://spatie.be" + }, + { + "name": "Tobias Feijten", + "email": "tobias@drenso.nl", + "homepage": "https://drenso.nl" + }, + { + "name": "Bob van de Vijver", + "email": "bob@drenso.nl", + "homepage": "https://drenso.nl" } ], "require": { - "php" : "^8.2", + "php": ">=8.2", "ext-gd": "*", - "ext-random": "*" + "ext-random": "*", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0", + "symfony/process": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { "pestphp/pest": "^1.21" diff --git a/src/Pdf.php b/src/Pdf.php index f4ae47f..31554d2 100644 --- a/src/Pdf.php +++ b/src/Pdf.php @@ -7,26 +7,26 @@ use Drenso\PdfToImage\Exceptions\PdfDoesNotExist; use GdImage; use Random\Engine\Secure; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Process\Process; class Pdf { - protected string $cacheDir; + protected ?string $cacheDir = null; protected ?int $width = null; protected ?ExportFormatEnum $outputFormat = null; protected int $page = 1; protected int $compressionQuality = -1; private ?int $numberOfPages = null; + private Filesystem $filesystem; public function __construct(private readonly string $pdfFile, protected readonly int $resolution = 144) { - if (! file_exists($pdfFile)) { + $this->filesystem = new Filesystem(); + if (! $this->filesystem->exists($pdfFile)) { throw new PdfDoesNotExist("File `{$pdfFile}` does not exist"); } - - // Convert to PNG files, so GD can be used for the following processing - $this->cacheDir = '/tmp/pdftoimage/' . bin2hex((new Secure())->generate()); - @mkdir($this->cacheDir, recursive: true); - exec(sprintf('gs -dSAFER -dBATCH -sDEVICE=png16m -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r%s -sOutputFile=%s %s', $this->resolution, $this->cacheDir . '/%03d.png', $this->pdfFile)); } public function setWidth(int $width): self @@ -61,8 +61,8 @@ public function setPage(int $page): self public function getNumberOfPages(): int { if ($this->numberOfPages === null) { - $files = scandir($this->cacheDir); - $this->numberOfPages = count(array_filter($files, fn (string $filename) => str_ends_with($filename, '.png'))); + $this->prepareImages(); + $this->numberOfPages = (new Finder())->in($this->cacheDir)->name('*.png')->count(); } return $this->numberOfPages; @@ -88,7 +88,7 @@ public function saveAllPagesAsImages(string $directory, string $prefix = ''): ar return array_map(function ($pageNumber) use ($directory, $prefix) { $this->setPage($pageNumber); - $destination = "{$directory}/{$prefix}{$pageNumber}.{$this->outputFormat}"; + $destination = "{$directory}".DIRECTORY_SEPARATOR."{$prefix}{$pageNumber}.{$this->outputFormat}"; $this->saveImage($destination); return $destination; @@ -97,8 +97,9 @@ public function saveAllPagesAsImages(string $directory, string $prefix = ''): ar public function getImageData(string $pathToImage): GdImage { + $this->prepareImages(); $this->outputFormat ??= ExportFormatEnum::fromFileName($pathToImage); - $pageName = $this->cacheDir . sprintf('/%03d.png', $this->page); + $pageName = $this->cacheDir . DIRECTORY_SEPARATOR . sprintf('%03d.png', $this->page); $originalImage = imagecreatefrompng($pageName); if ($this->width === null) { @@ -111,7 +112,7 @@ public function getImageData(string $pathToImage): GdImage return $originalImage; } // Calculate scaled height - $newHeight = $imageSize[1] * ($this->width / $imageSize[0]); + $newHeight = round((float)$imageSize[1] * ($this->width / $imageSize[0])); // Resize in new image $resizedImage = imagecreatetruecolor($this->width, $newHeight); imagecopyresampled($resizedImage, $originalImage, 0, 0, 0, 0, $this->width, $newHeight, $imageSize[0], $imageSize[1]); @@ -125,4 +126,27 @@ public function setCompressionQuality(int $compressionQuality): self return $this; } + + public function prepareImages(): void + { + if ($this->cacheDir) { + return; + } + + // Convert to PNG files, so GD can be used for the following processing + $this->cacheDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'pdftoimage' . DIRECTORY_SEPARATOR . bin2hex((new Secure())->generate()); + $this->filesystem->mkdir($this->cacheDir); + (new Process([ + 'gs', + '-dSAFER', + '-dBATCH', + '-dNOPAUSE', + '-sDEVICE=png16m', + '-dTextAlphaBits=4', + '-dGraphicsAlphaBits=4', + '-r' . $this->resolution, + '-sOutputFile=' . $this->cacheDir . DIRECTORY_SEPARATOR . '%03d.png', + $this->pdfFile]) + )->mustRun(); + } } diff --git a/tests/PdfTest.php b/tests/PdfTest.php index 630455f..a44bae7 100644 --- a/tests/PdfTest.php +++ b/tests/PdfTest.php @@ -12,10 +12,10 @@ it('will throw an exception when try to convert a non existing file', function () { - new Pdf('pdfdoesnotexists.pdf'); + new Pdf('pdfdoesnotexist.pdf'); })->throws(PdfDoesNotExist::class); -it('will throw an exception when passed an invalid page number', function ($invalidPage) { +it('will throw an exception when passed an invalid page number', function () { (new Pdf($this->testFile))->setPage(100); }) ->throws(PageDoesNotExist::class) @@ -31,8 +31,9 @@ $image = (new Pdf($this->testFile, resolution: 150)) ->getImageData('test.jpg'); - expect(imageresolution($image)[0])->toEqual(150) - ->and(imageresolution($image)[1])->toEqual(150); + $resolution = imageresolution($image); + expect($resolution[0])->toEqual(150) + ->and($resolution[1])->toEqual(150); }); it('will convert a specified page', function () { @@ -48,5 +49,6 @@ ->setWidth(400) ->getImageData('test.jpg'); - expect(imagesx($image))->toBe(400); + expect(imagesx($image))->toBe(400) + ->and(imagesy($image))->toBe(283); // round(210*400/297) });