dompdf
dompdf copied to clipboard
CSS counter for total pages?
Hey,
Read some suggestions on using:
<footer><div class="pagenum-container">Page <span class="pagenum"></span></div></footer>
footer .pagenum:before {
content: counter(page);
}
To get the current page number. Is it possible to get the total number of pages through css as well, to show something like Page 4 of 5
?
You can try this
<div id="pageCounter">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div id="pageNumbers">
<div class="page-number"></div>
<div class="page-number"></div>
<div class="page-number"></div>
<div class="page-number"></div>
</div>
#pageCounter {
counter-reset: pageTotal;
}
#pageCounter span {
counter-increment: pageTotal;
}
#pageNumbers {
counter-reset: currentPage;
}
#pageNumbers div:before {
counter-increment: currentPage;
content: "Page " counter(currentPage) " of ";
}
#pageNumbers div:after {
content: counter(pageTotal);
}
https://codepen.io/Bhupinderkumar/pen/gKzKGw here is pen you can see the total page number at the bottom of the page
Here is way to do it, while still being able to use CSS on the paging information.
The current page number can be generated via CSS features, as documented in samples, but the total page count must be injected after the rendering happened. To do that we use an arbitrary placeholder, DOMPDF_PAGE_COUNT_PLACEHOLDER
, in our HTML template and replace that string in the entire rendered PDF, juste before it get outputted.
The HTML would be:
<!doctype html>
<html>
<head>
<title>foo</title>
<style>
footer {
/* Place the footer at the bottom of each page */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* Any other appropriate styling */
color: #4f82d6;
font-weight: bold;
}
/* Show current page number via CSS counter feature */
.page-number:before {
content: counter(page);
}
</style>
</head>
<body>
<footer>
Page <span class="page-number"></span> of DOMPDF_PAGE_COUNT_PLACEHOLDER
</footer>
Very long content spanning multiple pages here ...
</body>
</html>
And PHP would be:
function htmlToPdf(string $html): string
{
$dompdf = new Dompdf();
$dompdf->loadHtml($html);
$dompdf->setPaper('A4');
$dompdf->render();
injectPageCount($dompdf);
return $dompdf->output();
}
/**
* Replace a predefined placeholder with the total page count in the whole PDF document
*
* @param Dompdf $dompdf
*/
function injectPageCount(Dompdf $dompdf): void
{
/** @var CPDF $canvas */
$canvas = $dompdf->getCanvas();
$pdf = $canvas->get_cpdf();
foreach ($pdf->objects as &$o) {
if ($o['t'] === 'contents') {
$o['c'] = str_replace('DOMPDF_PAGE_COUNT_PLACEHOLDER', $canvas->get_page_count(), $o['c']);
}
}
}
@PowerKiKi It doesn't work for me... when I checked it seems the placeholder string didn't make it to the $o['c']
variable without modifications. It is sort of there but there is something added before every letter:
Did anyone else encounter this issue?
EDIT: I managed to make it work. Here is my implementation: https://gist.github.com/enumag/f670865b70d11e0b8156b1e92acc3c92
@bsweeney I tried using just CSS with content: "Page " counter(page) " of " counter(pages);
but even though counter(page)
works fine counter(pages)
always returns 0
. Any idea why? Can that be fixed? It would be much cleaner solution.
@enumag I ended up with a shorter placeholder '^^'
because a long string would reserve space around it when the layout happen. That would force whitespaces around the final total page number. Maybe something shorter too ? do you have any special settings, such as encoding or something ?
@PowerKiKi Nope my settings are very basic. I'm not even using the Options class yet, everything is default.
We're not currently tracking total number of pages via CSS counters because of the way that Dompdf renders the document. Currently Dompdf renders as it parses the document. That means the PDF is already being generated before we know how many pages there will be. To support capturing the page count we would need to alter the rendering process to break the rendering step into two, pagination followed by render. Not impossible, but a fairly significant change to how Dompdf works.
The best we can do otherwise would be to use a placeholder, pretty much like what's been suggested here.
I ended up with a workaround rendering the pdf twice .. first time to get total page count, 2nd time to render with the correct total page count set.. something like:
// ...
$viewVars = [
'invoice' => $invoice,
'numPagesTotal' => 999
];
// ...
// ---- 1st render to get page count
$domPdf = new Dompdf($pdfOptions);
$domPdf->loadHtml($this->_renderTwig('InvoicePdf/invoiceBody.html.twig', $viewVars));
$domPdf->render();
$viewVars['numPagesTotal'] = $domPdf->getCanvas()->get_page_count();
// ---- 2nd (final) render with known page count
$domPdf = new Dompdf($pdfOptions);
$domPdf->loadHtml($this->_renderTwig('InvoicePdf/invoiceBody.html.twig', $viewVars));
$domPdf->render();
// ...
@marcbln good idea ! much more robust than my workaround, and simple to understand. Though it might be a performance hit that might not be acceptable for all projects I guess.
But I would still recommend your workaround over mine, thank you for sharing !
@marcbln Good idea! It can be optimized a little if you usually generate single-page PDFs (i.e. multi-page PDFs are the exception) by speculatively setting the page number to 1 on first render and only rendering again if it turns out that the PDF actually has multiple pages.