Исходные данные
Java 8 или выше
PrimeFaces 8.0 или выше
Maven 3.6.3 или выше
JasperReports Library 6.17.0
JasperSoft Studio 6.17.0
Немного слов о JasperReports
Библиотека JasperReports (The JasperReports Library) — это самый популярный в мире движок с открытым исходным кодом, предназначенный для генерирования отчетов. Библиотека полностью написана на языке Java и способна использовать данные, поступающие из любого источника, и создавать документы с превосходным качеством, которые можно просматривать, печатать или экспортировать в различные форматы, включая HTML, PDF, Excel, OpenOffice и Word.
JasperSoft Studio – это дизайнер отчетов JasperReports, созданный на базе Eclipse, с открытым исходным кодом. Позволяет создавать шаблоны отчетов и сами отчеты из любого источника данных, форматировать внешний вид отчетов (для печати или чтения на экране), экспортировать отчеты в широкий спектр форматов.
Подключение JasperReports в проект
Для того, чтобы использовать JasperReports Library (https://community.jaspersoft.com/project/jasperreports-library) в вашем проекте, добавьте в pom.xml вашего проекта следующую зависимость (dependency):
<dependencies>
<!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports -->
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.17.0</version>
</dependency>
</dependencies>
Для Gradle объявление будет таким:
// https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports
implementation group: 'net.sf.jasperreports', name: 'jasperreports', version: '6.17.0'
JasperSoft Studio 6.17.0 является внешним исполняемым приложением и устанавливается отдельно (https://community.jaspersoft.com/project/jaspersoft-studio). Программа доступна для всех популярных операционных систем: Linux, MacOS, Windows.
P.S. Для скачивания необходима регистрация в JasperSoft Community (это недолго).
Настройка отчета
Под настройкой отчета в данной статье понимается следующее:
1) написание Java класса для отображения данных модели предметной области на поля отчета (mapping)
2) создание шаблона отчета
3) связывание полей шаблона с соответствующими полями класса
Для начала создадим и напишем Java класс.
Для примера, предположим, что мы разрабатываем систему управления образовательным веб-приложением. Заказчик системы попросил реализовать следующую возможность: пользователь-администратор нажимает на кнопку в веб-интерфейсе и получает отчёт «об успеваемости учащихся на курсах». Отчет должен выгружаться в формате xlsx
И так, наша предметная область – это курсы, учащиеся на них и их успеваемость.
Класс, который описывает содержимое требуемого отчета, может, к примеру выглядеть так:
import lombok.*;
import java.io.Serializable;
import java.util.Date;
@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class ListenersGradeProgress implements Serializable {
/**
* Идентификатор курса
*/
private Long courseId;
/**
* Полное название курса
*/
private String courseTitle;
/**
* Дата записи слушателя (обучающегося) на курс
*/
private Date enrollmentDate;
/**
* Полное имя (ФИО) слушателя (обучающегося)
*/
private String listenerFullName;
/**
* Email слушателя (обучающегося)
*/
private String listenerEmail;
/**
* Успеваемость слушателя (обучающегося) в процентах (или баллах)
*/
private Double gradeProgressInPercent;
}
Следующий шаг – это создание шаблона отчета.
Шаблон отчета – это файл в формате xml (имеет расширение jrxml).
Существует несколько способов создания шаблонов: с помощью дизайнера отчетов, ручное составление файлов jrxml, а также посредством программного метода (object model of the JasperReports API).
Для краткости, в данной статье все эти способы не рассматриваются. Подробнее о каждом из них вы можете узнать из официальной документации JasperReports, а также на других ресурсах в сети интернет.
Приведем лишь результат создания шаблона отчета для нашего примера.
Так шаблон выглядит в дизайнере отчетов.

