3.2. Пример отчёта HTML/PDF
Предположим, нам нужно создать отчёт с альбомной ориентацией страниц, нумерацией страниц, а также фиксированными заголовком и подвалом на каждой странице, которые мы настроим через правила и свойства CSS. Формат вывода отчёта - HTML с конвертацией в PDF.
Полностью готовый пример этого отчёта вместе с тестовым проектом можно скачать с CUBA GitHub.
-
Модель данных
Отчёт будет отображать информацию о сущности
Client
. Она содержит два строковых атрибута,title
иsummary
, которые мы используем в структуре отчёта.public class Client extends StandardEntity { @NotNull @Column(name = "TITLE", nullable = false) protected String title; @Column(name = "SUMMARY") protected String summary; ... }
-
Создадим простой отчёт без параметров. Запрос JPQL возвращает список всех клиентов с их локальными атрибутами:
title
иsummary
. -
Теперь создадим файл шаблона. В нём мы определим блоки заголовка и подвала, которые должны выводиться на каждой странице итогового документа PDF. Также используем свойство CSS
page-break-before
:always
, которое будет создавать разрыв страницы перед каждым новым блоком информации о клиенте.Мы также используем теги FreeMarker для вставки данных в тело отчёта. Подробное руководство по FreeMarker назодится здесь: 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
Используем следующий код CSS для разметки страницы PDF:
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); }
В итоге у нас получился файл
paging-template.html
со следующим содержанием:<!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>
-
Загрузка шаблона и запуск отчёта.
Как мы видим, отчёт содержит титульную страницу и разрывы перед каждой страницей с информацией о клиенте, а также заголовок и подвал на каждой странице: