![JAR search and dependency download from the Maven repository](/logo.png)
it.discovery.jasperreports.jasper2word.J2WGridPageLayout Maven / Gradle / Ivy
The newest version!
package it.discovery.jasperreports.jasper2word;
import it.discovery.jasperreports.jasper2word.J2WAbstractPrintElementVisitorContext.DocxDocumentPart;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.ExporterFilter;
import net.sf.jasperreports.engine.type.BandTypeEnum;
import net.sf.jasperreports.export.ReportExportConfiguration;
import java.awt.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class represent the output page layout for the document. Every jasper report page has is own layout.
* The layout is building according the elements in the page, via the following algorithm:
*
* -
* The elements are grouping by its document {@link J2WAbstractPrintElementVisitorContext.DocxDocumentPart part}.
*
* -
* A grid is built with the original element positions (x, y, width, height). With a {@link ISimplifierGrid simplifier}
* the grid can be simplified for reducing the spacings, the gaps and the breaks between the elements.
* Then, for each part:
*
*
* -
* The page elements are ordering by its coordinate in the page, in the same order they must be printed in the document.
* So, the elements with minor y position precede the elements with greatest y, and the elements with minor x precede the
* elements with greatest x.
*
* -
* The elements are grouping by relative and absolute positions. So texts, images and tables have relative position,
* because they can't overlap, but they must be appended only in the document. Drawing objects have absolute position
* because it can specify their coordinates in the document.
*
* -
* Elements with relative positions can't be aligned horizontally: two text paragraphs can't be in the same line without using a text area.
* This exporter doesn't want to use text areas. So, a horizontal conflict detecting is performed. All the elements in a conflict
* are inserted in an ad hoc table.
*
* -
* Two or more consecutive tables are merged into a single table.
*
*
* -
* Finally, all the free paragraphs, the tables and the elements with absolute positions are printed out in the document.
*
*
* @author discovery
* @date 10/08/15
*/
public class J2WGridPageLayout {
/** A logger for debug */
private static final Logger log = Logger.getLogger(J2WGridPageLayout.class.getName());
/** The computed positions of the elements */
private final Map elementPositions;
/** All the elements sorted by position, and grouped in theri frame (if one) */
private final Map> sortedElements;
/** All the table in the document */
private final NavigableMap foundTables;
/** The page info */
private final HeaderFooterPageInfo pageInfo;
/**
* Semplifier for the page layout grid.
*/
public interface ISimplifierGrid {
/**
* Return {@code true} if {@code row1} and {@code row2} can be assumed as the same row in the grid.
* @param row1 The row1.
* @param row2 The row2.
* @return {@code true} if the same row, {@code false} otherwise.
*/
boolean canSimplifyRows(int row1, int row2);
/**
* Return {@code true} if {@code col1} and {@code col2} can be assumed as the same column in the grid.
* @param col1 The col1.
* @param col2 The col2.
* @return {@code true} if the same column, {@code false} otherwise.
*/
boolean canSimplifyColumns(int col1, int col2);
}
/**
* Construct and build the grid.
* @param page The jasper report page to export.
* @param configuration The global exporter configuration.
* @param simplifier The simplifier for the grid.
* @param report The report to export.
*/
public J2WGridPageLayout(JRPrintPage page, ReportExportConfiguration configuration, ISimplifierGrid simplifier, JasperPrint report) {
this.elementPositions = new IdentityHashMap<>();
this.sortedElements = new IdentityHashMap<>();
this.foundTables = new TreeMap<>();
// Compute the page size
this.pageInfo = new HeaderFooterPageInfo();
if (report.getBottomMargin() != null)
this.pageInfo.bottomMargin = report.getBottomMargin();
if (report.getTopMargin() != null)
this.pageInfo.topMargin = report.getTopMargin();
if (report.getLeftMargin() != null)
this.pageInfo.leftMargin = report.getLeftMargin();
if (report.getRightMargin() != null)
this.pageInfo.rightMargin = report.getRightMargin();
this.pageInfo.pageSize = new Dimension(report.getPageWidth(), report.getPageHeight());
// Build grid
this.fillPositions(page, configuration, simplifier);
}
/**
* List all the elements in the frame {@code parent}, ordered by their position.
* @param parent The frame of the elements, or {@code null} for the elements in the root page.
* @return The elements in the frame or in the root of the page.
*/
public Collection listJRPrintElements(JRPrintFrame parent) {
NavigableMap elements = this.sortedElements.get(parent);
if (elements == null)
return null;
else
return elements.values();
}
/**
* Return the element position in the grid layout.
* @param element The elemento to find.
* @return The position in the grid.
*/
public ComponentPosition getElementPosition(JRPrintElement element) {
return this.elementPositions.get(element);
}
/**
* List all the tables in the document.
* @return The tables in the document.
*/
public Collection listTables() {
return this.foundTables.values();
}
/**
* Build the grid.
* @param page The jasper report page.
* @param configuration The global exporter configuration.
* @param simplifier The grid simplifier.
*/
private void fillPositions(JRPrintPage page, ReportExportConfiguration configuration, ISimplifierGrid simplifier) {
ExporterFilter filter = new DefaultElementFilter(configuration.getExporterFilter());
FoundPositionsStatus foundPositionsStatus = new FoundPositionsStatus();
// Place every elements in the grid page
this.findPositions(foundPositionsStatus, page.getElements(), filter, simplifier, this.elementPositions, null, 0, 0);
// Grouping elements by frame: updating before for eventually table in table
Set> entries = this.elementPositions.entrySet();
Map frameForElement = new IdentityHashMap<>();
for (Entry entry : entries) {
JRPrintElement element = entry.getKey();
ComponentPosition position = entry.getValue();
if (element instanceof JRPrintFrame) {
JRPrintFrame frame = (JRPrintFrame) element;
for (JRPrintElement subElement : (frame).getElements()) {
if (filter.isToExport(subElement)) {
ComponentPosition subPosition = this.elementPositions.get(subElement);
subPosition.setParent(position);
frameForElement.put(subElement, frame);
if (subPosition instanceof ComponentPositionInTable && position instanceof ComponentPositionInTable)
((ComponentPositionInTable) subPosition).getTableInfo().setParent(((ComponentPositionInTable) position).getTableInfo());
}
}
}
}
// Sorting the elements by their position and compute the bands height (header, footer and body)
int elementIndex = 0;
for (Entry entry : this.elementPositions.entrySet()) {
JRPrintElement element = entry.getKey();
ComponentPosition position = entry.getValue();
JRPrintFrame frame = frameForElement.get(element);
NavigableMap groupSortedElements = this.sortedElements.get(frame);
if (groupSortedElements == null) {
groupSortedElements = new TreeMap<>();
this.sortedElements.put(frame, groupSortedElements);
}
groupSortedElements.put(new JRPrintElementComparableKey(element, position, elementIndex++), element);
if (position.getDocumentPart() != null) {
if (position.getDocumentPart() == DocxDocumentPart.FOOTER) {
if (this.pageInfo.footerHeight == null)
this.pageInfo.footerHeight = this.pageInfo.pageSize.height - position.getOriginalY() - this.pageInfo.bottomMargin;
else
this.pageInfo.footerHeight = Math.max(this.pageInfo.footerHeight, this.pageInfo.pageSize.height - position.getOriginalY() - this.pageInfo.bottomMargin);
if (this.pageInfo.minFooter == null)
this.pageInfo.minFooter = this.pageInfo.pageSize.height - position.getOriginalY();
else
this.pageInfo.minFooter = Math.min(this.pageInfo.minFooter, this.pageInfo.pageSize.height - position.getOriginalY());
if (this.pageInfo.maxFooter == null)
this.pageInfo.maxFooter = this.pageInfo.pageSize.height - position.getOriginalY() + position.getHeight();
else
this.pageInfo.maxFooter = Math.min(this.pageInfo.maxFooter, this.pageInfo.pageSize.height - position.getOriginalY() + position.getHeight());
}
else if (position.getDocumentPart() == DocxDocumentPart.HEADER) {
if (this.pageInfo.headerHeight == null)
this.pageInfo.headerHeight = position.getOriginalY() + position.getHeight() - this.pageInfo.topMargin;
else
this.pageInfo.headerHeight = Math.max(this.pageInfo.headerHeight, position.getOriginalY() + position.getHeight() - this.pageInfo.topMargin);
if (this.pageInfo.minHeader == null)
this.pageInfo.minHeader = position.getOriginalY();
else
this.pageInfo.minHeader = Math.min(this.pageInfo.minHeader, position.getOriginalY());
if (this.pageInfo.maxHeader == null)
this.pageInfo.maxHeader = position.getOriginalY() + position.getHeight();
else
this.pageInfo.maxHeader = Math.max(this.pageInfo.maxHeader, position.getOriginalY() + position.getHeight());
}
}
}
// Dump the grid to log
for (Entry> entry : this.sortedElements.entrySet()) {
JRPrintFrame frame = entry.getKey();
if (log.isLoggable(Level.FINE)) {
if (frame == null)
log.fine("Master");
else
log.fine("Frame " + frame.hashCode());
}
for (Entry elementEntry : entry.getValue().entrySet()) {
if (log.isLoggable(Level.FINE)) {
JRPrintElement value = elementEntry.getValue();
String sValue;
if (value instanceof JRPrintText)
sValue = ((JRPrintText) value).getFullText();
else
sValue = value.getClass().getSimpleName();
log.fine(MessageFormat.format("\t{5} - > {6}\t({0},{1}) [sid={2}, pid={3}, uuid={4}]",
value.getX(), value.getY(), value.getSourceElementId(), value.getPrintElementId(), value.getUUID(),
elementEntry.getKey(), sValue));
}
}
}
}
/**
* Place the elements in the grid.
* @param foundPositionsStatus The status of the current stack indexes of elements.
* @param elements The elements to place.
* @param filter The (optional) element filter.
* @param simplifier The simplifier for the grid.
* @param outputPositions Input/output parameter to store the elements position.
* @param parent The parent of this stack ({@code null} for the root page, or the jasper report frame).
* @param offsetX Optional offsetX.
* @param offsetY Optional offsetY.
*/
private void findPositions(FoundPositionsStatus foundPositionsStatus, List elements, ExporterFilter filter, ISimplifierGrid simplifier,
Map outputPositions, ComponentPosition parent, int offsetX, int offsetY) {
// Original grid rows
TreeSet rows = new TreeSet<>();
// Original grid columns
TreeSet columns = new TreeSet<>();
// Computed grid rows
TreeMap remapRows = new TreeMap<>();
// Computed grid columns
TreeMap remapColumns = new TreeMap<>();
// Build the grid
this.buildRawGrid(elements, filter, rows, columns, offsetX, offsetY);
// Simplify the grid
this.simplifyGrid(rows, columns, remapRows, remapColumns, simplifier);
Map currentStackPositions = new IdentityHashMap<>();
// Place the elements into the simplified grid
for (JRPrintElement element : elements) {
if (filter == null || filter.isToExport(element)) {
// Compute the document part
DocxDocumentPart part;
JROrigin origin = element.getOrigin();
if (origin == null) {
origin = new JROrigin(BandTypeEnum.TITLE);
}
if (origin.getReportName() == null) {
if (origin.getBandTypeValue() == BandTypeEnum.PAGE_HEADER)
part = DocxDocumentPart.HEADER;
else if (origin.getBandTypeValue() == BandTypeEnum.PAGE_FOOTER || origin.getBandTypeValue() == BandTypeEnum.LAST_PAGE_FOOTER)
part = DocxDocumentPart.FOOTER;
else
part = DocxDocumentPart.BODY;
}
else
part = DocxDocumentPart.BODY; // Sub-reports always in document body
// Compute the element position in simplified grid
ComponentPosition position = new ComponentPosition(
remapColumns.get(element.getX() + offsetX), remapRows.get(element.getY() + offsetY),
remapColumns.get(element.getWidth() + element.getX() + offsetX) - remapColumns.get(element.getX() + offsetX),
remapRows.get(element.getHeight() + element.getY() + offsetY) - remapRows.get(element.getY() + offsetY), part);
position.setParent(parent);
outputPositions.put(element, position);
currentStackPositions.put(element, position); // for recursive invocation of this method
// Recursive invocation in case of frame
if (element instanceof JRPrintFrame) {
JRPrintFrame frame = (JRPrintFrame) element;
FoundPositionsStatus innerStatus = new FoundPositionsStatus(); // stack status indexes
innerStatus.tableId = foundPositionsStatus.tableId;
this.findPositions(innerStatus, frame.getElements(), filter, simplifier, outputPositions, position, position.x + offsetX, position.y + offsetY);
foundPositionsStatus.tableId = innerStatus.tableId;
}
}
}
// Ordering elements according positions
NavigableMap preOrderingElements = new TreeMap<>();
for (Entry entry : currentStackPositions.entrySet()) {
preOrderingElements.put(new JRPrintElementComparableKey(entry.getKey(), entry.getValue(), foundPositionsStatus.elementIndex++), entry.getKey());
}
// Detect horizonally conflicts
SortedMap> conflicts = this.findConflicts(currentStackPositions, preOrderingElements);
// Build tabled for conflicts
foundPositionsStatus.tableId = this.findTables(conflicts, currentStackPositions, this.foundTables, foundPositionsStatus.tableId, parent);
// Store computed positions
outputPositions.putAll(currentStackPositions);
}
/**
* Build the original grid based on elements coordinates.
* @param elements The elements in the grid.
* @param filter Elements filter.
* @param rows Input/Output parameter which will contain the grid rows.
* @param columns Input/Output parameter which will contain the grid columns.
* @param offsetX Optional offsetX (included in output columns).
* @param offsetY Optional offsetY (included in output rows).
*/
private void buildRawGrid(List elements, ExporterFilter filter, Set rows, Set columns, int offsetX, int offsetY) {
for (JRPrintElement element : elements) {
if (filter == null || filter.isToExport(element)) {
rows.add(element.getY() + offsetY);
rows.add(element.getY() + element.getHeight() + offsetY);
columns.add(element.getX() + offsetX);
columns.add(element.getX() + element.getWidth() + offsetX);
}
}
}
/**
* Simplify the grid via {@code simplifier}.
* @param rows Original grid rows.
* @param columns Original grid columns.
* @param remapRows Map the original row (key) into its correspondent computed row (value).
* @param remapColumns Map the original column (key) into its correspondent computed column (value).
* @param simplifier The grid simplifier.
*/
private void simplifyGrid(Set rows, Set columns, Map remapRows, Map remapColumns,
ISimplifierGrid simplifier) {
int oldValue = 0;
for (Integer row : rows) {
if (simplifier.canSimplifyRows(oldValue, row)) {
remapRows.put(row, oldValue);
}
else {
remapRows.put(row, row);
oldValue = row;
}
}
oldValue = 0;
for (Integer column : columns) {
if (simplifier.canSimplifyColumns(oldValue, column)) {
remapColumns.put(column, oldValue);
}
else {
remapColumns.put(column, column);
oldValue = column;
}
}
}
/**
* Find the horizontal conflicts.
* @param rawElementPositions The elements position.
* @param sortedElements The elements to check, sorted by its position.
* @return A not {@code null} map with detected conflicts. The key identifies the conflict,
* the value contains all the elements in that conflict. Elements that are not in conflict with other are placed
* into a own entry map with a unique conflict id and a singleton set of elements that contains it as value.
*/
private SortedMap> findConflicts(Map rawElementPositions, NavigableMap sortedElements) {
SortedMap> groupsById = new TreeMap<>();
Map groupByElement = new IdentityHashMap<>();
int nextIdConflict = 0;
// Group by report band
List> splitByBandTypeGroup = new ArrayList<>();
BandTypeEnum lastBandType = null;
DocxDocumentPart lastDocxDocumentPart = null;
List currentGroup = null;
for (JRPrintElement element : sortedElements.values()) {
JROrigin origin = element.getOrigin();
if (origin == null)
origin = new JROrigin(BandTypeEnum.TITLE);
BandTypeEnum bandType = origin.getBandTypeValue();
DocxDocumentPart docxDocumentPart = rawElementPositions.get(element).getDocumentPart();
boolean currentConflict = this.canBeInConflict(element);
boolean newGroup = !bandType.equals(lastBandType) || !docxDocumentPart.equals(lastDocxDocumentPart);
String extraLog;
if (newGroup) {
currentGroup = new ArrayList<>();
splitByBandTypeGroup.add(currentGroup);
extraLog = ", New group";
}
else
extraLog = "";
log.fine(MessageFormat.format("Band = {0}, CanConflict = {1}{2}", bandType, currentConflict, extraLog));
currentGroup.add(element);
lastBandType = bandType;
lastDocxDocumentPart = docxDocumentPart;
}
// Forced conflicts
Map conflictByName = new TreeMap<>();
for (List group : splitByBandTypeGroup) {
for (JRPrintElement element : group) {
ComponentPosition position1 = rawElementPositions.get(element);
// Check for relative component position
if (this.canBeInConflict(element)) {
// Check for forced table (by report properties)
String groupName = element.getPropertiesMap().getProperty("jasper2docx.grid.name");
if (groupName != null) {
ConflictPosition idConflicts = conflictByName.get(groupName);
ConflictPosition idConflictsOld = groupByElement.get(element);
if (idConflicts == null) {
idConflicts = new ConflictPosition(position1.getDocumentPart(), nextIdConflict++);
conflictByName.put(groupName, idConflicts);
idConflicts.name = groupName;
}
groupByElement.put(element, idConflicts);
Set namedConflicts = groupsById.get(idConflicts);
if (namedConflicts == null) {
namedConflicts = new HashSet<>();
groupsById.put(idConflicts, namedConflicts);
}
namedConflicts.add(element);
if (idConflictsOld != null && idConflicts.getIdConflict() != idConflicts.getIdConflict()) {
Set movingElements = groupsById.remove(idConflictsOld);
namedConflicts.addAll(movingElements);
for (JRPrintElement movingElement : movingElements) {
groupByElement.put(movingElement, idConflicts);
}
}
}
// Detect conflict between elements: one element with all others.
for (JRPrintElement cmpElement : group) {
if (element != cmpElement && this.canBeInConflict(cmpElement)) {
ComponentPosition position2 = rawElementPositions.get(cmpElement);
// Check for conflict
if (this.checkForElementsConflict(position1, position2)) {
ConflictPosition idConflicts1 = groupByElement.get(cmpElement);
ConflictPosition idConflicts2 = groupByElement.get(element);
ConflictPosition idConflicts;
if (idConflicts1 == null && idConflicts2 == null)
idConflicts = new ConflictPosition(position1.getDocumentPart(), nextIdConflict++);
else if (idConflicts1 != null && idConflicts2 == null)
idConflicts = idConflicts1;
else if (idConflicts1 == null)
idConflicts = idConflicts2;
else if (idConflicts1.getIdConflict() != idConflicts2.getIdConflict()) {
ConflictPosition idConflictsToMove;
if (idConflicts1.getIdConflict() < idConflicts2.getIdConflict()) {
idConflicts = idConflicts1;
idConflictsToMove = idConflicts2;
}
else {
idConflicts = idConflicts2;
idConflictsToMove = idConflicts1;
}
Set elements = groupsById.get(idConflicts);
Set movingElements = groupsById.remove(idConflictsToMove);
elements.addAll(movingElements);
for (JRPrintElement movingElement : movingElements) {
groupByElement.put(movingElement, idConflicts);
}
}
else
idConflicts = null;
if (idConflicts != null) {
groupByElement.put(cmpElement, idConflicts);
groupByElement.put(element, idConflicts);
Set elements = groupsById.remove(idConflicts);
if (elements == null) {
elements = new HashSet<>();
groupsById.put(idConflicts, elements);
}
elements.add(cmpElement);
elements.add(element);
// Compute the area in conflict
idConflicts.setOriginalX(Math.min(idConflicts.getOriginalX(), Math.min(position1.getX(), position2.getX())));
idConflicts.setOriginalY(Math.min(idConflicts.getOriginalY(), Math.min(position1.getY(), position2.getY())));
idConflicts.setWidth(Math.max(idConflicts.getWidth(), Math.max(position1.getOriginalX() + position1.getWidth(), position2.getOriginalX() + position2.getWidth()) - idConflicts.getOriginalX()));
idConflicts.setHeight(Math.max(idConflicts.getHeight(), Math.max(position1.getOriginalY() + position1.getHeight(), position2.getOriginalY() + position2.getHeight()) - idConflicts.getOriginalY()));
groupsById.put(idConflicts, elements);
}
}
}
}
}
if (!groupByElement.containsKey(element)) { // Non-conflict elements
ConflictPosition idConflict = new ConflictPosition(position1.getDocumentPart(), nextIdConflict++);
idConflict.setOriginalX(position1.getOriginalX());
idConflict.setOriginalY(position1.getOriginalY());
idConflict.setWidth(position1.getWidth());
idConflict.setHeight(position1.getHeight());
groupByElement.put(element, idConflict);
HashSet noConflicts = new HashSet<>();
noConflicts.add(element);
groupsById.put(idConflict, noConflicts);
}
}
}
return groupsById;
}
/**
* Check for elements conflict: two elements are in conflict if one of this:
*
* - The the top of element1 is between the top (inclusive) and the bottom (exclusive) of element2.
* - The bottom of element1 is between the the top (exclusive) and the bottom (inclusice) of element2.
*
* @param position1 The element1 position.
* @param position2 The element2 position.
* @return {@code true} if {@code position1} and {@code position2} are in conflict, {@code false} otherwise.
*/
protected boolean checkForElementsConflict(ComponentPosition position1, ComponentPosition position2) {
return ((position1.y >= position2.y && position1.y < position2.y + position2.height) ||
(position1.y + position1.height > position2.y && position1.y + position1.height <= position2.y + position2.height));
}
/**
* Check for element can be in conflict, that is when element has relative position.
* @param element The element to check.
* @return {@code true} if element can be in conflict with other, {@code false} otherwise
*/
protected boolean canBeInConflict(JRPrintElement element) {
return element instanceof JRPrintImage || element instanceof JRPrintText || element instanceof JRPrintFrame;
}
/**
* Build tables for conflicts.
* @param conflicts The conflicts.
* @param rawElementPositions The element positions.
* @param tables Input/Output parameter for built tables.
* @param nextTableId Unique start id for tables.
* @param parent The parent of the tables (es. other table, frame, etc...)
* @return The next free id for new tables.
*/
private int findTables(SortedMap> conflicts, Map rawElementPositions, Map tables, int nextTableId,
ComponentPosition parent) {
List> candidateTableGroups = new ArrayList<>();
List currentGroup = null;
BandTypeEnum lastBandTypeEnum = null;
boolean prevSingleElementGroup = false;
// Group table by band. Merge table in column/group header of jasper report with their detail.
// A new table will build if found a column/group header after a detail.
for (Set elements : conflicts.values()) {
boolean newGroup = false;
if (elements.size() == 1) {
newGroup = true;
prevSingleElementGroup = true;
}
else {
BandTypeEnum bandTypeEnum = elements.iterator().next().getOrigin().getBandTypeValue();
if (lastBandTypeEnum == null || lastBandTypeEnum.compareTo(bandTypeEnum) > 0)
newGroup = true;
else if (prevSingleElementGroup)
newGroup = true;
lastBandTypeEnum = bandTypeEnum;
prevSingleElementGroup = false;
}
if (newGroup) {
currentGroup = new ArrayList<>();
candidateTableGroups.add(currentGroup);
}
currentGroup.addAll(elements);
}
// Build the table (for conflicts with more then one element)
for (List elements : candidateTableGroups) {
if (elements.size() > 1) {
NavigableSet rows = new TreeSet<>();
NavigableSet columns = new TreeSet<>();
boolean forceBreak = false;
boolean forceRowHeight = true;
DocxDocumentPart band = null;
for (JRPrintElement element : elements) {
if (this.canBeInConflict(element)) {
ComponentPosition position = rawElementPositions.get(element);
int col = position.getOriginalX();
int row = position.getOriginalY();
rows.add(row);
rows.add(row + position.getHeight());
columns.add(col);
columns.add(col + position.getWidth());
if (!forceBreak && element.getOrigin().getBandTypeValue() == BandTypeEnum.DETAIL)
forceBreak = true;
if (forceRowHeight && element.getOrigin().getBandTypeValue() == BandTypeEnum.DETAIL)
forceRowHeight = false;
}
if (band == null)
band = rawElementPositions.get(element).getDocumentPart();
}
if (band == null)
band = DocxDocumentPart.BODY;
if (columns.size() > 2) {
int tableRows[] = new int[rows.size() - 1];
int tableColumns[] = new int[columns.size() - 1];
Integer oldInt = null;
int index = 0;
for (Integer row : rows) {
if (oldInt != null)
tableRows[index++] = row - oldInt;
oldInt = row;
}
oldInt = null;
index = 0;
for (Integer col : columns) {
if (oldInt != null)
tableColumns[index++] = col - oldInt;
oldInt = col;
}
int checkMaxRow = 0;
int checkMaxCol = 0;
int rightCellMerged[][] = new int[tableRows.length][tableColumns.length];
for (int row = 0; row < tableRows.length; row++) {
for (int col = 0; col < tableColumns.length; col++) {
rightCellMerged[row][col] = col;
}
}
ComponentTableInfo tableInfo = new ComponentTableInfo(band, nextTableId++, tableRows, tableColumns, rightCellMerged);
tableInfo.setOriginalX(columns.first());
tableInfo.setOriginalY(rows.first());
tableInfo.setWidth(columns.last() - columns.first());
tableInfo.setHeight(rows.last() - rows.first());
tableInfo.setBordered(false);
tableInfo.setForceRowHeight(true);
tableInfo.setParent(parent);
tables.put(tableInfo.getTableId(), tableInfo);
for (JRPrintElement element : elements) {
if (this.canBeInConflict(element)) {
ComponentPositionInTable positionInTable = new ComponentPositionInTable(band, tableInfo);
positionInTable.setParent(parent);
ComponentPosition position = rawElementPositions.get(element);
positionInTable.setOriginalX(position.getOriginalX());
positionInTable.setOriginalY(position.getOriginalY());
positionInTable.setWidth(position.getWidth());
positionInTable.setHeight(position.getHeight());
int originalCol = columns.headSet(position.getOriginalX(), false).size();
int originalRow = rows.headSet(position.getOriginalY(), false).size();
positionInTable.setCol(originalCol);
positionInTable.setRow(originalRow);
positionInTable.setColSpan(columns.headSet(position.getOriginalX() + position.getWidth(), false).size() - originalCol);
positionInTable.setRowSpan(rows.headSet(position.getOriginalY() + position.getHeight(), false).size() - originalRow);
checkMaxCol = Math.max(checkMaxCol, originalCol + positionInTable.getColSpan());
checkMaxRow = Math.max(checkMaxRow, originalRow + positionInTable.getRowSpan());
rawElementPositions.put(element, positionInTable);
// Compute row/column span
if (positionInTable.getColSpan() > 1 || positionInTable.getRowSpan() > 1) {
Rectangle mergedRegion = new Rectangle(originalCol, originalRow, positionInTable.getColSpan(), positionInTable.getRowSpan());
tableInfo.mergedRegions.add(mergedRegion);
if (mergedRegion.width > 1) {
for (int rowOffset = 0; rowOffset < positionInTable.getRowSpan(); rowOffset++) {
int row = positionInTable.getRow() + rowOffset;
//noinspection ManualArrayCopy
for (int col = originalCol + 1; col < originalCol + mergedRegion.width; col++) {
rightCellMerged[row][col] = rightCellMerged[row][col - 1];
}
for (int col = originalCol + mergedRegion.width; col < tableColumns.length; col++) {
rightCellMerged[row][col] = rightCellMerged[row][col] - (mergedRegion.width - 1);
}
}
}
}
}
}
if (checkMaxCol != tableColumns.length || checkMaxRow != tableRows.length)
throw new JRRuntimeException("Build table error");
// Remap column index for merged cells
for (JRPrintElement element : elements) {
if (this.canBeInConflict(element)) {
ComponentPositionInTable position = (ComponentPositionInTable) rawElementPositions.get(element);
int colMerged = rightCellMerged[position.getRow()][position.getCol()];
position.setCol(colMerged);
}
}
}
}
}
return nextTableId;
}
/**
* Return the page info for this layout.
* @return The page info for this layout.
*/
public HeaderFooterPageInfo getPageInfo() {
return pageInfo;
}
/**
* Page info, like margin, size, header and footer band height.
*/
public static class HeaderFooterPageInfo {
/** Header band height */
private Integer headerHeight;
/** Footer band height */
private Integer footerHeight;
/** The top edge for the header */
private Integer minHeader;
/** The bottom edge for the header */
private Integer maxHeader;
/** The top edge for the footer */
private Integer minFooter;
/** The top edge for the footer */
private Integer maxFooter;
/** Right page margin */
private int rightMargin;
/** Left page margin */
private int leftMargin;
/** Top page margin */
private int topMargin;
/** Bottom page margin */
private int bottomMargin;
/** The page size, margin inclusive */
private Dimension pageSize;
/** Constructor */
private HeaderFooterPageInfo() {
}
/**
* Return then header band height in points.
* @return The header band height in points.
*/
public Integer getHeaderHeight() {
return headerHeight;
}
/**
* Return then footer band height in points.
* @return The footer band height in points.
*/
public Integer getFooterHeight() {
return footerHeight;
}
/**
* Return the page size in points (width and height). Any modification of returned object is not backed to
* the original one.
* @return The page size in points (width and height).
*/
public Dimension getPageSize() {
return new Dimension(pageSize);
}
/**
* Return the right page margin in points.
* @return The right page margin in points.
*/
public int getRightMargin() {
return rightMargin;
}
/**
* Return the left page margin in points.
* @return The left page margin in points.
*/
public int getLeftMargin() {
return leftMargin;
}
/**
* Return the top page margin in points.
* @return The top page margin in points.
*/
public int getTopMargin() {
return topMargin;
}
/**
* Return the bottom page margin in points.
* @return The bottom page margin in points.
*/
public int getBottomMargin() {
return bottomMargin;
}
/**
* Return the top edge for the header in points.
* @return The top edge for the header in points.
*/
public Integer getMinHeader() {
return minHeader;
}
/**
* Return the bottom edge for the header in points.
* @return The bottom edge for the header in points.
*/
public Integer getMaxHeader() {
return maxHeader;
}
/**
* Return the top edge for the footer in points.
* @return The top edge for the footer in points.
*/
public Integer getMinFooter() {
return minFooter;
}
/**
* Return the bottom edge for the footer in points.
* @return The bottom edge for the footer in points.
*/
public Integer getMaxFooter() {
return maxFooter;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof HeaderFooterPageInfo)) return false;
HeaderFooterPageInfo that = (HeaderFooterPageInfo) o;
if (headerHeight != null ? !headerHeight.equals(that.headerHeight) : that.headerHeight != null)
return false;
//noinspection SimplifiableIfStatement
if (footerHeight != null ? !footerHeight.equals(that.footerHeight) : that.footerHeight != null)
return false;
return pageSize.equals(that.pageSize);
}
public int hashCode() {
int result = headerHeight != null ? headerHeight.hashCode() : 0;
result = 31 * result + (footerHeight != null ? footerHeight.hashCode() : 0);
result = 31 * result + pageSize.hashCode();
return result;
}
/**
* Return if there is an header or footer.
* @return {@code true} is there is an header or footer.
*/
public boolean containsHeaderFooter() {
return this.footerHeight != null || this.headerHeight != null;
}
}
/**
* The new coordinate for elements in simplified grid layout.
*/
public class ComponentPosition implements Comparable {
/** The x position (in points) */
private int x;
/** The y position (in points) */
private int y;
/** The width (in points) */
private int width;
/** The height (in points) */
private int height;
/** Optional parent (ex.table) */
private ComponentPosition parent;
/** The document part */
private final DocxDocumentPart documentPart;
/**
* Construct the coordinate with the document part.
* @param documentPart The document part.
*/
public ComponentPosition(DocxDocumentPart documentPart) {
this.documentPart = documentPart;
}
/**
* Construct the coordinate.
* @param x The x position
* @param y The y position
* @param width The width
* @param height The height
* @param documentPart The document part
*/
public ComponentPosition(int x, int y, int width, int height, DocxDocumentPart documentPart) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.documentPart = documentPart;
}
/**
* Return the x position relative to the right margin.
* @return The x position relative to the right margin.
*/
public int getX() {
return x; //- pageInfo.getRightMargin();
}
/**
* Set the x position.
* @param x The x position.
*/
public void setOriginalX(int x) {
this.x = x;
}
/**
* Return the y position, relative the document part and the top and bottom margin.
* @return The y position, relative the document part and the top and bottom margin.
*/
public int getY() {
HeaderFooterPageInfo pageInfo = getPageInfo();
int header = 0;
int footer = 0;
if (pageInfo.getHeaderHeight() != null)
header = pageInfo.getHeaderHeight();
if (pageInfo.getFooterHeight() != null)
footer = pageInfo.getFooterHeight();
DocxDocumentPart calcBand = this.getDocumentPart();
if (calcBand == DocxDocumentPart.HEADER)
return y + pageInfo.getTopMargin();
else if (calcBand == DocxDocumentPart.FOOTER)
return y - (pageInfo.getPageSize().height - footer - pageInfo.getBottomMargin());
else
return y - header - pageInfo.getTopMargin();
}
/**
* Set the y position.
* @param y The y position.
*/
public void setOriginalY(int y) {
this.y = y;
}
/**
* Return the x position.
* @return The x position.
*/
public int getOriginalX() {
return this.x;
}
/**
* Return the y position.
* @return The y position.
*/
public int getOriginalY() {
return this.y;
}
/**
* Return the width.
* @return The width.
*/
public int getWidth() {
return width;
}
/**
* Set the width.
* @param width The width.
*/
public void setWidth(int width) {
this.width = width;
}
/**
* Return the height.
* @return The height.
*/
public int getHeight() {
return height;
}
/**
* Set the height.
* @param height The height.
*/
public void setHeight(int height) {
this.height = height;
}
/**
* Return the {@link DocxDocumentPart document part}.
* @return The document part.
*/
public DocxDocumentPart getDocumentPart() {
return this.documentPart;
}
/**
* Return the container position of this, if any.
* @return The position of the parent or {@code null} if the element is in the root of page.
*/
public ComponentPosition getParent() {
return parent;
}
/**
* Set the parent for this position.
* @param parent The parent.
*/
public void setParent(ComponentPosition parent) {
this.parent = parent;
}
/**
* Sort the position.
* @param o The other position.
* @see J2WGridPageLayout
*/
@SuppressWarnings({"SuspiciousNameCombination", "NullableProblems"})
@Override
public int compareTo(ComponentPosition o) {
return this.compareResult(
Integer.compare(this.y, o.y),
Integer.compare(this.x, o.x),
this.compareParents(o));
}
/**
* Safe parent comparison.
* @param o The other position.
* @return A negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*/
private int compareParents(ComponentPosition o) {
if (this.parent == null && o.parent != null)
return 1;
else if (this.parent != null && o.parent == null)
return -1;
else if (this.parent == null)
return 0;
else
return this.parent.compareTo(o.parent);
}
/**
* Return first non zero integer or zero otherwise.
* @param compareRes Comparison results.
* @return The first non zero integer or zero otherwise.
*/
protected int compareResult(int ... compareRes) {
for (int cmp: compareRes) {
if (cmp != 0)
return cmp;
}
return 0;
}
@Override
public String toString() {
return MessageFormat.format("[(x={0},y={1}) -> (w={2},h={3})]", this.x, this.y, this.width, this.height);
}
}
/**
* Position inside a table. Same as {@link ComponentPosition}, but with row, col, rowspan, colspan and table position.
*/
public class ComponentPositionInTable extends ComponentPosition {
/** The row in the table (zero index based) */
private int row;
/** The column in the table (zero index based) */
private int col;
/** The row span (at least one) */
private int rowSpan;
/** The column span (at least one) */
private int colSpan;
/** The table position */
private final ComponentTableInfo tableInfo;
/**
* Construct a new position in table.
* @param band The document part.
* @param tableInfo The table position.
*/
public ComponentPositionInTable(DocxDocumentPart band, ComponentTableInfo tableInfo) {
super(band);
this.tableInfo = tableInfo;
}
/**
* Return the row in the table (zero index based).
* @return The row in table.
*/
public int getRow() {
return row;
}
/**
* Set the row in table (zero index based).
* @param row The row in table.
*/
public void setRow(int row) {
this.row = row;
}
/**
* Return the column in table (zero index based).
* @return The column in table.
*/
public int getCol() {
return col;
}
/**
* Set the column in table (zero index based).
* @param col The column in table.
*/
public void setCol(int col) {
this.col = col;
}
/**
* Return the row span (at least one).
* @return The row span.
*/
public int getRowSpan() {
return rowSpan;
}
/**
* Set the row span (at least one).
* @param rowSpan The row span.
*/
public void setRowSpan(int rowSpan) {
this.rowSpan = rowSpan;
}
/**
* Return the column span (at least one).
* @return The column span.
*/
public int getColSpan() {
return colSpan;
}
/**
* Set the column span (al least one).
* @param colSpan The column span.
*/
public void setColSpan(int colSpan) {
this.colSpan = colSpan;
}
/**
* Return the table position.
* @return The table position.
*/
public ComponentTableInfo getTableInfo() {
return tableInfo;
}
@Override
public int compareTo(ComponentPosition o) {
if (o instanceof ComponentPositionInTable) {
ComponentPositionInTable that = (ComponentPositionInTable) o;
return this.compareResult(super.compareTo(o),
Integer.compare(this.tableInfo.getTableId(), that.tableInfo.getTableId()),
Integer.compare(this.row, that.row),
Integer.compare(this.col, that.col));
}
else
return super.compareTo(o);
}
@Override
public String toString() {
return super.toString() + MessageFormat.format("[(c={0},r={1}) -> (w={2},h={3}), tableId={4}]", this.col, this.row, this.colSpan, this.rowSpan, this.tableInfo.getTableId());
}
}
/**
* Table position.
*/
public class ComponentTableInfo extends ComponentPosition {
/** A unique table id */
private final int tableId;
/** Size of rows */
private final int rows[];
/** Size of columns */
private final int columns[];
/** Draw table border */
private boolean bordered;
/** Exact row height */
private boolean forceRowHeight;
/** Merged cells */
private final List mergedRegions;
/** The name for the table */
private String name;
/** The table cells coordinates for merged regions */
private final int mapMergedCell[][];
/**
* Construct a table position.
* @param band The document part.
* @param tableId The unique table id.
* @param rows The rows size, so {@code rows.length} is the number of rows and {@code rows[i]} is the size in points of {@code i-row}.
* @param columns The columns size, so {@code columns.length} is the number of columns and {@code coluns[i]} is the size in points of {@code i-column}.
* @param mapMergedCell Remap table cell coordinates for merged cell.
*/
private ComponentTableInfo(DocxDocumentPart band, int tableId, int[] rows, int[] columns, int mapMergedCell[][]) {
super(band);
this.tableId = tableId;
this.rows = rows;
this.columns = columns;
this.mergedRegions = new ArrayList<>();
this.mapMergedCell = mapMergedCell;
}
/**
* Return the unique id of the table.
* @return The unique id of the table.
*/
public int getTableId() {
return tableId;
}
/**
* Return the rows size.
* {@code rows.length} is the number of rows and {@code rows[i]} is the size in points of {@code i-row}.
* @return The rows size
*/
public int[] getRows() {
return rows;
}
/**
* Return the rows size.
* {@code columns.length} is the number of columns and {@code coluns[i]} is the size in points of {@code i-column}.
* @return The columns size
*/
public int[] getColumns() {
return columns;
}
/**
* Return if table has borders.
* @return {@code true} if table has borders, {@code false} otherwise.
*/
public boolean isBordered() {
return bordered;
}
/**
* Set if table has borders.
* @param bordered {@code true} if table has borders, {@code false} otherwise.
*/
public void setBordered(boolean bordered) {
this.bordered = bordered;
}
/**
* Return if the table row size must be exact.
* @return {@code true} if the row size must be exact, {@code false} for automatic.
*/
public boolean isForceRowHeight() {
return forceRowHeight;
}
/**
* Set if the table row size must be exact.
* @param forceRowHeight {@code true} if the row size must be exact, {@code false} for automatic.
*/
public void setForceRowHeight(boolean forceRowHeight) {
this.forceRowHeight = forceRowHeight;
}
/**
* Return the cells merged of the table. A region is a rectangle with its x and y coordinates are the column and row of the table and
* the width and height are the column span and row span.
* @return The cells merged of the table.
*/
public List getMergedRegions() {
return Collections.unmodifiableList(mergedRegions);
}
/**
* Convert absolute coordinates of table into relating ones, in case of merged cells.
* @param column The absolute column.
* @param row The absolute row.
* @return The relating coordinates in table.
*/
public Point getCellCoordinate(int column, int row) {
return new Point(this.mapMergedCell[row][column], row);
}
/**
* Convert absolute coordinates of table into relating ones, in case of merged cells.
* @param coord The absolute coordinates.
* @return The relating coordinates in table.
*/
public Point getCellCoordinate(Point coord) {
return this.getCellCoordinate(coord.x, coord.y);
}
/**
* Convert relating coordinates in table into absolute ones, in case of merged cells.
* @param column The relative column.
* @param row The relative row.
* @return The absolute coordinates in table.
*/
public Point getInverseCellCoordinate(int column, int row) {
int[] ints = this.mapMergedCell[row];
for (int i = 0; i < ints.length; i++) {
int col = ints[i];
if (col == column)
return new Point(i, row);
}
throw new IllegalArgumentException("Invalid cell coordinate: (c=" + column + ", r=" + row + ")");
}
/**
* Convert relating coordinates in table into absolute ones, in case of merged cells.
* @param coord The relative coordinates.
* @return The absolute coordinates in table.
*/
public Point getInverseCellCoordinate(Point coord) {
return this.getInverseCellCoordinate(coord.x, coord.y);
}
/**
* Return the name of the table.
* @return Table's name
*/
public String getName() {
return name;
}
@Override
public String toString() {
return super.toString() + MessageFormat.format(" [rows={0},columns={1}]", this.rows.length, this.columns.length);
}
}
/**
* The conflict area.
*/
private class ConflictPosition extends ComponentPosition {
/** Unique id conflict */
private final int idConflict;
/** Optiona conflict name */
private String name;
/**
* Construct a conflict.
* @param documentPart The document part.
* @param idConflict The unique id conflict.
*/
public ConflictPosition(DocxDocumentPart documentPart, int idConflict) {
super(documentPart);
this.idConflict = idConflict;
this.setOriginalX(Integer.MAX_VALUE);
this.setOriginalY(Integer.MAX_VALUE);
this.setWidth(0);
this.setHeight(0);
}
/**
* Return the name of the conflict.
* @return The name of the conflict.
*/
public String getName() {
return name;
}
/**
* Return the unique id conflict.
* @return The unique id conflict.
*/
public int getIdConflict() {
return idConflict;
}
@Override
public int compareTo(ComponentPosition o) {
int res = super.compareTo(o);
if (res == 0 && o instanceof ConflictPosition)
res = Integer.compare(this.idConflict, ((ConflictPosition) o).idConflict);
return res;
}
}
/**
* A simplifier grid class, that reduce gaps.
*/
public static class GapSimplifierGrid implements ISimplifierGrid {
/** Minimum x gap */
private final int gapX;
/** Minimum y gap */
private final int gapY;
/**
* Construct a grid simplifier with minimum gaps indicated.
* @param gapX The minimum x gap between elements.
* @param gapY The minimum y gap between elements.
*/
public GapSimplifierGrid(int gapX, int gapY) {
this.gapX = gapX;
this.gapY = gapY;
}
/**
* Return the minimum x gap between elements. Two x coordinates distant from each other less then {@code gapX} are
* merged into the minimum x.
* @return The minimum x gap between elements.
*/
public int getGapX() {
return gapX;
}
/**
* Return the minimum y gap between elements. Two y coordinates distant from each other less then {@code gapY} are
* merged into the minimum y.
* @return The minimum y gap between elements.
*/
public int getGapY() {
return gapY;
}
public boolean canSimplifyRows(int row1, int row2) {
return Math.abs(row1 - row2) < this.gapY;
}
public boolean canSimplifyColumns(int col1, int col2) {
return Math.abs(col1 - col2) < this.gapX;
}
}
/**
* A percentage simplifier grid. Two coordinates are similar if the ratio between their gaps and the reference base is less then
* the reference percentage.
* Ex.
* {@code x1} similar {@code x2} if only if {@code abs(x1 - x2) / baseX} <= {@code percentX}
* or else
* {@code y1} similar {@code y2} if only if {@code abs(y1 - y2) / baseY} <= {@code percentY}.
*/
public static class PercentageSimplifierGrid implements ISimplifierGrid {
/** The x percentage tolerance */
private final double percentX;
/** The y percentage tolerance */
private final double percentY;
/** Grid x base */
private final int baseX;
/** Grid y base */
private final int baseY;
/**
* Construct a percent simplifier grid with bases and percentages.
* @param percentX The x percentage tolerance.
* @param percentY The y percentage tolerance.
* @param baseX The grid x base.
* @param baseY The grid y base.
*/
public PercentageSimplifierGrid(double percentX, double percentY, int baseX, int baseY) {
this.percentX = percentX;
this.percentY = percentY;
this.baseX = baseX;
this.baseY = baseY;
}
public boolean canSimplifyRows(int row1, int row2) {
if (row1 == row2)
return true;
else {
int diff = Math.abs(row1 - row2);
return ((double) diff / (double) this.baseX) < this.percentY;
}
}
public boolean canSimplifyColumns(int col1, int col2) {
if (col1 == col2)
return true;
else {
int diff = Math.abs(col1 - col2);
return ((double) diff / (double) this.baseY) < this.percentX;
}
}
}
/**
* Sorting key for element positions.
*/
private static class JRPrintElementComparableKey implements Comparable {
/** Extra id unique value */
private final int index;
/** The element origin */
private final JROriginKey origin;
/** The element type */
private final JRTypeKey type;
/** The element position */
private final ComponentPosition position;
/**
* Construct the sorting key.
* @param element The jasper element.
* @param position The element position.
* @param index An extra unique id.
*/
public JRPrintElementComparableKey(JRPrintElement element, ComponentPosition position, int index) {
this.index = index;
this.origin = new JROriginKey(element.getOrigin());
this.position = position;
if (element instanceof JRPrintText)
this.type = JRTypeKey.TEXT;
else if (element instanceof JRPrintImage)
this.type = JRTypeKey.IMAGE;
else if (element instanceof JRPrintRectangle || element instanceof JRPrintLine || element instanceof JRPrintEllipse)
this.type =JRTypeKey.GRAPHICS;
else if (element instanceof JRPrintFrame)
this.type = JRTypeKey.FRAME;
else
this.type = JRTypeKey.OTHER;
}
public int compareTo(JRPrintElementComparableKey o) {
return this.compareResult(o, this.position.compareTo(o.position));
}
/**
* Return first non-zero values.
* @param other The other value to compare.
* @param compareRes The previous comparison result.
* @return The first non-zero values, or zero otherwise.
*/
private int compareResult(JRPrintElementComparableKey other, int ... compareRes) {
for (int cmp: compareRes) {
if (cmp != 0)
return cmp;
}
return Integer.compare(this.index, other.index);
}
@Override
public String toString() {
return MessageFormat.format("[origin={0}, type={1}, position={2}, index={3}]", this.origin.toString(), this.type.toString(), this.position.toString(), this.index);
}
}
/**
* Origin sorting key.
*/
private static class JROriginKey implements Comparable {
/** Jasper element origin */
private final JROrigin origin;
/**
* Construct the key.
* @param origin The element origin.
*/
public JROriginKey(JROrigin origin) {
if (origin != null)
this.origin = origin;
else
this.origin = new JROrigin(BandTypeEnum.UNKNOWN);
}
public int compareTo(JROriginKey o) {
return this.compareResult(
this.origin.getBandTypeValue().compareTo(o.origin.getBandTypeValue()),
this.compareNullString(this.origin.getReportName(), o.origin.getReportName()),
this.compareNullString(this.origin.getGroupName(), o.origin.getGroupName()));
}
/**
* Return first non-zero values.
* @param compareRes The previous comparison result.
* @return The first non-zero values, or zero otherwise.
*/
private int compareResult(int ... compareRes) {
for (int cmp: compareRes) {
if (cmp != 0)
return cmp;
}
return 0;
}
/**
* String safe comparator.
* @param s1 The first string.
* @param s2 The second string.
* @return The string comparison: a {@code null} value is less then a not {@code null} value.
*/
private int compareNullString(String s1, String s2) {
if (s1 == null && s2 == null)
return 0;
else if (s1 == null)
return -1;
else if (s2 == null)
return 1;
else
return s1.compareTo(s2);
}
@Override
public String toString() {
return MessageFormat.format("[b={0}, r={1}, g={2}]", this.origin.getBandTypeValue(), this.origin.getReportName(), this.origin.getGroupName());
}
}
/**
* Element type sorting key.
*/
private enum JRTypeKey implements Comparable {
/** A text element */
TEXT,
/** An image element */
IMAGE,
/** A generic graphics element (line, ellipse, rectangle) */
GRAPHICS,
/** A jasper report frame */
FRAME,
/** Other... */
OTHER
}
/**
* A simple status index class.
*/
private static class FoundPositionsStatus {
/** The next table id */
private int tableId ;
/** The next unique element id */
private int elementIndex;
/**
* Constructor.
*/
public FoundPositionsStatus() {
this.tableId = 0;
this.elementIndex = 0;
}
}
private static class DefaultElementFilter implements ExporterFilter {
private final ExporterFilter defaultFilter;
private DefaultElementFilter(ExporterFilter defaultFilter) {
this.defaultFilter = defaultFilter;
}
public boolean isToExport(JRPrintElement element) {
boolean res = element != null && !(element instanceof JRGenericPrintElement);
if (false && this.defaultFilter != null)
res = this.defaultFilter.isToExport(element);
return res;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy