PDF Generation in PHP

PDF Generation in PHP

Difficulty: 
Piece of Cake

This article is a follow up of our previous post Decent PDF Generation in Drupal.

As a quick recap, in that article we discussed how half-broken, slow, memory hungry and not business ready are all the available PDF generation libraries for PHP.

In this article we are going to share the code snippet needed to generate PDF's using ABCpdf. ABCpdf has been our enterprise choice for many years, generating first class quality PDF files for education certificates in our e-learning platforms for the last 10 years (it gets very frequent updates, and we have renewed the license on every upgrade).

What we like most about ABCpdf is its fidelity to the original HTML document and the fact the it suppports nearly every single HTML specification. That means that you can work on generating beautiful HTML markup, and blindly rely on ABCpdf to make an exact copy of this into a PDF file.

Even more, it let's you choose the rendering engine (embedded Gecho or Internet Explorer) so if you markup does not quite render as you like with one engine, you can give the other one a try. And Internet Explorer support has saved our lives many times, allowing PDF rendering of really, really old and out of specification markup.

ABCpdf does not parse or interpret the HTML, it relies on standard rendering engines to generate the PDF output. That means that it relies on passing an URL to generate the PDF. You can virtually render any page, or save any custom HTML in your own server to be converted by ABCpdf.

Not many much to say, here is the code that takes HTML markup as input, and returns a PDF file.

public static function HtmlToPDF_ABCpdf($source, $dest = 'I') {
    global $base_url;
    // Write the HTML to a temporary location.
    $filename = uniqid("", TRUE) . '.html';
    $uri = "public://fdf/tmp/PDF/";
    file_prepare_directory($uri, FILE_CREATE_DIRECTORY);
    $uri = "{$uri}/{$filename}";
    $path = drupal_realpath($uri);
    file_put_contents($path, $source);
    if (!file_exists($path)) {
      throw new \Exception('Could not prepare HTML for PDF generation.');
    }
    $url  = file_create_url($uri);
    $vertical = TRUE;
    
    // Prepare the assembly details.
    $manager = new \NetPhp\Core\NetManager();
    $path = drupal_realpath(drupal_get_path('module', 'fdf') . '/net/bin/ABCpdf.dll');
    
    $manager->RegisterAssembly($path, 'abcpdf');
    $manager->RegisterAssembly('mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', 'mscorlib');
    
    $manager->RegisterClass('abcpdf', 'WebSupergoo.ABCpdf8.Doc', 'Doc');
    $manager->RegisterClass('abcpdf', 'WebSupergoo.ABCpdf8.MediaType', 'MediaType');
    $manager->RegisterClass('abcpdf', 'WebSupergoo.ABCpdf8.EngineType', 'EngineType');
    
    $pdfFile = $manager->Create('abcpdf', 'Doc')->Instantiate(array());

    $pdfFile->HtmlOptions->Engine = $manager->Create('abcpdf', 'EngineType')->Enum('Gecko');
    $pdfFile->HtmlOptions->GeckoSubset->Media = $manager->Create('abcpdf', 'MediaType')->Enum('Screen');
    $pdfFile->HtmlOptions->GeckoSubset->UseScript = TRUE;

    // apply a rotation transform
    $w = $pdfFile->MediaBox->Width->Val();
    $h = $pdfFile->MediaBox->Height->Val();

    if (!$vertical) {
      
      $l = $pdfFile->MediaBox->Left->Val();
      $b = $pdfFile->MediaBox->Bottom->Val();
      
      $pdfFile->Transform->Rotate(90, $l, $b);
      $pdfFile->Transform->Translate($w, 0);
      
      // rotate our rectangle
      $pdfFile->Rect->Width = $h;
      $pdfFile->Rect->Height = $w;
    }
    else {
      // rotate our rectangle
      $pdfFile->Rect->Width = $w;
      $pdfFile->Rect->Height = $h;
    }

    // Añadimos el HTML
    $theID = 0;
    try {
      $theID = $pdfFile->AddImageUrl($url, false, (int) $h, true);
      while (TRUE) {
        $pdfFile->FrameRect();
        if (!$pdfFile->Chainable($theID)) {
          break;
        }

        $pdfFile->Page = $pdfFile->AddPage();
        $theID = $pdfFile->AddImageToChain($theID);
      }
    }
    catch (\Exception $ex) {
      
    }

    // adjust the default rotation and save
    if (!$vertical) {
      $theID = $pdfFile->GetInfoInt($pdfFile->Root, "Pages");
      pdfFile.SetInfo($theID, "/Rotate", "90");
    }

    $bytes = $pdfFile->GetData()->AsIterator();
    
    // There is no way to deal with a byte[] in PHP
    // so use System.Convert to deal with that
    // using base64 as a bridge.
    $convert = $manager->Create('mscorlib', 'System.Convert');
    $b64 = $convert->ToBase64String($bytes)->Val();
    $pdf = base64_decode($b64);

    return static::serveData($pdf, count($bytes), 'file.pdf');

  }

 

Add new comment

By: david_garcia Friday, April 24, 2015 - 21:46