А ниже приведена разметка xml, которую сгенерировал дизайнер отчета.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.17.0.final using JasperReports Library version 6.17.0-6d93193241dd8cc42629e188b94f9e0bc5722efd -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="for_article" pageWidth="800" pageHeight="842" whenNoDataType="NoDataSection" columnWidth="790" leftMargin="5" rightMargin="5" topMargin="5" bottomMargin="5" isIgnorePagination="true" uuid="d453672e-0023-4314-8ef7-c80264b95e99">
<property name="com.jaspersoft.studio.report.description" value=""/>
<style name="ClmHdr" mode="Opaque" forecolor="#FFFFFF" backcolor="#595959" fill="Solid" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontName="SansSerif" fontSize="10" isBold="true"/>
<style name="Dtls" isDefault="true" mode="Transparent" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontSize="11"/>
<style name="NoData" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontSize="19"/>
<field name="courseId" class="java.lang.Long">
<fieldDescription><![CDATA[Course identification number]]></fieldDescription>
</field>
<field name="courseTitle" class="java.lang.String">
<fieldDescription><![CDATA[Full name of a course]]></fieldDescription>
</field>
<field name="enrollmentDate" class="java.util.Date">
<fieldDescription><![CDATA[Listener's course enrollment date]]></fieldDescription>
</field>
<field name="listenerFullName" class="java.lang.String">
<fieldDescription><![CDATA[The full name of a listener]]></fieldDescription>
</field>
<field name="listenerEmail" class="java.lang.String">
<fieldDescription><![CDATA[Listener's email]]></fieldDescription>
</field>
<field name="gradeProgressInPercent" class="java.lang.Double">
<fieldDescription><![CDATA[Listener's grade progress in percent]]></fieldDescription>
</field>
<background>
<band splitType="Stretch"/>
</background>
<columnHeader>
<band height="55" splitType="Stretch">
<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
<textField>
<reportElement key="courseIdHdr" style="ClmHdr" isPrintRepeatedValues="false" x="0" y="0" width="132" height="55" uuid="a8e7fd9f-5da2-45a1-a703-275f1786fee4">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="id-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["ID курса"]]></textFieldExpression>
</textField>
<textField>
<reportElement key="courseTitleHdr" style="ClmHdr" isPrintRepeatedValues="false" x="132" y="0" width="132" height="55" uuid="373c6e21-c5ef-4041-9cd2-f4810228adc8">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.unit.x" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="course-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["Название курса"]]></textFieldExpression>
</textField>
<textField>
<reportElement key="enrollmentDateHdr" style="ClmHdr" isPrintRepeatedValues="false" x="264" y="0" width="132" height="55" uuid="165e7f35-7bd8-448a-8642-325952bbc54f">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.unit.x" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="date-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["Дата записи на курс"]]></textFieldExpression>
</textField>
<textField>
<reportElement key="listenerFullnameHdr" style="ClmHdr" isPrintRepeatedValues="false" x="396" y="0" width="132" height="55" uuid="c0825ef5-5b9b-4be0-8aa8-91e5baf4732c">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.unit.x" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="listener-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["Обучающийся"]]></textFieldExpression>
</textField>
<textField>
<reportElement key="listenerEmailHdr" style="ClmHdr" isPrintRepeatedValues="false" x="528" y="0" width="131" height="55" uuid="76fc1ac4-8da1-4f95-b9c9-5983c48a498e">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.unit.x" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="email-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["Email"]]></textFieldExpression>
</textField>
<textField>
<reportElement key="listenerGradeProgressHdr" style="ClmHdr" isPrintRepeatedValues="false" x="659" y="0" width="131" height="55" uuid="33922d22-5cb3-468f-a847-7de7f3c5df8b">
<property name="com.jaspersoft.studio.unit.width" value="px"/>
<property name="com.jaspersoft.studio.unit.x" value="px"/>
<property name="com.jaspersoft.studio.element.name" value="progress-hdr"/>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA["Уровень освоения (баллы)"]]></textFieldExpression>
</textField>
</band>
</columnHeader>
<detail>
<band height="50" splitType="Stretch">
<property name="com.jaspersoft.studio.unit.height" value="px"/>
<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="courseId" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="132" height="50" backcolor="#FFFFFF" uuid="e01cae5a-1224-4d35-a671-dc24d0dc2e79">
<property name="com.jaspersoft.studio.element.name" value="id-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>
</textField>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="courseTitle" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="132" y="0" width="132" height="50" uuid="e099966b-6e68-4bc5-9ac3-28d92d67275f">
<property name="com.jaspersoft.studio.element.name" value="course-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{courseTitle}]]></textFieldExpression>
</textField>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="enrollmentDate" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="264" y="0" width="132" height="50" uuid="7144ea06-3947-4ead-8b61-e6ec4a1605b1">
<property name="com.jaspersoft.studio.element.name" value="date-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{enrollmentDate}]]></textFieldExpression>
</textField>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="listenerFullName" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="396" y="0" width="132" height="50" uuid="e86e802b-9a3a-41e6-a38b-b2a74315940b">
<property name="com.jaspersoft.studio.element.name" value="listener-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{listenerFullName}]]></textFieldExpression>
</textField>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="listenerEmail" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="528" y="0" width="131" height="50" uuid="86b01f71-f4de-4199-b724-a9a5357e8a47">
<property name="com.jaspersoft.studio.element.name" value="email-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{listenerEmail}]]></textFieldExpression>
</textField>
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="gradeProgressInPercent" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="659" y="0" width="131" height="50" uuid="63d80376-ad5d-455e-8b1c-6fa4841ce2e1">
<property name="com.jaspersoft.studio.element.name" value="progress-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{gradeProgressInPercent}]]></textFieldExpression>
</textField>
</band>
</detail>
<noData>
<band height="71" splitType="Stretch">
<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
<staticText>
<reportElement style="NoData" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="790" height="71" isRemoveLineWhenBlank="true" uuid="318c618b-4af1-4ad5-a121-31a97904df7e"/>
<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="19"/>
</textElement>
<text><![CDATA[Для данного курса нет зарегистрированных слушателей]]></text>
</staticText>
</band>
</noData>
</jasperReport>
Немного слов о связывании полей шаблона с соответствующими полями класса.
Рассмотрим для примера поле «Идентификатор курса» (courseId) в классе ListenersGradeProgress:
/**
* Идентификатор курса
*/
private Long courseId;
public Long getCourseId(){
return courseId;
}
Обратите внимание на тип поля – Long.
Для того, чтобы связывание этого поля с соответствующим полем в отчете (field):
<field name="courseId" class="java.lang.Long">
<fieldDescription><![CDATA[Course identification number]]></fieldDescription>
</field>
произошло успешно, необходимо выполнить следующие условия:
1) типы полей должны совпадать (в нашем случае Long).
2) названия* полей должны совпадать.
* Имеется ввиду, что если к примеру field отчета имеет название courseId, то в таком случае, в Java классе должен присутствовать соответствующий getter, наименование которого соответствует стандартам наименования Java: getCourseId().
Для отображения значения поля на экран, в JasperReports могут использоваться различные элементы отчета. В нашем примере все значения выводятся с помощью элементов Text Field.
<textField textAdjust="StretchHeight" isBlankWhenNull="true">
<reportElement key="courseId" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="132" height="50" backcolor="#FFFFFF" uuid="e01cae5a-1224-4d35-a671-dc24d0dc2e79">
<property name="com.jaspersoft.studio.element.name" value="id-dtl"/>
</reportElement>
<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
<font fontName="SansSerif" size="11"/>
</textElement>
<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>
</textField>
Обратите внимание на место, где происходит связывание text field с field отчета:
<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>
В дизайнере отчетов эта связь задается с помощью редактора выражений.

