A couple of days ago, i needed to send a Fax (yes, in 2017 the people still use the fax, don't ask me why) and it seems the router needed an image file instead of a PDF, otherwise the process may fail. So I just simply converted the PDF with a online service into images and completed my task. If you are a backend developer that needs to implement such an useful feature in your application, you may need to know that there's no way to do it with plain and pure PHP.
However, you can achieve it thanks to Imagick and Ghostscript and we'll show you how in this article.
Requirements
The library that we're going to use is a wrapper that abstracts the usage of the command line with some PHP code. You will need to have installed on your machine specifically the following 2 tools:
- Ghostscript installed on your machine and available from the PATH.
- Imagick extension for PHP installed and loaded (check this installation tutorial for Windows).
Remember that Ghostscript needs to be accesible from the command line, that means that the bin
and lib
folders (inside the installation path e.g C:\Program Files (x86)\gs\gs9.21
) need to exists in the PATH environment variable.
1. Install the PHP Wrapper
The first thing you need to do to convert a PDF to an image in your Symfony project is to install the wrapper library that does the hard job of the commands for you. We are talking about pdf-to-image, a package that provides an easy way to work with class to convert pdf's to images. As mentioned, the wrapper relies on an installed and functional Imagick extension and Ghostscript as well.
According to the version of PHP that you're using, you will need to install a different version of the library:
For PHP 7
If you are using the 7.x branch of PHP, then you can work with the latest version of the library. Install it with composer running the following command:
REM Download the latest version of the wrapper library
composer require spatie/pdf-to-image
For PHP >=5.5.0
As the last version of the library requires PHP 7 or newer, in case that you're using the 5.x version of PHP, you can install an older version of the wrapper with composer as well:
REM Download the 1.4.0 version of pdf-to-image
composer require spatie/pdf-to-image 1.4.0
For more information about this library please visit the official repository at Github here.
2. Using the library
The usage of this library is pretty straightforward and will work perfectly as long as you have Imagick and Ghostscript installed correctly on your machine. You only need to create an instance of the Pdf class of Spatie\PdfToImage, the constructor expects as first argument the path to the PDF that you want to convert into an image, then you can simply use the saveImage method to generate the image from the PDF. This method expects the absolute path with filename of the image that you will generate. If the path you pass to saveImage
has the extensions jpg
, jpeg
, or png
the image will be saved in that format. Otherwise the output will be a jpg. You can force the format using the setOutputFormat
method as well.
In our example we are going to force it in the png format, to have transparency, however you are free to use the format that you need:
A. Single image
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
// Use the BinaryFileResponse class of Symfony to return a file as response
use Symfony\Component\HttpFoundation\BinaryFileResponse;
// Require PDF class of the library
use Spatie\PdfToImage\Pdf;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
// Path to the PDF file that you want to convert into an image
$pdfFilePath = $this->get('kernel')->getRootDir() . '/../web/filename.pdf';
// Path and filename of the image that will be generated
// it must be absolute and with the file extension
$outputImagePath = $this->get('kernel')->getRootDir() . '/../web/pdf_image.png';
// Create a PDF instance with the filepath of the PDF
$pdf = new Pdf($pdfFilePath);
// Set the format of image to the output file
$pdf->setOutputFormat('png');
// Generate image from PDF into the desired filepath
$pdf->saveImage($outputImagePath);
// Return the image as response
return new BinaryFileResponse(
$outputImagePath
);
}
}
B. Multiple images from multiple pages
By default, if the PDF has multiple pages and you use the default code to convert it to an image, you will always get a single image (of the first page). To generate an image from every page, you will need to change the index of page of the PDF. You can do it by simply looping through the total number of pages and updating the index manually and then execute the saveImage
method with the filename and index as result:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
// Use the normal response class
use Symfony\Component\HttpFoundation\Response;
// Require PDF class of the library
use Spatie\PdfToImage\Pdf;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
// Path to the PDF file that you want to convert into an image
$pdfFilePath = $this->get('kernel')->getRootDir() . '/../web/pdf_with_many_pages.pdf';
// Create a TMP file of the image with PNG format
$fileName = uniqid().'.png';
// Get the path of the temporal image
$outputImagePath = tempnam(sys_get_temp_dir(), $fileName);
// Create a PDF instance with the filepath of the PDF
$pdf = new Pdf($pdfFilePath);
// Loop through every page
foreach (range(1, $pdf->getNumberOfPages()) as $pageNumber) {
// Set the index of the PDF to the page you want
$pdf->setPage($pageNumber);
// Set the image format and output path
$pdf->setOutputFormat('png')
->saveImage('pdf_image'.$pageNumber.".png");
}
// Return the TMP file image as response
return new Response("All the pages of the PDF were succesfully converted to images");
}
}
C. Generate TMP image without saving it locally
In case you are willing to generate an image from a PDF without saving it inside some local path, then you will need to create a TMP file with PHP in your operative system, as the wrapper requires to write the file somewhere. You can easily create a temporal file with tempnam
and sys_get_temp_dir
methods of PHP:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
// Use the BinaryFileResponse class of Symfony to return a file as response
use Symfony\Component\HttpFoundation\BinaryFileResponse;
// Require PDF class of the library
use Spatie\PdfToImage\Pdf;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
// Path to the PDF file that you want to convert into an image
$pdfFilePath = $this->get('kernel')->getRootDir() . '/../web/filename.pdf';
// Create a TMP file of the image with PNG format
$fileName = uniqid().'.png';
// Get the path of the temporal image
$outputImagePath = tempnam(sys_get_temp_dir(), $fileName);
// Create a PDF instance with the filepath of the PDF
$pdf = new Pdf($pdfFilePath);
// Set the format of image to the output file
$pdf->setOutputFormat('png');
// Generate image from PDF into the TMP file
$pdf->saveImage($outputImagePath);
// Return the TMP file image as response
return new BinaryFileResponse(
$outputImagePath
);
}
}
Problems for Windows user
In Windows, it's very important that the architecture of the installed version of Imagick and Ghostscript are the same. This means that if you have Imagick x86 (32 bits), Ghostscript needs to have the same architecture as well x86 and viceversa. Otherwise your code is open to many possible exceptions like:
Happy coding !