Search code examples
phppdfdrupalimagicksnappy

Convert PNG to PDF using Imagick in PHP


I created module on Drupal 8 in order to generate PDF file from node, since request is that this PDF looks exactly like the page I want to create screenshot in PNG and to convert it to PDF using Imagick.

Everything goes fine until Imagick, I'm getting this:

ImagickException: Zero size image string passed in Imagick->readimageblob() 

But image is there and it's ok, I can open it without any problems.

Here is my code:

<?php
/**
 * @file
 * MyControllerBase
 */

namespace Drupal\my_pdf\Controller;

use Drupal\node\Entity\Node;
use Knp\Snappy\Image;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Controller\ControllerBase;

class MyPdfController extends ControllerBase {

  /**
   * @return \Symfony\Component\HttpFoundation\Response
   */
  public function createPdf($nodeid) {

    $node = Node::load($nodeid);
    $url = \Drupal::request()->getSchemeAndHttpHost() . $node->url();
    $module_path = drupal_get_path('module', 'verifone_pdf');
    $filename = 'node-id-' . $nodeid . '.png';

    $snappy = new Image('xvfb-run /usr/bin/wkhtmltoimage');
    $snappy->setOption('no-stop-slow-scripts', TRUE);
    $snappy->setOption('javascript-delay', 5000);
    $snappy->setOption('images', TRUE);
    $snappy->setOption('enable-smart-width', TRUE);
    $snappy->setOption('user-style-sheet', $module_path . '/css/style.css');
    $snappy->setOption('width', '1920');
    $snappy->setDefaultExtension('png');

    if(!file_exists("/tmp/$filename")) {
      $snappy->generate($url, "/tmp/$filename");
    }

    $image = imagecreatefrompng("/tmp/$filename");
    $im = new \Imagick();

    ob_start();
    imagepng($image);
    $image_data = ob_get_contents();
    ob_end_clean();

    // Get image source data
    $im->readimageblob($image_data);

    $im->setImageFormat('pdf');

    return new Response(
      $im,
      200,
      array(
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => 'attachment; filename="page-' . $nodeid . '.pdf"'
      )
    );
  }
}

Am I missing something? Can someone help me resolve this or give me some working example for PNG to PDF format changing?


Solution

  • readImageBlob() reads image from a binary string. In your case, you need readImage() who reads image from filename.

    Also, you'll have to use the setFormat() method instead of the setImageFormat() method.

    i.e. :

    $im = new \Imagick();
    $im->readImage("/tmp/{$filename}");
    $im->setFormat('pdf');
    

    You can get a list of supported formats with $im->queryFormats().

    The last step will be to pass the getImage() method to the Response object.

    I would write something like this (not tested) :

    <?php
    /**
     * @file
     * MyControllerBase
     */
    
    namespace Drupal\my_pdf\Controller;
    
    use Drupal\node\Entity\Node;
    use Knp\Snappy\Image;
    use Symfony\Component\HttpFoundation\Response;
    use Drupal\Core\Controller\ControllerBase;
    
    class MyPdfController extends ControllerBase
    {
        /**
         * @return \Symfony\Component\HttpFoundation\Response
         */
        public function createPdf($nodeid)
        {
            $node = Node::load($nodeid);
            $url = \Drupal::request()->getSchemeAndHttpHost() . $node->url();
            $module_path = drupal_get_path('module', 'verifone_pdf');
            $filename = 'node-id-' . $nodeid . '.png';
    
            $snappy = new Image('xvfb-run /usr/bin/wkhtmltoimage');
            $snappy->setOption('no-stop-slow-scripts', TRUE);
            $snappy->setOption('javascript-delay', 5000);
            $snappy->setOption('images', TRUE);
            $snappy->setOption('enable-smart-width', TRUE);
            $snappy->setOption('user-style-sheet', $module_path . '/css/style.css');
            $snappy->setOption('width', '1920');
            $snappy->setDefaultExtension('png');
    
            if(!file_exists("/tmp/$filename")) {
                $snappy->generate($url, "/tmp/{$filename}");
            }
    
            $im = new \Imagick();    
            $im->readImage("/tmp/{$filename}");
            $im->setFormat('pdf');
    
            return new Response(
                $im->getImage(),
                200,
                array(
                    'Content-Type' => 'application/pdf',
                    'Content-Disposition' => 'attachment; filename="page-' . $nodeid . '.pdf"'
                )
            );
        }
    }
    

    Hope it helps.