Reputation: 6816
I'm trying to render a pdf using CakePdf 3.5.3 using the wkhtmltopdf engine with html headers on every page showing custom text as well as the page number.
https://wkhtmltopdf.org/usage/wkhtmltopdf.txt describes the process as passing the --header-html
command line argument and using the following javascript and html:
<!DOCTYPE html>
<html><head><script>
function subst() {
var vars = {};
var query_strings_from_url = document.location.search.substring(1).split('&');
for (var query_string in query_strings_from_url) {
if (query_strings_from_url.hasOwnProperty(query_string)) {
var temp_var = query_strings_from_url[query_string].split('=', 2);
vars[temp_var[0]] = decodeURI(temp_var[1]);
}
}
var css_selector_classes = ['page', 'frompage', 'topage', 'webpage', 'section', 'subsection', 'date', 'isodate', 'time', 'title', 'doctitle', 'sitepage', 'sitepages'];
for (var css_class in css_selector_classes) {
if (css_selector_classes.hasOwnProperty(css_class)) {
var element = document.getElementsByClassName(css_selector_classes[css_class]);
for (var j = 0; j < element.length; ++j) {
element[j].textContent = vars[css_selector_classes[css_class]];
}
}
}
}
</script></head><body style="border:0; margin: 0;" onload="subst()">
<table style="border-bottom: 1px solid black; width: 100%">
<tr>
<td class="section"></td>
<td style="text-align:right">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
</body></html>
I created src/Template/Layout/pdf/default.ctp
and src/Template/Layout/pdf/header.ctp
default.ctp
<!DOCTYPE html>
<html lang="en">
<head>
<?= $this->Html->charset() ?>
<title>
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
</head>
<body>
<?= $this->Flash->render() ?>
<div>
<?= $this->fetch('content') ?>
</div>
</body>
</html>
header.ctp
<!DOCTYPE html>
<html lang="en">
<head>
<script>
function subst() {
var vars = {};
var query_strings_from_url = document.location.search.substring(1).split('&');
for (var query_string in query_strings_from_url) {
if (query_strings_from_url.hasOwnProperty(query_string)) {
var temp_var = query_strings_from_url[query_string].split('=', 2);
vars[temp_var[0]] = decodeURI(temp_var[1]);
}
}
var css_selector_classes = ['page', 'frompage', 'topage', 'webpage', 'section', 'subsection', 'date', 'isodate', 'time', 'title', 'doctitle', 'sitepage', 'sitepages'];
for (var css_class in css_selector_classes) {
if (css_selector_classes.hasOwnProperty(css_class)) {
var element = document.getElementsByClassName(css_selector_classes[css_class]);
for (var j = 0; j < element.length; ++j) {
element[j].textContent = vars[css_selector_classes[css_class]];
}
}
}
}
</script>
<title></title>
</head>
<body onload="subst()">
<?= $some_view_variable ?>
Page <span class="page"></span> of <span class="topage"></span>
</body>
</html>
This is how I configure CakePdf in my controller.
Configure::write('CakePdf', [
'engine' => [
'className' => 'CakePdf.WkHtmlToPdf',
'binary' => "/usr/local/bin/wkhtmltopdf",
'options' => [
'header-html' => APP . 'Template' . DS . 'Layout' . DS . 'pdf' . DS . 'header.ctp',
],
],
'margin' => [
'bottom' => 0,
'left' => 0,
'right' => 0,
'top' => 0
],
'pageSize' => 'Letter',
'orientation' => 'portrait'
]);
The header information is not shown in the generated PDF. I'm also not sure how to pass viewVars to the header template.
Upvotes: 0
Views: 787
Reputation: 5099
My understanding is that the header must be in a .html
file, it must be raw HTML (e.g. not a Cake template), and I've not found a way to specify the header-html
option. Here's the hack I've used to work around it:
$view = new View();
$dir = TMP . 'html';
if (!is_dir($dir)) {
mkdir($dir);
}
while (true) {
// WkHtmlToPdf requires .html extension on these files, but tempnam can't do that.
$file = tempnam($dir, 'header');
if (rename($file, $file . '.html')) {
$file .= '.html';
break;
} else {
unlink($file);
}
}
file_put_contents($file, '<!DOCTYPE HTML><html lang=\'en-US\'><body style=\'font-family: "Times New Roman"\'>' . $view->element($element) . '</body></html>');
// Hack to access protected member: we want --header-html on the command line.
// This is easier than extending the class for a single such usage.
$closure = function($file) {
$this->_header = ['html' => 'file://' . $file];
};
$hack = \Closure::bind($closure, $pdf, 'CakePdf\Pdf\CakePdf');
$hack($file);
$this->_tmp[] = $file;
This is, of course, all off in a separate function, so it's easy to replace if I find a better way. :-)
Upvotes: 2