0

I'm developing a feature to generate a PDF health report from an EJS template. I'm trying to add page numbers (e.g., "Page 1 of 10") to the footer of each page. I'm using the CSS Paged Media Module features, specifically counter(page) and the @page rule. While the PDF generates correctly with all the content and page breaks, the page numbers are completely missing.

Problem Description

The goal is to have a "Page X of Y" counter at the bottom of every page in the final PDF file. I have implemented this using CSS counters and the @page at-rule with a @bottom-center margin box. The rest of the report's content and styling, including dynamic page breaks between sections, works as expected. However, the footer with the page number does not appear on any page.

As you can see CSS is being applied but some how I'm not able to see the content text

What I've Tried

My setup involves a Node.js backend that renders an EJS file into HTML, which is then converted into a PDF. Here is the relevant CSS I've added to the section of my report.ejs file. I've intentionally used a large, red font to make sure the page numbers would be noticeable if they were rendering.

.report-container {
  /* Initialize the page counter */
  counter-reset: page;
}

.section-content {
  /* Increment the page counter on each new section that causes a page break */
  counter-increment: page;
}

/* Define styles for printing and PDF generation */
@page {
  @bottom-center {
    /* These properties are my attempt to style the page number */
    display: block;
    width: 100%;
    height: 100%;
    position: absolute;
    text-align: center;
    color: rgb(188, 0, 0); /* Bright red for visibility */
    font-size: 45px;       /* Very large font size for visibility */
    content: "Page " counter(page) " of " counter(pages);
    z-index: 1000;
  }
}

And this is a simplified version of my EJS/HTML structure that generates the pages. Each page-group is designed to start on a new page (except the first one).

<div class="report-container">
  <% pageGroups.forEach((group, index) => { %>
    <table class="page-table" <%- index > 0 ? 'style="page-break-before: always;"' : '' %>>
      <thead>
        <tr>
          <td class="page-header">
            <!-- Header content -->
          </td>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="page-content">
            <div class="section-content content-area">
              <!-- Main content for the page -->
            </div>
          </td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <td class="page-footer">
            <!-- This is empty because I'm trying to use the @page rule for the footer -->
          </td>
        </tr>
      </tfoot>
    </table>
  <% }); %>
</div>

My Questions

Is there an issue with my CSS or the way I'm incrementing counters that would cause the page numbers not to render? I've read that some CSS properties like position and z-index don't apply to page-margin boxes like @bottom-center. Could including them be breaking the rendering of the content property? Could this be a limitation of the PDF generation library I am using in my Node.js environment? Are there known compatibility issues with counter(pages) or the @page rule in common libraries (like Puppeteer, html-pdf, etc.)?

Expected Outcome

I expect to see a page number, such as "Page 1 of 10", rendered in large red text at the bottom-center of each page of the output PDF. Currently, there is nothing there. Any help or insight into why my page numbers are not visible would be greatly appreciated!

4
  • 1
    which is then converted into a PDF: How do you convert the HTML to PDF? The browser-internal conversion implements its own treatment of page numbers, which considers the CSS @page rules only partially. See also here. Commented Jun 8 at 12:15
  • Browsers are generally terrible with print CSS rules. I've had good luck with Flying Saucer, but that is a totally different technology. (Free though.) Commented Jun 8 at 12:38
  • @HeikoTheißen we use Puppetter with headless mode. Commented Jun 9 at 5:27
  • Then you can use Page <span class="pageNumber"></span> of <span class="totalPages"></span> in the footerTemplate option. Commented Jun 9 at 6:05

2 Answers 2

0

I don't think you need to manage the page counter with counter-reset and counter-increment.
And on the other hand, it does not work on Firefox.

Sign up to request clarification or add additional context in comments.

1 Comment

Then how do I make it work? My pdf conversion happen using Puppeteer service which we have in backend. I just send the rendered HTML and it gives back PDF.
0

The issue isn’t with your code—it’s due to limitations in how puppeteer and html-pdf handle Ppaged media. The @bottom-center rule and counter(pages) aren’t fully supported in these tools, so the page numbers don’t render. Also, position and z-index don’t apply inside page margin boxes and won’t affect the result.

Instead of relying on @page, use tfoot in the table.

<tfoot>
  <tr>
    <td class="page-footer">
      Page <span class="pageNumber"></span> of <span class="totalPages"></span>
    </td>
  </tr>
</tfoot>

but it would be better if you used something that fully supports Paged Media like "PrinceXML"
and most of the things you mentioned don't work at it and aren't a problem.

1 Comment

Previously, I was using this code only. Issue: my HTML has a section. And when it gets converted to the PDF, it breaks into multiple pages. Since during the HTML pages there were only five footers, when it's moved to page/print mode these footer gets replicated to the pages now in batches of 5 from each section; all pages has same footer page number from their section. so that's fail too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.