Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS counter for total pages? #1636

Open
jerrebm opened this issue Jan 15, 2018 · 10 comments
Open

CSS counter for total pages? #1636

jerrebm opened this issue Jan 15, 2018 · 10 comments

Comments

@jerrebm
Copy link

jerrebm commented Jan 15, 2018

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?

@bhupinderkumarbl
Copy link

bhupinderkumarbl commented Jun 22, 2018

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); 
}

@bhupinderkumarbl
Copy link

https://codepen.io/Bhupinderkumar/pen/gKzKGw here is pen you can see the total page number at the bottom of the page

@PowerKiKi
Copy link
Contributor

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']);
        }
    }
}

@enumag
Copy link
Contributor

enumag commented Aug 27, 2019

@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:

Screenshot from 2019-08-27 09-31-13

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.

@PowerKiKi
Copy link
Contributor

@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 ?

@enumag
Copy link
Contributor

enumag commented Aug 27, 2019

@PowerKiKi Nope my settings are very basic. I'm not even using the Options class yet, everything is default.

@bsweeney
Copy link
Member

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.

@marcbln
Copy link

marcbln commented Dec 7, 2020

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();

        // ...

@PowerKiKi
Copy link
Contributor

@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 !

@steffenweber
Copy link

steffenweber commented Jan 15, 2024

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants