7.4. 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 is 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; @Lob @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
.Figure 74. Report data structure -
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 a 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: https://freemarker.apache.org/docs/.
<body> <h1>Clients report</h1> <!-- Custom HTML header --> <div class="header"> Annual Report of our Company </div> <!-- Custom HTML footer --> <div class="footer"> Address: William Road </div> <#assign clients = Root.bands.Clients /> <#list clients as 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; @top-center { content: element(header); } @bottom-center { content: element(footer); } @bottom-right{ content: counter(page) " of " counter(pages); } }
This CSS code will set header/footer positions:
div.header { display: block; text-align: center; position: running(header); width: 100%; } div.footer { display: block; text-align: center; position: running(footer); width: 100%; }
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; }
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; padding-top: 50px; } div.header { display: block; text-align: center; position: running(header); width: 100%; } div.footer { display: block; text-align: center; position: running(footer); width: 100%; } @page { /* switch to landscape */ size: landscape; /* set page margins */ margin: 0.5cm; @top-center { content: element(header); } @bottom-center { content: element(footer); } @bottom-right { content: counter(page) " of " counter(pages); } } .custom-page-start { margin-top: 50px; } </style> </head> <body> <h1>Clients report</h1> <!-- Custom HTML header --> <div class="header"> Annual Report of our Company </div> <!-- Custom HTML footer --> <div class="footer"> Address: William Road </div> <#assign clients = Root.bands.Clients /> <#list clients as 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.
Figure 75. Template editorAs 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.
Figure 76. Report result