3.2. Sample HTML/PDF Report with Paging, Headers and Footers
Let’s imagine that we now want to create a report with landscape orientation, page numbers, fixed header and footer on each page, configured using special CSS rules and properties. The output format is HTML exported to PDF.
This sample report with its demo project are also available on CUBA GitHub.
-
Data model
Our report will display information on the
Client
entity. It contains two String attributes:title
andsummary
, we will use them in our report structure.public class Client extends StandardEntity { @NotNull @Column(name = "TITLE", nullable = false) protected String title; @Column(name = "SUMMARY") protected String summary; ... }
-
Let’s create a simple report without parameters. The JPQL query will select all clients with their local attributes:
title
andsummary
. -
Now let’s create the report template file. Here we define header and footer blocks that will be printed on each PDF page. Also we use special
page-break-before
:always
CSS property. It will generate page break before each client info block.As you can see, we use FreeMarker statements to insert data to our template. See complete FreeMarker reference here: http://freemarker.org/docs/.
<body> <h1>Clients report</h1> <!-- Custom HTML header --> <div id="header"> <h2>Annual Report of our Company</h2> </div> <!-- Custom HTML footer --> <div id="footer"> <h2>Address: William Road</h2> <span class="custom-footer-page-number">Number: </span> </div> <#assign clients = Root.bands.Clients /> <#list clients as client> <!-- New page for each client --> <div class="custom-page-start" style="page-break-before: always;"> <h2>Client</h2> <p>Name: ${client.fields.title}</p> <p>Summary: ${client.fields.summary}</p> </div> </#list> </body>
-
CSS rules
We will use the following CSS code to tune our PDF page representation:
body { font: 12pt Georgia, "Times New Roman", Times, serif; line-height: 1.3;} @page { /* switch to landscape */ size: landscape; /* set page margins */ margin: 0.5cm; /* Default footers */ @bottom-left { content: "Department of Strategy"; } @bottom-right { content: counter(page) " of " counter(pages); } }
This CSS code will set header/footer positions:
/* footer, header - position: fixed */ #header { position: fixed; width: 100%; top: 0; left: 0; right: 0; } #footer { position: fixed; width: 100%; bottom: 0; left: 0; right: 0; }
After that we need to fix paddings of the main content to prevent content and header/footer overlapping:
/* Fix overflow of headers and content */ body { padding-top: 50px; } .custom-page-start { margin-top: 50px; }
What if we want to insert page number to custom position? That’s it:
.custom-footer-page-number:after { content: counter(page); }
So, the complete
paging-template.html
file will look as below:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Invoice</title> <style type="text/css"> body { font: 12pt Georgia, "Times New Roman", Times, serif; line-height: 1.3; } @page { /* switch to landscape */ size: landscape; /* set page margins */ margin: 0.5cm; /* Default footers */ @bottom-left { content: "Department of Strategy"; } @bottom-right { content: counter(page) " of " counter(pages); } } /* footer, header - position: fixed */ #header { position: fixed; width: 100%; top: 0; left: 0; right: 0; } #footer { position: fixed; width: 100%; bottom: 0; left: 0; right: 0; } /* Fix overflow of headers and content */ body { padding-top: 50px; } .custom-page-start { margin-top: 50px; } .custom-footer-page-number:after { content: counter(page); } </style> </head> <body> <h1>Clients report</h1> <!-- Custom HTML header --> <div id="header"> <h2>Annual Report of our Company</h2> </div> <!-- Custom HTML footer --> <div id="footer"> <h2>Address: William Road</h2> <span class="custom-footer-page-number">Number: </span> </div> <#assign clients = Root.bands.Clients /> <#list clients as client> <!-- New page for each client --> <div class="custom-page-start" style="page-break-before: always;"> <h2>Client</h2> <p>Name: ${client.fields.title}</p> <p>Summary: ${client.fields.summary}</p> </div> </#list> </body> </html>
-
Upload the template file and run the report.
As you can see, the report contains the title page and page breaks before each client’s page. There are headers and footers on each page as well.