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