html to pdf
2016 28 Jun

Save a pdf copy of node whenever its created

Did you ever feel the need to save a node in pdf format on your server whenever a new node is published, if this is your case then you have come to the right place.

Although creating a pdf is not a problem here, that you can do it easily using any pdf creating library available in the market, by the way I used “dompdf”, but there are several other libraries available also.
Here is the code to generate a pdf of any html document
   

  $html = < HTML DOCUMENT >

   use Dompdf\Dompdf;
   $dompdf_path = libraries_get_path('dompdf');
   require_once($dompdf_path . '/autoload.inc.php');

   // Instantiate and use the dompdf class
   $dompdf = new Dompdf();
   $dompdf->loadHtml($html);

   // Setup the paper size and orientation
   $dompdf->setPaper('A4', 'landscape');

   // Render the HTML as PDF
   $dompdf->render();
   $pdfoutput = $dompdf->output();
   $filename = 'public://' . '/submission-' . $node->nid . '.pdf';
   $file_object = file_save_data($pdfoutput, $filename, FILE_EXISTS_REPLACE);

So in order to create this pdf you need that node’s html view right when that node is created, so drupal provides some hooks for that which triggers when a node is created like:
hook_node_presave($node), hook_node_insert($node).

Now at this stage this node’s database transaction is still not complete which means that you don’t have a node in database and you cannot get node_view($node); here in node presave & node insert stages.

You can do your pdf generation process only once your node is in database, but drupal does not provide any kind of ‘post save hook’, although there is a module for it https://www.drupal.org/project/hook_post_action which provides some hooks which will trigger when your node is saved.

What this module does is that it makes use of drupal_register_shutdown_function($callback = NULL); 
As the name suggest it registers a function for execution on shutdown.
You can send some extra parameters in drupal_register_shutdown_function other than callback function.

So we can register our callback function in node presave function so that our callback function will be called once the node_save process is completed. Hence we can do our pdf creation process in our callback function.

use Dompdf\Dompdf;

function HOOK_node_presave($node) {
 if ($node->type == 'article') {
    drupal_register_shutdown_function(_my_custom_callback, $node);
 }
}

function _my_custom_callback($node) {
  // do pdf creation process here
   $node_view = node_view($node, 'full');
   $html = render($node_view);

   $dompdf_path = libraries_get_path('dompdf');
   require_once($dompdf_path . '/autoload.inc.php');

   // Instantiate and use the dompdf class
   $dompdf = new Dompdf();
   $dompdf->loadHtml($html);

   // Setup the paper size and orientation
   $dompdf->setPaper('A4', 'landscape');

   // Render the HTML as PDF
   $dompdf->render();
   $pdfoutput = $dompdf->output();
   $filename = 'public://' . '/submission-' . $node->nid . '.pdf';
   $file_object = file_save_data($pdfoutput, $filename, FILE_EXISTS_REPLACE);
}

Alright so your pdf is saved in files directory of your installation, now all you need to do is to save a reference of this file in the same node so that you could know where to find the pdf file for any node.

   .
   .
   .
   $pdfoutput = $dompdf->output();

   $filename = 'public://' . '/submission-' . $node->nid . '.pdf';

   $file_object = file_save_data($pdfoutput, $filename, FILE_EXISTS_REPLACE);
   $file_object_arr = (array)$file_object;
   $file_object_arr['display'] = 1;
   $node->article_pdf[$node->language][0] = $file_object_arr;
   node_save($saved_node);
}

But wait we are saving that node again this would put a us in a loop, because that node_presave will trigger again as we are saving the same type of node again, so we must put a check there so that it registers the shutdown function only once.

Now the best way to do is using drupal_static(__FUNCTION__); it keeps the variable cached just for a full request and that's what we want.

So here we go:
 

function HOOK_node_presave($node) {
 if ($node->type == 'article') {
   $life_cycle_complete = &drupal_static(__FUNCTION__);
   if (!isset($life_cycle_complete)) {     
    drupal_register_shutdown_function(_my_custom_callback, $node);
    $life_cycle_complete = 'processed';
   }
 }
}

Cheers yes

Latest Blogs

intor_react

Introduction to React JS and Components

React is a flexible JavaScript library for building user interfaces and it lets you compose complex UIs from small and isolated pieces of codes which is known as Components.

Read More

Flexbox

How to simplify the layout of your website using Flexbox?

Have you ever scratched your head while working with CSS to make some simple layouts for your website? Are you fed up of running to the UI developers for small layout designs every time?

Read More

cache_context

Drupal 8 Cache Context: An efficient way for context based caching.

We are well aware of the fact that Drupal Cache API is a remarkable feature introduced in Drupal 8.

Read More

CacheImg

How does entity cache work in Drupal 8

The Drupal Cache API is used to store data that takes a long time to compute.

Read More