Генерирование отчета
Следующий шаг интеграции – это непосредственно само генерирование отчета.
Создадим простой сервис для работы с отчетами. Мы будем обращаться к его методам из веб-интерфейса.
import org.springframework.stereotype.Service;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleXlsxReportConfiguration;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
@Slf4j
@Service
public class JasperReportsService {
public InputStream getReportAsStream() {
InputStream s;
try {
// В данном примере, путь к файлу шаблона отчета: src/main/resources/reports.jasper/..
s = getClass().getResourceAsStream("/reports.jasper/for_article.jrxml");
}
catch (Exception e) {
log.error("[Jasper Reports Service. Get Report As Stream]: " +
"Error occurred while getting resource (report jrxml file) as stream. " +
"Details: {}",
ExceptionUtils.getStackTrace(e));
s = null;
}
return s;
}
public StreamedContent generateListenersGradeProgressReport()
{
DefaultStreamedContent sc = DefaultStreamedContent.builder().build();
JasperReport r;
JasperPrint jp;
try {
r = JasperCompileManager.compileReport(getReportAsStream());
} catch (Exception e) {
log.error(
"[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
"Error occurred while compiling the report. " +
"Details: {}",
ExceptionUtils.getStackTrace(e));
return sc;
}
//Метод findListenersGradeProgressReportData возвращает данные по успеваемости обучающихся из хранилища данных.
//Реализация метода опущена.
List<ListenersGradeProgress> reportData = findListenersGradeProgressReportData();
if (reportData == null) {
return sc;
}
//Второй параметр выставляем в false.
//Это означает, что мы не хотим, чтобы описание полей (тег <fieldDescription>)
//в шаблоне отчета (файл .jrxml) использовалось в качестве синонимов полей отчета
//при отображении их на поля Java класса, описывающего предметную область.
var s = new JRBeanCollectionDataSource(reportData, false);
var p = new HashMap<String, Object>();
//Устанавливаем этот параметр в true, чтобы информация не разбивалась на страницы (листы в электронных таблицах),
//а оставалась на одной единственной странице (листе)
p.put(JRParameter.IS_IGNORE_PAGINATION, true);
p.put(JRParameter.REPORT_LOCALE, new Locale("ru", "RU"));
try {
jp = JasperFillManager.fillReport(r, p, s);
} catch (Exception e) {
log.error("[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
"Error occurred while filling the report. " +
"Details: {}",
ExceptionUtils.getStackTrace(e));
return sc;
}
var os = new ByteArrayOutputStream();
var exp = new JRXlsxExporter();
var conf = new SimpleXlsxReportConfiguration();
conf.setRemoveEmptySpaceBetweenRows(true);
conf.setRemoveEmptySpaceBetweenColumns(true);
conf.setSheetNames(new String[]{"Успеваемость слушателей"});
conf.setDetectCellType(true);
conf.setWrapText(true);
conf.setCollapseRowSpan(false);
conf.setWhitePageBackground(false);
conf.setFreezeRow(2);
exp.setConfiguration(conf);
exp.setExporterInput(new SimpleExporterInput(jp));
exp.setExporterOutput(new SimpleOutputStreamExporterOutput(os));
try {
exp.exportReport();
sc = DefaultStreamedContent.builder()
.name("название_отчета.xlsx")
.contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.contentLength(os.size())
.stream(()
-> new ByteArrayInputStream(os.toByteArray())
)
.build();
} catch (Exception e) {
log.error("[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
"Error occurred while exporting the report. " +
"Details: {}",
ExceptionUtils.getStackTrace(e));
}
return sc;
}
}
Теперь напишем класс, который будет доступен для JSF страницы, и который будет обрабатывать запрос от пользователя на формирование отчета.
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.Scope;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import <your services package path> .JasperReportsService;
@Slf4j
@Scope("view")
@Component
@Getter
@Setter
public class AdminCoursesManagementBean{
@Autowired
private JasperReportsService jasperReportsService;
public StreamedContent downloadListenersGradeProgressReport() {
StreamedContent c = DefaultStreamedContent.builder().build();
try {
//Вызываем метод, созданного нами сервиса
c = jasperReportsService.generateListenersGradeProgressReport();
} catch (Exception e) {
log.error("[Admin Courses Management. Download Listeners Grade Progress Report]: " +
"Error occurred while generating the report. " +
"Details: {}",
ExceptionUtils.getStackTrace(e));
}
if(c.getStream() == null){
log.error("[Admin Courses Management. Download Listeners Grade Progress Report]: " +
"Error occurred while downloading the report. " +
"Details: Streamed content is null!");
}
return c;
}
}
Наконец, напишем разметку JSF страницы (.xhtml) (для краткости приведен лишь участок разметки, реализующий нужный нам функционал):
<p:menuButton value="Операции">
<p:menuitem value="Операция такая-то" action="some action here"
icon="some icon here"/>
<p:menuitem ajax="false" immediate="true">
<i class="fa fa-file-excel-o" style="display: table-cell;
vertical-align: middle;"/>
<p:outputLabel style="display: table-cell; cursor: pointer;" value="Скачать отчет по успеваемости"/>
<p:fileDownload value="#{ adminCoursesManagementBean.downloadListenersGradeProgressReport()}"/>
</p:menuitem>
</p:menuButton>
Результатом работы написанного функционала может быть подобный отчет:

Нюансы
Для экспортирования отчета в формат xlsx мы использовали класс JRXlsxExporter из библиотеки JasperReports.
Существуют некоторые нюансы при работе с данным экспортером, которые необходимо учитывать, чтобы результирующий файл xlsx полностью без искажений соответствовал созданному нами шаблону отчета.
1) Для отображения статического текста (например в заголовках столбцов – раздел Column Header в шаблоне), а также для отображения значений полей отчета (раздел Detail в шаблоне) используйте исключительно элементы Text fields. Не используйте элементы static text. В противном случае, в результирующем файле xlsx данные выведены не будут.
Можно использовать элемент Static text в разделе шаблона No Data.
Коротко об этом разделе:
если результатом выполнения следующего кода:
var s = new JRBeanCollectionDataSource(reportData, false);
будет пустой источник данных и, такое возможно, если reportData является пустой коллекцией (запрос в базу данных вернул отсутствие данных об успеваемости), то в выгружаемом файле будет отображаться именно содержимое раздела No Data, в то время как другие разделы (Column Header и Detail) выведены не будут.

2) Необходимо и рекомендовано использовать универсальные шрифты в шаблоне отчета. Под универсальностью подразумевается гарантированное наличие такого шрифта в любой из следующих операционных систем одновременно: Linux, MacOS, Windows.
К таким шрифтам, к примеру, относится SansSerif.
Не соблюдение этого правила приведет к следующему:
1) Если при разработке шаблона Вы использовали шрифт, который есть в вашей операционной системе, и он не относится к универсальному, то при использовании вашего шаблона приложением, генерирующим отчет, на другой операционной системе или на машине, где нет такого шрифта, движок Jasper Reports выдаст критическое исключение (Exception) и процесс генерирования отчета будет невозможным.
2) Пользователь, если у него в операционной системе нет шрифта, который вы задали в шаблоне отчета, попытавшись открыть сгенерированный с этим шрифтом файл, может столкнуться с искажениями форматирования или отсутствием информации.
Заключение
Библиотека JasperReports является очень мощным инструментом для создания отчетов практически любой формы, содержания и формата.
Ее интеграция в проект достаточна проста и не требует больших затрат времени.
Библиотека постоянно обновляется, поддерживается и бесплатна для коммерческого использования.
Если вы разрабатываете приложение на Java и/или Kotlin, то данная библиотека будет хорошим выбором и может стать вашим любимым инструментом для генерирования отчетности.