All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jasperreports.engine.fill.JRHorizontalFiller Maven / Gradle / Ivy

There is a newer version: 6.21.2
Show newest version
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2016 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see .
 */
package net.sf.jasperreports.engine.fill;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.type.FooterPositionEnum;
import net.sf.jasperreports.engine.type.IncrementTypeEnum;
import net.sf.jasperreports.engine.type.ResetTypeEnum;
import net.sf.jasperreports.engine.type.RunDirectionEnum;


/**
 * @author Teodor Danciu ([email protected])
 */
public class JRHorizontalFiller extends JRBaseFiller
{

	private static final Log log = LogFactory.getLog(JRHorizontalFiller.class);

	private int lastDetailOffsetX = -1;
	private int lastDetailOffsetY = -1;
	private int currentDetailOffsetY;
	private int maxDetailOffsetY;
	

	/**
	 *
	 */
	protected JRHorizontalFiller(
		JasperReportsContext jasperReportsContext, 
		JasperReport jasperReport
		) throws JRException
	{
		this(jasperReportsContext, jasperReport, null);
	}

	/**
	 *
	 */
	public JRHorizontalFiller(
		JasperReportsContext jasperReportsContext, 
		JasperReport jasperReport, 
		BandReportFillerParent parent 
		) throws JRException
	{
		super(jasperReportsContext, jasperReport, parent);

		setPageHeight(pageHeight);
	}


	@Override
	protected void setPageHeight(int pageHeight)
	{
		this.pageHeight = pageHeight;

		columnFooterOffsetY = pageHeight - bottomMargin - pageFooter.getHeight() - columnFooter.getHeight();
		lastPageColumnFooterOffsetY = pageHeight - bottomMargin - lastPageFooter.getHeight() - columnFooter.getHeight();
		
		if (log.isDebugEnabled())
		{
			log.debug("Filler " + fillerId + " - pageHeight: " + pageHeight
					+ ", columnFooterOffsetY: " + columnFooterOffsetY
					+ ", lastPageColumnFooterOffsetY: " + lastPageColumnFooterOffsetY);
		}
	}


	@Override
	protected synchronized void fillReport() throws JRException
	{
		setLastPageFooter(false);

		if (next())
		{
			fillReportStart();

			while (next())
			{
				fillReportContent();
			}

			fillReportEnd();
		}
		else
		{
			if (log.isDebugEnabled())
			{
				log.debug("Fill " + fillerId + ": no data");
			}

			switch (getWhenNoDataType())
			{
				case ALL_SECTIONS_NO_DETAIL :
				{
					if (log.isDebugEnabled())
					{
						log.debug("Fill " + fillerId + ": all sections");
					}

					scriptlet.callBeforeReportInit();
					calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
					scriptlet.callAfterReportInit();

					printPage = newPage();
					addPage(printPage);
					setFirstColumn();
					offsetY = topMargin;
					isFirstPageBand = true;
					isFirstColumnBand = true;

					fillBackground();

					fillTitle();

					fillPageHeader(JRExpression.EVALUATION_DEFAULT);

					fillColumnHeaders(JRExpression.EVALUATION_DEFAULT);

					fillGroupHeaders(true);

					fillGroupFooters(true);

					fillSummary();

					break;
				}
				case BLANK_PAGE :
				{
					if (log.isDebugEnabled())
					{
						log.debug("Fill " + fillerId + ": blank page");
					}

					printPage = newPage();
					addPage(printPage);
					break;
				}
				case NO_DATA_SECTION:
				{
					if (log.isDebugEnabled())
					{
						log.debug("Fill " + fillerId + ": NoData section");
					}

					scriptlet.callBeforeReportInit();
					calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
					scriptlet.callAfterReportInit();

					printPage = newPage();
					addPage(printPage);
					setFirstColumn();
					offsetY = topMargin;
					isFirstPageBand = true;
					isFirstColumnBand = true;

					fillBackground();

					fillNoData();

					break;

				}
				case NO_PAGES :
				default :
				{
					if (log.isDebugEnabled())
					{
						log.debug("Fill " + fillerId + ": no pages");
					}
				}
			}
		}

		recordUsedPageHeight(offsetY + bottomMargin);
		if (ignorePagination)
		{
			jasperPrint.setPageHeight(usedPageHeight);
		}

		if (isSubreport())
		{
			addPageToParent(true);
		}
		else
		{
			addLastPageBookmarks();
		}
		
		if (bookmarkHelper != null)
		{
			jasperPrint.setBookmarks(bookmarkHelper.getRootBookmarks());
		}
	}


	/**
	 *
	 */
	private void fillReportStart() throws JRException
	{
		scriptlet.callBeforeReportInit();
		calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
		scriptlet.callAfterReportInit();

		printPage = newPage();
		addPage(printPage);
		setFirstColumn();
		offsetY = topMargin;
		isFirstPageBand = true;
		isFirstColumnBand = true;

		fillBackground();

		fillTitle();

		fillPageHeader(JRExpression.EVALUATION_DEFAULT);

		fillColumnHeaders(JRExpression.EVALUATION_DEFAULT);

		fillGroupHeaders(true);

		fillDetail();
	}


	private void setFirstColumn()
	{
		columnIndex = 0;
		offsetX = leftMargin;
		setColumnNumberVariable();
	}

	/**
	 *
	 */
	private void fillReportContent() throws JRException
	{
		calculator.estimateGroupRuptures();

		fillGroupFooters(false);

		resolveGroupBoundElements(JRExpression.EVALUATION_OLD, false);
		scriptlet.callBeforeGroupInit();
		calculator.initializeVariables(ResetTypeEnum.GROUP, IncrementTypeEnum.GROUP);
		scriptlet.callAfterGroupInit();

		fillGroupHeaders(false);

		fillDetail();
	}


	/**
	 *
	 */
	private void fillReportEnd() throws JRException
	{
		fillGroupFooters(true);

		fillSummary();
	}


	/**
	 *
	 */
	 private void fillTitle() throws JRException
	 {
		if (log.isDebugEnabled() && !title.isEmpty())
		{
			log.debug("Fill " + fillerId + ": title at " + offsetY);
		}

		title.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

		if (title.isToPrint())
		{
			while (
				title.getBreakHeight() > pageHeight - bottomMargin - offsetY
				)
			{
				addPage(false);
			}

			title.evaluate(JRExpression.EVALUATION_DEFAULT);

			JRPrintBand printBand = title.fill(pageHeight - bottomMargin - offsetY);

			if (title.willOverflow() && title.isSplitPrevented() && !title.isSplitTypePreventInhibited())
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				printBand = 
					title.refill(
						JRExpression.EVALUATION_DEFAULT,
						pageHeight - bottomMargin - offsetY
						);
			}

			fillBand(printBand);
			offsetY += printBand.getHeight();
			isCrtRecordOnPage = true;
			isCrtRecordOnColumn = true;

			while (title.willOverflow())
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				printBand = title.fill(pageHeight - bottomMargin - offsetY);

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;
			}

			resolveBandBoundElements(title, JRExpression.EVALUATION_DEFAULT);

			if (isTitleNewPage)
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);
			}
		}
	}


	/**
	 *
	 */
	private void fillPageHeader(byte evaluation) throws JRException
	{
		if (log.isDebugEnabled() && !pageHeader.isEmpty())
		{
			log.debug("Fill " + fillerId + ": page header at " + offsetY);
		}

		setNewPageColumnInBands();

		pageHeader.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

		if (pageHeader.isToPrint())
		{
			int reattempts = getMasterColumnCount();
			if (isCreatingNewPage)
			{
				--reattempts;
			}

			boolean filled = fillBandNoOverflow(pageHeader, evaluation);

			for (int i = 0; !filled && i < reattempts; ++i)
			{
				resolveGroupBoundElements(evaluation, false);
				resolveColumnBoundElements(evaluation);
				resolvePageBoundElements(evaluation);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				filled = fillBandNoOverflow(pageHeader, evaluation);
			}

			if (!filled)
			{
				throw 
					new JRRuntimeException(
						EXCEPTION_MESSAGE_KEY_PAGE_HEADER_OVERFLOW_INFINITE_LOOP,
						(Object[])null);
			}
		}

		columnHeaderOffsetY = offsetY;

		isNewPage = true;
	}


	private boolean fillBandNoOverflow(JRFillBand band, byte evaluation) throws JRException
	{
		int availableHeight = columnFooterOffsetY - offsetY;
		boolean overflow = availableHeight < band.getHeight();

		if (!overflow)
		{
			band.evaluate(evaluation);
			JRPrintBand printBand = band.fill(availableHeight);

			overflow = band.willOverflow();
			if (overflow)
			{
				band.rewind();
			}
			else
			{
				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
				isCrtRecordOnColumn = isCrtRecordOnPage;

				resolveBandBoundElements(band, evaluation);
			}
		}

		return !overflow;
	}


	/**
	 *
	 */
	private void fillColumnHeaders(byte evaluation) throws JRException
	{
		if (log.isDebugEnabled() && !columnHeader.isEmpty())
		{
			log.debug("Fill " + fillerId + ": column headers at " + offsetY);
		}

		setNewPageColumnInBands();
		isFirstColumnBand = true;

		for(columnIndex = 0; columnIndex < columnCount; columnIndex++)
		{
			setColumnNumberVariable();

			columnHeader.evaluatePrintWhenExpression(evaluation);

			if (columnHeader.isToPrint())
			{
				int reattempts = getMasterColumnCount();
				if (isCreatingNewPage)
				{
					--reattempts;
				}

				boolean fits = columnHeader.getHeight() <= columnFooterOffsetY - offsetY;
				for (int i = 0; !fits && i < reattempts; ++i)
				{
					fillPageFooter(evaluation);

					resolveGroupBoundElements(evaluation, false);
					resolveColumnBoundElements(evaluation);
					resolvePageBoundElements(evaluation);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);

					fillPageHeader(evaluation);

					fits = columnHeader.getHeight() <= columnFooterOffsetY - offsetY;
				}

				if (!fits)
				{
					throw 
						new JRRuntimeException(
							EXCEPTION_MESSAGE_KEY_COLUMN_HEADER_OVERFLOW_INFINITE_LOOP,
							(Object[])null);
				}

				setOffsetX();
				offsetY = columnHeaderOffsetY;

				fillFixedBand(columnHeader, evaluation, false);
			}
		}

		setFirstColumn();

		isNewColumn = true;
	}


	/**
	 *
	 */
	private void fillGroupHeaders(boolean isFillAll) throws JRException
	{
		if (groups != null && groups.length > 0)
		{
			for (int i = 0; i < groups.length; i++)
			{
				JRFillGroup group = groups[i];

				if (isFillAll || group.hasChanged())
				{
					fillGroupHeader(group);
				}
			}
		}
	}


	/**
	 *
	 */
	private void fillGroupHeader(JRFillGroup group) throws JRException
	{
		JRFillSection groupHeaderSection = (JRFillSection)group.getGroupHeaderSection();

		if (log.isDebugEnabled() && !groupHeaderSection.isEmpty())
		{
			log.debug("Fill " + fillerId + ": " + group.getName() + " header at " + offsetY);
		}

		//byte evalPrevPage = (group.isTopLevelChange()?JRExpression.EVALUATION_OLD:JRExpression.EVALUATION_DEFAULT);

		if (
			(group.isStartNewPage() || group.isResetPageNumber()) && !isNewPage
			|| ( group.isStartNewColumn() && !isNewColumn )
			)
		{
			fillPageBreak(
				group.isResetPageNumber(),
				isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD, //evalPrevPage,
				JRExpression.EVALUATION_DEFAULT,
				true
				);
		}

		boolean isFirstHeaderBandToPrint = true;
		boolean isGroupReset = false;
		
		JRFillBand[] groupHeaderBands = groupHeaderSection.getFillBands();
		for (int i = 0; i < groupHeaderBands.length; i++)
		{
			JRFillBand groupHeaderBand = groupHeaderBands[i];

			groupHeaderBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (groupHeaderBand.isToPrint())
			{
				while (
					groupHeaderBand.getBreakHeight() > columnFooterOffsetY - offsetY ||
					(isFirstHeaderBandToPrint && group.getMinHeightToStartNewPage() > columnFooterOffsetY - offsetY)
					)
				{
					fillPageBreak(
						false,
						isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD, //evalPrevPage,
						JRExpression.EVALUATION_DEFAULT,
						true
						);
				}
			}

			if (!isGroupReset && (isFirstHeaderBandToPrint || i == groupHeaderBands.length - 1))
			{
				// perform this group reset before the first header band prints, 
				// or before the last header band, regardless if it prints or not 
				setNewGroupInBands(group);

				group.setFooterPrinted(false);
				group.resetDetailsCount();
				
				isGroupReset = true;
			}

			ElementRange elementRange = null;
			
			if (
				(group.isKeepTogether() && !isNewColumn)
				|| group.getMinDetailsToStartFromTop() > 0
				)
			{
				elementRange = group.getKeepTogetherElementRange();
				
				if (elementRange == null)
				{
					// we need to set a keep together element range for the group
					// even if its header does not print,
					// but only if the column is not already new
					elementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
					
					group.setKeepTogetherElementRange(elementRange);
					// setting a non-null element range here would cause the group header band to be
					// refilled below and thus kept together, in case a split occurs in it;
					// the non-null element range will be also moved onto the new page/column in the process,
					// but it will contain no elements as the already mentioned non-splitting behavior of the group header band
					// would not add any element to it;
					// so the keep together element range set here is more like flag to signal the group header itself
					// should be prevented from splitting in the fillColumnBand call below
				}
			}

			if (groupHeaderBand.isToPrint())
			{
				setFirstColumn();

				fillColumnBand(groupHeaderBand, JRExpression.EVALUATION_DEFAULT);
				
				ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
				
				// in case a column/page break occurred during the filling of the band above,
				// the provided element range is discarded/ignored,
				// but that should not be a problem because the discarded element range was already dealt with during the break, 
				// because it was a keep together element range
				ElementRangeUtil.expandOrIgnore(elementRange, newElementRange);

				isFirstPageBand = false;
				isFirstColumnBand = true;
				
				isFirstHeaderBandToPrint = false;
			}
		}

		group.setHeaderPrinted(true);
	}


	/**
	 *
	 */
	private void fillGroupHeadersReprint(byte evaluation) throws JRException
	{
		if (groups != null && groups.length > 0)
		{
			for (int i = 0; i < groups.length; i++)
			{
				JRFillGroup group = groups[i];
				
				if (
					group.getKeepTogetherElementRange() != null
					&& (group.isKeepTogether() || !group.hasMinDetails())
					)
				{
					// we reprint headers only for groups that are "outer" to the one which 
					// triggered a potential "keep together" move 
					break;
				}

				if (
					group.isReprintHeaderOnEachPage()
					&& (!group.hasChanged() || (group.hasChanged() && group.isHeaderPrinted()))
					)
				{
					fillGroupHeaderReprint(groups[i], evaluation);
				}
			}
		}
	}


	/**
	 *
	 */
	 private void fillGroupHeaderReprint(JRFillGroup group, byte evaluation) throws JRException
	 {
		JRFillSection groupHeaderSection = (JRFillSection)group.getGroupHeaderSection();

		JRFillBand[] groupHeaderBands = groupHeaderSection.getFillBands();
		for (int i = 0; i < groupHeaderBands.length; i++)
		{
			JRFillBand groupHeaderBand = groupHeaderBands[i];

			groupHeaderBand.evaluatePrintWhenExpression(evaluation);

			if (groupHeaderBand.isToPrint())
			{
				setFirstColumn();

				while (groupHeaderBand.getBreakHeight() > columnFooterOffsetY - offsetY)
				{
					fillPageBreak(false, evaluation, evaluation, true); // using same evaluation for both side of the break is ok here
				}

				fillColumnBand(groupHeaderBand, evaluation);

				//isFirstPageBand = false;
				isFirstColumnBand = true;
			}
		}
	}


	/**
	 *
	 */
	private void fillDetail() throws JRException
	{
		if (log.isDebugEnabled() && !detailSection.isEmpty())
		{
			log.debug("Fill " + fillerId + ": detail at " + offsetY);
		}

		if (
			offsetX == lastDetailOffsetX
			&& offsetY == lastDetailOffsetY
			)
		{
			if (columnIndex == columnCount - 1)
			{
				columnIndex = 0;

				maxDetailOffsetY = 0;
			}
			else
			{
				columnIndex++;

				offsetY = currentDetailOffsetY;
			}

		}

		if (!detailSection.areAllPrintWhenExpressionsNull())
		{
			calculator.estimateVariables();
		}

		JRFillBand[] detailBands = detailSection.getFillBands();
		for (int i = 0; i < detailBands.length; i++)
		{
			JRFillBand detailBand = detailBands[i];

			detailBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_ESTIMATED);

			if (detailBand.isToPrint())
			{
				while (detailBand.getHeight() > columnFooterOffsetY - offsetY)
				{
					fillPageBreak(
						false,
						isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD,
						JRExpression.EVALUATION_DEFAULT,
						true
						);
				}
				
				break;
			}
		}

		scriptlet.callBeforeDetailEval();
		calculator.calculateVariables(true);
		scriptlet.callAfterDetailEval();
				
		setColumnNumberVariable();

		setOffsetX();

		currentDetailOffsetY = offsetY;

		detailElementRange = null;

		boolean keepDetailElementRangeForOrphanFooter = true;
		boolean atLeastOneDetailBandPrinted = false;
		
		for (int i = 0; i < detailBands.length; i++)
		{
			JRFillBand detailBand = detailBands[i];

			detailBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (detailBand.isToPrint())
			{
				if (
					keepDetailElementRangeForOrphanFooter
					&& detailElementRange == null
					)
				{
					detailElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
				}
				
				while (detailBand.getHeight() > columnFooterOffsetY - offsetY)
				{
					fillPageBreak(
						false,
						isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD,
						JRExpression.EVALUATION_DEFAULT,
						true
						);

					currentDetailOffsetY = offsetY;
				}
				
				fillFixedBand(detailBand, JRExpression.EVALUATION_DEFAULT, false);

				if (detailElementRange == null)
				{
					// page break occurred so we give up keeping the detail element range altogether
					keepDetailElementRangeForOrphanFooter = false;
				}
				else
				{
					// there was no page break, otherwise this range would have been reset to null during page break
					
					ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);

					ElementRangeUtil.expandOrIgnore(detailElementRange, newElementRange);
				}

				atLeastOneDetailBandPrinted = true;
				
				isFirstPageBand = false;
				isFirstColumnBand = false;
			}
		}

		if (atLeastOneDetailBandPrinted)
		{
			if (groups != null)
			{
				for (JRFillGroup group : groups)
				{
					group.incrementDetailsCount();
				}
			}
		}
	 
		maxDetailOffsetY = maxDetailOffsetY < offsetY ? offsetY : maxDetailOffsetY;
		offsetY = maxDetailOffsetY;

		lastDetailOffsetX = offsetX;
		lastDetailOffsetY = offsetY;
		
		isNewPage = false;
		isNewColumn = false;
	}


	/**
	 *
	 */
	private void fillGroupFooters(boolean isFillAll) throws JRException
	{
		if (groups != null && groups.length > 0)
		{
			byte evaluation = (isFillAll)?JRExpression.EVALUATION_DEFAULT:JRExpression.EVALUATION_OLD;

			preventOrphanFootersMinLevel = null;
			for (int i = groups.length - 1; i >= 0; i--)
			{
				JRFillGroup group = groups[i];
				
				if (
					(isFillAll || group.hasChanged())
					&& group.isPreventOrphanFooter()
					)
				{
					// we need to decide up-front if during the current group footers printing,
					// there are any potential orphans to take care of
					preventOrphanFootersMinLevel = i;
					break;
				}
			}
			
			for (int i = groups.length - 1; i >= 0; i--)
			{
				JRFillGroup group = groups[i];
				
				crtGroupFootersLevel = i;
				if (
					preventOrphanFootersMinLevel != null
					&& crtGroupFootersLevel < preventOrphanFootersMinLevel
					)
				{
					// reset the element ranges when we get to the group footers
					// that are outer to the ones for which we need to prevent orphans;
					// these ranges act like flags to signal we need to deal with orphans
					orphanGroupFooterDetailElementRange = null;
					orphanGroupFooterElementRange = null;
				}
				
				if (isFillAll || group.hasChanged())
				{
					fillGroupFooter(group, evaluation);
					
					// regardless of whether the fillGroupFooter was printed or not, 
					// we just need to mark the end of the group 
					group.setKeepTogetherElementRange(null);
				}
			}
			
			// resetting orphan footer element ranges because all group footers have been rendered
			orphanGroupFooterDetailElementRange = null;
			orphanGroupFooterElementRange = null;
			
			// we need to take care of groupFooterPositionElementRange here because all groups footers have been 
			// rendered and we need to consume remaining space before next groups start;
			//
			// but we don't process the last groupFooterPositionElementRange when the report ends (isFillAll true),
			// because it will be dealt with during summary rendering, depending on whether a last page footer exists or not
			if (
				!isFillAll
				&& groupFooterPositionElementRange != null
				)
			{
				ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
				groupFooterPositionElementRange = null;
				// update the offsetY to signal there is no more space left at the bottom after forcing the footer
				offsetY = columnFooterOffsetY;
			}
		}
	}


	/**
	 *
	 */
	private void fillGroupFooter(JRFillGroup group, byte evaluation) throws JRException
	{
		JRFillSection groupFooterSection = (JRFillSection)group.getGroupFooterSection();

		if (log.isDebugEnabled() && !groupFooterSection.isEmpty())
		{
			log.debug("Fill " + fillerId + ": " + group.getName() + " footer at " + offsetY);
		}
		
		JRFillBand[] groupFooterBands = groupFooterSection.getFillBands();
		for (int i = 0; i < groupFooterBands.length; i++)
		{
			JRFillBand groupFooterBand = groupFooterBands[i];
			
			groupFooterBand.evaluatePrintWhenExpression(evaluation);

			if (groupFooterBand.isToPrint())
			{
				setFirstColumn();

				if (
					preventOrphanFootersMinLevel != null
					&& crtGroupFootersLevel >= preventOrphanFootersMinLevel 
					&& orphanGroupFooterDetailElementRange == null
					)
				{
					// the detail element range can't be null here, unless there is no detail printing;
					// keeping the detail element range in this separate variable signals we are currently
					// dealing with orphan group footers
					orphanGroupFooterDetailElementRange = detailElementRange;
				}
				
				if (
					groupFooterBand.getBreakHeight() > columnFooterOffsetY - offsetY
					)
				{
					fillPageBreak(false, evaluation, evaluation, true); // using same evaluation for both side of the break is ok here
				}

				if (
					groupFooterPositionElementRange == null 
					&& group.getFooterPositionValue() != FooterPositionEnum.NORMAL
					)
				{
					groupFooterPositionElementRange = 
						new SimpleGroupFooterElementRange(
							new SimpleElementRange(getCurrentPage(), columnIndex, offsetY), 
							group.getFooterPositionValue()
							);
				}

				if (groupFooterPositionElementRange != null)
				{
					// keep the current group footer position because it will be needed
					// in case the band breaks and the group footer element range needs to
					// be recreated on the new page
					groupFooterPositionElementRange.setCurrentFooterPosition(group.getFooterPositionValue());
				}
				
				if (orphanGroupFooterDetailElementRange != null)
				{
					ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
					if (orphanGroupFooterElementRange == null)
					{
						orphanGroupFooterElementRange = newElementRange;
					}
					else
					{
						ElementRangeUtil.expandOrIgnore(orphanGroupFooterElementRange, newElementRange);
					}
				}
				
				fillColumnBand(groupFooterBand, evaluation);
				
				ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
					
				if (groupFooterPositionElementRange != null)
				{
					ElementRangeUtil.expandOrIgnore(groupFooterPositionElementRange.getElementRange(), newElementRange);

					switch (group.getFooterPositionValue())
					{
						case STACK_AT_BOTTOM :
						{
							groupFooterPositionElementRange.setMasterFooterPosition(FooterPositionEnum.STACK_AT_BOTTOM);
							break;
						}
						case FORCE_AT_BOTTOM :
						{
							groupFooterPositionElementRange.setMasterFooterPosition(FooterPositionEnum.FORCE_AT_BOTTOM);
							break;
						}
						case COLLATE_AT_BOTTOM :
						{
							break;
						}
						case NORMAL :
						default :
						{
							// only StackAtBottom and CollateAtBottom can get here
							if (groupFooterPositionElementRange.getMasterFooterPosition() == FooterPositionEnum.COLLATE_AT_BOTTOM)
							{
								groupFooterPositionElementRange = null;
							}
							break;
						}
					}
				}

				isFirstPageBand = false;
				isFirstColumnBand = true;
			}
		}

		// we need to perform ForceAtBottom here because only the group footer as a whole should be forced to bottom, 
		// not the individual bands in this footer section;
		// also, when forcing a group footer to bottom, we consider the normal/current columnFooterOffsetY, because it is impossible
		// to tell at this point if this would be the last page or not (last page footer)
		if (
			groupFooterPositionElementRange != null
			&& groupFooterPositionElementRange.getMasterFooterPosition() == FooterPositionEnum.FORCE_AT_BOTTOM
			)
		{
			ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
			groupFooterPositionElementRange = null;
			// update the offsetY to signal there is no more space left at the bottom after forcing the footer
			offsetY = columnFooterOffsetY;
		}

		isNewPage = false;
		isNewColumn = false;

		group.setHeaderPrinted(false);
		group.setFooterPrinted(true);
	}


	/**
	 *
	 */
	 private void fillColumnFooters(byte evaluation) throws JRException
	 {
		if (log.isDebugEnabled() && !columnFooter.isEmpty())
		{
			log.debug("Fill " + fillerId + ": column footers at " + offsetY);
		}

		/*
		if (!isSubreport)
		{
			offsetY = columnFooterOffsetY;
		}
		*/

		if (isSubreport() && !isSubreportRunToBottom())
		{
			columnFooterOffsetY = offsetY;
		}

		int tmpColumnFooterOffsetY = columnFooterOffsetY;

		if (isFloatColumnFooter || ignorePagination)
		{
			tmpColumnFooterOffsetY = offsetY;
		}

		// we first let the column footer Y offset calculations to occur normally above, 
		// before attempting to deal with existing groupFooterPositionElementRange
		if (groupFooterPositionElementRange != null)
		{
			// all types of footer position can get here (StackAtBottom, CollapseAtBottom and ForceAtBottom);
			// ForceAtBottom group footer element ranges could reach this point in case multi-band group footer gets
			// split across a column/page break; remaining bands in such group footer would be dealt at the end 
			// of the group footer filling method (see fillGroupFooter() method above)
			ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
			groupFooterPositionElementRange = null;
			// we do not need to set the offsetY because it has already been set properly earlier in this method;
		}
		
		if (isFloatColumnFooter && !ignorePagination)
		{
			floatColumnFooterElementRange = new SimpleElementRange(getCurrentPage(), 0, tmpColumnFooterOffsetY);
		}
		
		for (columnIndex = 0; columnIndex < columnCount; columnIndex++)
		{
			setColumnNumberVariable();

			setOffsetX();
			offsetY = tmpColumnFooterOffsetY;

			columnFooter.evaluatePrintWhenExpression(evaluation);

			if (columnFooter.isToPrint())
			{
				fillFixedBand(columnFooter, evaluation, false);
			}
		}
		
		if (floatColumnFooterElementRange != null)
		{
			floatColumnFooterElementRange.expand(offsetY);
		}
	}


	/**
	 *
	 */
	private void fillPageFooter(byte evaluation) throws JRException
	{
		JRFillBand crtPageFooter = getCurrentPageFooter();

		if (log.isDebugEnabled() && !crtPageFooter.isEmpty())
		{
			log.debug("Fill " + fillerId + ": " + (isLastPageFooter ? "last " : "") + "page footer at " + offsetY);
		}

		offsetX = leftMargin;

		if ((!isSubreport() || isSubreportRunToBottom()) && !ignorePagination)
		{
			offsetY = pageHeight - crtPageFooter.getHeight() - bottomMargin;
		}

		crtPageFooter.evaluatePrintWhenExpression(evaluation);

		if (crtPageFooter.isToPrint())
		{
			fillFixedBand(crtPageFooter, evaluation);
		}
	}


	/**
	 *
	 */
	private void fillSummary() throws JRException
	{
		if (log.isDebugEnabled() && !summary.isEmpty())
		{
			log.debug("Fill " + fillerId + ": summary at " + offsetY);
		}

		offsetX = leftMargin;

		if (lastPageFooter == missingFillBand)
		{
			if (
				!isSummaryNewPage
				//&& columnIndex == 0
				&& summary.getBreakHeight() <= columnFooterOffsetY - offsetY
				)
			{
				fillSummaryNoLastFooterSamePage();
			}
			else
			{
				fillSummaryNoLastFooterNewPage();
			}
		}
		else
		{
			if (isSummaryWithPageHeaderAndFooter)
			{
				fillSummaryWithLastFooterAndPageBands();
			}
			else
			{
				fillSummaryWithLastFooterNoPageBands();
			}
		}

		resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
		resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
		resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
		resolveReportBoundElements();
		if (isMasterReport())
		{
			resolveMasterBoundElements();
		}
	}


	/**
	 *
	 */
	private void fillSummaryNoLastFooterSamePage() throws JRException
	{
		summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

		if (summary.isToPrint())
		{
			// deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
			if (groupFooterPositionElementRange != null)
			{
				ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
				offsetY = columnFooterOffsetY;
				// reset the element range here although it will not be checked anymore as the report ends
				groupFooterPositionElementRange = null;
			}
			
			summary.evaluate(JRExpression.EVALUATION_DEFAULT);

			JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);

			if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
			{
				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);

				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);
				
				if (isSummaryWithPageHeaderAndFooter)
				{
					fillPageHeader(JRExpression.EVALUATION_DEFAULT);
				}

				printBand = 
					summary.refill(
						JRExpression.EVALUATION_DEFAULT,
						pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0)
						);

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;

				/*   */
				fillSummaryOverflow();
				
				//DONE
			}
			else
			{
				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;

				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				if (summary.willOverflow())
				{
					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);
					
					if (isSummaryWithPageHeaderAndFooter)
					{
						fillPageHeader(JRExpression.EVALUATION_DEFAULT);
					}

					printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));

					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;

					/*   */
					fillSummaryOverflow();
					
					//DONE
				}
				else
				{
					resolveBandBoundElements(summary, JRExpression.EVALUATION_DEFAULT);

					//DONE
				}
			}
		}
		else
		{
			// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
			
			fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

			fillPageFooter(JRExpression.EVALUATION_DEFAULT);
			
			//DONE
		}
	}


	/**
	 *
	 */
	private void fillSummaryNoLastFooterNewPage() throws JRException
	{
		// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
		
		fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

		fillPageFooter(JRExpression.EVALUATION_DEFAULT);

		summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

		if (summary.isToPrint())
		{
			resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
			resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
			resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
			scriptlet.callBeforePageInit();
			calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
			scriptlet.callAfterPageInit();

			addPage(false);

			if (isSummaryWithPageHeaderAndFooter)
			{
				fillPageHeader(JRExpression.EVALUATION_DEFAULT);
			}

			summary.evaluate(JRExpression.EVALUATION_DEFAULT);

			JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));

			if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
			{
				if (isSummaryWithPageHeaderAndFooter)
				{
					fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				}

				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				if (isSummaryWithPageHeaderAndFooter)
				{
					fillPageHeader(JRExpression.EVALUATION_DEFAULT);
				}

				printBand = 
					summary.refill(
						JRExpression.EVALUATION_DEFAULT,
						pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0)
						);
			}

			fillBand(printBand);
			offsetY += printBand.getHeight();
			isCrtRecordOnPage = true;
			isCrtRecordOnColumn = true;

			/*   */
			fillSummaryOverflow();
		}
		
		//DONE
	}


	/**
	 *
	 */
	private void fillSummaryWithLastFooterAndPageBands() throws JRException
	{
		if (
			!isSummaryNewPage
			//&& columnIndex == 0
			&& summary.getBreakHeight() <= columnFooterOffsetY - offsetY
			)
		{
			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				// deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
				if (groupFooterPositionElementRange != null)
				{
					ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
					offsetY = columnFooterOffsetY;
					// reset the element range here although it will not be checked anymore as the report ends
					groupFooterPositionElementRange = null;
				}
				
				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

					fillPageFooter(JRExpression.EVALUATION_DEFAULT);

					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);
					
					fillPageHeader(JRExpression.EVALUATION_DEFAULT);
					
					printBand = 
						summary.refill(
							JRExpression.EVALUATION_DEFAULT,
							pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
							);

					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;
				}
				else
				{
					//SummaryReport.17 test

					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;

					if (
						!summary.willOverflow()
						&& offsetY <= lastPageColumnFooterOffsetY
						)
					{
						setLastPageFooter(true);
					}
					
					fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
				}
				
				/*   */
				fillSummaryOverflow();

				//DONE
			}
			else
			{
				// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
				
				setLastPageFooter(true);

				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				//DONE
			}
		}
		else if (
				//columnIndex == 0 && 
				offsetY <= lastPageColumnFooterOffsetY)
		{
			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
				
				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);

				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);
				
				fillPageHeader(JRExpression.EVALUATION_DEFAULT);

				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - pageFooter.getHeight());

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					fillPageFooter(JRExpression.EVALUATION_DEFAULT);

					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);
					
					fillPageHeader(JRExpression.EVALUATION_DEFAULT);

					printBand = 
						summary.refill(
							JRExpression.EVALUATION_DEFAULT,
							pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
							);
				}

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;

				/*   */
				fillSummaryOverflow();
				
				//DONE
			}
			else
			{
				// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
				
				setLastPageFooter(true);

				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				//DONE
			}
		}
		else
		{
			// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
			
			fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

			fillPageFooter(JRExpression.EVALUATION_DEFAULT);

			resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
			resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
			resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
			scriptlet.callBeforePageInit();
			calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
			scriptlet.callAfterPageInit();

			addPage(false);

			fillPageHeader(JRExpression.EVALUATION_DEFAULT);

			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				//SummaryReport.18 test

				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - pageFooter.getHeight());

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					fillPageFooter(JRExpression.EVALUATION_DEFAULT);

					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);
					
					fillPageHeader(JRExpression.EVALUATION_DEFAULT);

					printBand = 
						summary.refill(
							JRExpression.EVALUATION_DEFAULT,
							pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
							);
				}

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;
			}
			else
			{
				//SummaryReport.19 test
			}

			/*   */
			fillSummaryOverflow();
			
			//DONE
		}
	}


	/**
	 *
	 */
	private void fillSummaryWithLastFooterNoPageBands() throws JRException
	{
		if (
			!isSummaryNewPage
			//&& columnIndex == 0
			&& summary.getBreakHeight() <= lastPageColumnFooterOffsetY - offsetY
			)
		{
			setLastPageFooter(true);

			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				// deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
				if (groupFooterPositionElementRange != null)
				{
					ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
					offsetY = columnFooterOffsetY;
					// reset the element range here although it will not be checked anymore as the report ends
					groupFooterPositionElementRange = null;
				}
				
				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

					fillPageFooter(JRExpression.EVALUATION_DEFAULT);

					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);

					printBand = 
						summary.refill(
							JRExpression.EVALUATION_DEFAULT,
							pageHeight - bottomMargin - offsetY
							);

					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;
				}
				else
				{
					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;

					fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

					fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				}

				/*   */
				fillSummaryOverflow();
				
				//DONE
			}
			else
			{
				// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;

				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				//DONE
			}
		}
		else if (
			!isSummaryNewPage
			//&& columnIndex == 0
			&& summary.getBreakHeight() <= columnFooterOffsetY - offsetY
			)
		{
			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				// deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
				if (groupFooterPositionElementRange != null)
				{
					ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
					offsetY = columnFooterOffsetY;
					// reset the element range here although it will not be checked anymore as the report ends
					groupFooterPositionElementRange = null;
				}
				
				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					if (offsetY <= lastPageColumnFooterOffsetY)
					{
						setLastPageFooter(true);

						fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

						fillPageFooter(JRExpression.EVALUATION_DEFAULT);

						resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
						resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
						resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
						scriptlet.callBeforePageInit();
						calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
						scriptlet.callAfterPageInit();

						addPage(false);

						printBand = 
							summary.refill(
								JRExpression.EVALUATION_DEFAULT,
								pageHeight - bottomMargin - offsetY
								);

						fillBand(printBand);
						offsetY += printBand.getHeight();
						isCrtRecordOnPage = true;
						isCrtRecordOnColumn = true;
					}
					else
					{
						fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);

						setLastPageFooter(true);

						printBand = 
							summary.refill(
								JRExpression.EVALUATION_DEFAULT,
								lastPageColumnFooterOffsetY - offsetY
								);

						fillBand(printBand);
						offsetY += printBand.getHeight();
						isCrtRecordOnPage = true;
						isCrtRecordOnColumn = true;

						fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

						fillPageFooter(JRExpression.EVALUATION_DEFAULT);
					}
				}
				else
				{
					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;

					fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);

					setLastPageFooter(true);

					if (summary.willOverflow())
					{
						printBand = summary.fill(lastPageColumnFooterOffsetY - offsetY);

						fillBand(printBand);
						offsetY += printBand.getHeight();
						isCrtRecordOnPage = true;
						isCrtRecordOnColumn = true;
					}

					fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

					fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				}

				/*   */
				fillSummaryOverflow();
				
				//DONE
			}
			else
			{
				// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;
				// it will be either the one in fillPageBreak or the following
				
				if (offsetY > lastPageColumnFooterOffsetY)
				{
					fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);
				}

				setLastPageFooter(true);

				fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				//DONE
			}
		}
		else if (
				//columnIndex == 0 && 
				offsetY <= lastPageColumnFooterOffsetY)
		{
			// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do

			setLastPageFooter(true);

			fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

			fillPageFooter(JRExpression.EVALUATION_DEFAULT);

			summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

			if (summary.isToPrint())
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				summary.evaluate(JRExpression.EVALUATION_DEFAULT);

				JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY);

				if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
				{
					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);

					printBand = 
						summary.refill(
							JRExpression.EVALUATION_DEFAULT,
							pageHeight - bottomMargin - offsetY
							);
				}

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;

				/*   */
				fillSummaryOverflow();
			}
			
			//DONE
		}
		else
		{
			// do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;

			fillColumnFooters(JRExpression.EVALUATION_DEFAULT);

			fillPageFooter(JRExpression.EVALUATION_DEFAULT);

			resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
			resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
			resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
			scriptlet.callBeforePageInit();
			calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
			scriptlet.callAfterPageInit();

			addPage(false);

			fillPageHeader(JRExpression.EVALUATION_DEFAULT);

			//fillColumnHeader(JRExpression.EVALUATION_DEFAULT);

			setLastPageFooter(true);

			if (isSummaryNewPage)
			{
				fillPageFooter(JRExpression.EVALUATION_DEFAULT);

				summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

				if (summary.isToPrint())
				{
					resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
					resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
					resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
					scriptlet.callBeforePageInit();
					calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
					scriptlet.callAfterPageInit();

					addPage(false);

					summary.evaluate(JRExpression.EVALUATION_DEFAULT);

					JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY);

					if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
					{
						resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
						resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
						resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
						scriptlet.callBeforePageInit();
						calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
						scriptlet.callAfterPageInit();

						addPage(false);

						printBand = 
							summary.refill(
								JRExpression.EVALUATION_DEFAULT,
								pageHeight - bottomMargin - offsetY
								);
					}

					fillBand(printBand);
					offsetY += printBand.getHeight();
					isCrtRecordOnPage = true;
					isCrtRecordOnColumn = true;

					/*   */
					fillSummaryOverflow();
				}
				
				//DONE
			}
			else
			{
				summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

				if (summary.isToPrint())
				{
					summary.evaluate(JRExpression.EVALUATION_DEFAULT);

					JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);

					if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
					{
						fillPageFooter(JRExpression.EVALUATION_DEFAULT);

						resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
						resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
						resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
						scriptlet.callBeforePageInit();
						calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
						scriptlet.callAfterPageInit();

						addPage(false);

						printBand = 
							summary.refill(
								JRExpression.EVALUATION_DEFAULT,
								pageHeight - bottomMargin - offsetY
								);

						fillBand(printBand);
						offsetY += printBand.getHeight();
						isCrtRecordOnPage = true;
						isCrtRecordOnColumn = true;
					}
					else
					{
						fillBand(printBand);
						offsetY += printBand.getHeight();
						isCrtRecordOnPage = true;
						isCrtRecordOnColumn = true;

						fillPageFooter(JRExpression.EVALUATION_DEFAULT);
					}

					/*   */
					fillSummaryOverflow();
				}
				else
				{
					fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				}
				
				//DONE
			}
		}
	}


	/**
	 *
	 */
	private void fillSummaryOverflow() throws JRException
	{
		while (summary.willOverflow())
		{
			if (isSummaryWithPageHeaderAndFooter)
			{
				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
			}
			
			resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
			resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
			resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
			scriptlet.callBeforePageInit();
			calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
			scriptlet.callAfterPageInit();

			addPage(false);

			if (isSummaryWithPageHeaderAndFooter)
			{
				fillPageHeader(JRExpression.EVALUATION_DEFAULT);
			}
			
			JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));

			fillBand(printBand);
			offsetY += printBand.getHeight();
			isCrtRecordOnPage = true;
			isCrtRecordOnColumn = true;
		}

		resolveBandBoundElements(summary, JRExpression.EVALUATION_DEFAULT);

		if (isSummaryWithPageHeaderAndFooter)
		{
			if (offsetY > pageHeight - bottomMargin - lastPageFooter.getHeight())
			{
				fillPageFooter(JRExpression.EVALUATION_DEFAULT);
				
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				fillPageHeader(JRExpression.EVALUATION_DEFAULT);
			}
			
			if (lastPageFooter != missingFillBand)
			{
				setLastPageFooter(true);
			}
			
			fillPageFooter(JRExpression.EVALUATION_DEFAULT);
		}
	}


	/**
	 *
	 */
	private void fillBackground() throws JRException
	{
		if (log.isDebugEnabled() && !background.isEmpty())
		{
			log.debug("Fill " + fillerId + ": background at " + offsetY);
		}
		
		//offsetX = leftMargin;
		
		//if (!isSubreport)
		//{
		//  offsetY = pageHeight - pageFooter.getHeight() - bottomMargin;
		//}
		
		if (background.getHeight() <= pageHeight - bottomMargin - offsetY)
		{
			background.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
			
			if (background.isToPrint())
			{
				background.evaluate(JRExpression.EVALUATION_DEFAULT);
				
				JRPrintBand printBand = background.fill(pageHeight - bottomMargin - offsetY);
				
				fillBand(printBand);
				//offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;
			}
		}
	}


	/**
	 *
	 */
	private void addPage(boolean isResetPageNumber) throws JRException
	{
		if (isSubreport())
		{
			addPageToParent(false);
		}
		
		if (printPage != null)
		{
			recordUsedPageHeight(offsetY + bottomMargin);
		}

		printPage = newPage();

		JRFillVariable pageNumberVar = calculator.getPageNumber();
		if (isResetPageNumber)
		{
			pageNumberVar.setValue(Integer.valueOf(1));
		}
		else
		{
			pageNumberVar.setValue(
				Integer.valueOf(((Number)pageNumberVar.getValue()).intValue() + 1)
				);
		}
		pageNumberVar.setOldValue(pageNumberVar.getValue());

		addPage(printPage);
		setFirstColumn();
		offsetY = topMargin;
		isFirstPageBand = true;
		isFirstColumnBand = true;

		lastDetailOffsetX = -1;
		lastDetailOffsetY = -1;
		maxDetailOffsetY = 0;

		fillBackground();
	}

	/**
	 * Sets the column number value computed based on {@link #columnIndex columnIndex}
	 */
	private void setColumnNumberVariable()
	{
		JRFillVariable columnNumberVar = calculator.getColumnNumber();
		columnNumberVar.setValue(Integer.valueOf(columnIndex + 1));
		columnNumberVar.setOldValue(columnNumberVar.getValue());
	}

	/**
	 *
	 */
	private void fillPageBreak(
		boolean isResetPageNumber,
		byte evalPrevPage,
		byte evalNextPage,
		boolean isReprintGroupHeaders
		) throws JRException
	{
		if (isCreatingNewPage)
		{
			throw 
				new JRException(
					EXCEPTION_MESSAGE_KEY_INFINITE_LOOP_CREATING_NEW_PAGE,  
					(Object[])null 
					);
		}

		if (groups != null)
		{
			for (JRFillGroup group : groups)
			{
				if (group.getKeepTogetherElementRange() != null)
				{
					group.getKeepTogetherElementRange().expand(offsetY);
				}
			}
		}
		
		FooterPositionEnum groupFooterPositionForOverflow = null;
		if (groupFooterPositionElementRange != null)
		{
			groupFooterPositionForOverflow = groupFooterPositionElementRange.getCurrentFooterPosition();
			// we are during group footers filling, otherwise this element range would have been null;
			// adding the content of the group footer band that is currently breaking
			groupFooterPositionElementRange.getElementRange().expand(offsetY);
		}

		if (orphanGroupFooterElementRange != null)
		{
			// we are during a group footer filling and footers already started to print,
			// so the current expansion applies to the group footer element range, not the detail element range
			orphanGroupFooterElementRange.expand(offsetY);
		}
		else if (orphanGroupFooterDetailElementRange != null)
		{
			// we are during a group footer filling, but footers did not yet start to print,
			// so the current expansion applies to the detail element range
			orphanGroupFooterDetailElementRange.expand(offsetY);
		}
		
		isCreatingNewPage = true;

		fillColumnFooters(evalPrevPage);

		fillPageFooter(evalPrevPage);

		resolveGroupBoundElements(evalPrevPage, false);
		resolveColumnBoundElements(evalPrevPage);
		resolvePageBoundElements(evalPrevPage);
		scriptlet.callBeforePageInit();
		calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
		scriptlet.callAfterPageInit();

		JRFillGroup keepTogetherGroup = getKeepTogetherGroup();

		ElementRange elementRangeToMove = null;
		ElementRange elementRangeToMove2 = null; // we don't have more than two possible element ranges to move; at least for now
		if (keepTogetherGroup != null)
		{
			elementRangeToMove = keepTogetherGroup.getKeepTogetherElementRange();
		}
		else if (orphanGroupFooterDetailElementRange != null)
		{
			elementRangeToMove = orphanGroupFooterDetailElementRange;
			elementRangeToMove2 = orphanGroupFooterElementRange;
		}

		if (
			floatColumnFooterElementRange != null 
			&& elementRangeToMove != null
			&& (elementRangeToMove.getColumnIndex() == 0 || elementRangeToMove2 != null) 
				// either the moved detail is from first column, or there were some group footers moved too,
				// otherwise the float column footer does not need to be moved
			)
		{
			ElementRangeUtil.moveContent(
				floatColumnFooterElementRange, 
				elementRangeToMove.getColumnIndex() == 0 ? elementRangeToMove.getTopY() : elementRangeToMove2.getTopY()
				);
		}

		// remove second range first, otherwise element indexes would become out of range
		ElementRangeContents elementsToMove2 = null;
		if (elementRangeToMove2 != null)
		{
			elementsToMove2 = ElementRangeUtil.removeContent(elementRangeToMove2, delayedActions);
		}
		ElementRangeContents elementsToMove = null;
		if (elementRangeToMove != null)
		{
			elementsToMove = ElementRangeUtil.removeContent(elementRangeToMove, delayedActions);
		}

		addPage(isResetPageNumber);

		fillPageHeader(evalNextPage);

		fillColumnHeaders(evalNextPage);

		if (isReprintGroupHeaders)
		{
			fillGroupHeadersReprint(evalNextPage);
			
			ElementRange keepTogetherElementRange = keepTogetherGroup == null ? null : keepTogetherGroup.getKeepTogetherElementRange();

			if (
				keepTogetherElementRange != null
				&& offsetY > keepTogetherElementRange.getBottomY()
				)
			{
				throw new JRException("Keep together moved content does not fit on the new page.");
			}
		}

		// reseting all movable element ranges
		orphanGroupFooterDetailElementRange = null;
		orphanGroupFooterElementRange = null;
		if (keepTogetherGroup != null)
		{
			keepTogetherGroup.setKeepTogetherElementRange(null);
		}

		if (elementRangeToMove != null)
		{
			ElementRangeUtil.addContent( 
				printPage, 
				currentPageIndex(),
				elementsToMove,
				//regardless whether there was page break or column  break, the X offset needs to account for columnIndex difference
				(columnIndex - elementRangeToMove.getColumnIndex()) * (columnSpacing + columnWidth),
				offsetY - elementRangeToMove.getTopY(),
				delayedActions
				);

			offsetY = offsetY + elementRangeToMove.getBottomY() - elementRangeToMove.getTopY();
			
			if (elementRangeToMove2 != null)
			{
				ElementRangeUtil.addContent( 
					printPage, 
					currentPageIndex(),
					elementsToMove2,
					//regardless whether there was page break or column  break, the X offset needs to account for columnIndex difference
					(columnIndex - elementRangeToMove2.getColumnIndex()) * (columnSpacing + columnWidth),
					offsetY - elementRangeToMove2.getTopY(),
					delayedActions
					);

				offsetY = offsetY + elementRangeToMove2.getBottomY() - elementRangeToMove2.getTopY();
			}
			
			isFirstPageBand = false;
			isFirstColumnBand = false;
		} 
		else if (
			groupFooterPositionForOverflow != null
			&& groupFooterPositionForOverflow != FooterPositionEnum.NORMAL
			)
		{
			// here we are during a group footer filling that broke over onto a new page;
			// recreating the group footer element range for the overflow content of the band
			groupFooterPositionElementRange = 
				new SimpleGroupFooterElementRange(
					new SimpleElementRange(getCurrentPage(), columnIndex, offsetY), 
					groupFooterPositionForOverflow
					);
		}

		isCreatingNewPage = false;
	}


	/**
	 *
	 */
	protected void fillColumnBand(JRFillBand band, byte evaluation) throws JRException
	{
		band.evaluate(evaluation);

		JRPrintBand printBand = band.fill(columnFooterOffsetY - offsetY);

		if (band.willOverflow())
		{
			boolean toRefill = band.isSplitPrevented() && !band.isSplitTypePreventInhibited();
			
			if (!toRefill)
			{
				if (groups != null)
				{
					// outer groups keep together is honored, while for the
					// inner keep together groups, the element range would be null 
					// in-between parent group breaks
					for (JRFillGroup group : groups)
					{
						if (
							group.getKeepTogetherElementRange() != null
							&& (group.isKeepTogether() || !group.hasMinDetails())
							)
						{
							toRefill = true;
							break;
						}
					}
				}
			}
			
			if (!toRefill)
			{
				if (orphanGroupFooterDetailElementRange != null)
				{
					toRefill = true;
				}
			}
			
			if (toRefill)
			{
				fillPageBreak(
					false, 
					evaluation == JRExpression.EVALUATION_DEFAULT 
						? (isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD) 
						: evaluation, 
					evaluation,
					true
					);

				printBand = band.refill(evaluation, columnFooterOffsetY - offsetY);
			}
		}

		fillBand(printBand);
		offsetY += printBand.getHeight();
		isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
		isCrtRecordOnColumn = isCrtRecordOnPage;
		
		while (band.willOverflow())
		{
			// this overflow here is special in the sense that it is the overflow of a detail band or group header or footer,
			// which are the only bands that are involved with movable element ranges such as keep together, footer position or orphan footer;
			// it is also special in the sense that it is an overflow after the band actually generated some content on the current page/column
			// and is not an early overflow like the one occurring when the band does not fit with its declared height or is non-splitting band;
			// having said that, it is OK to be more specific about the type of overflow here and only deal with non-white-space overflows of the band,
			// as they are the only ones which actually need to introduce a page/column break and continue rendering their remaining elements;
			// white space band overflows do not render anything on the next page/column and don't even preserve their remaining white space (historical behavior);
			// avoiding a page/column break here in case of white space overflows helps with preserving the detail element range, which would
			// thus be moved onto the new page/column as a non-breaking detail, if orphan footers follow; 
			// a page/column break here would cause the existing detail element range to be discarded (lost on subsequent element range expand),
			// and thus it would not be moved in case orphan footer follows, 
			// even if nothing gets rendered by this detail on the next page/column 
			if (band.willOverflowWithElements())
			{
				fillPageBreak(
					false, 
					evaluation == JRExpression.EVALUATION_DEFAULT 
						? (isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD) 
						: evaluation, 
					evaluation,
					true
					);
			}

			// we continue filling band overflow normally, because even in case of white space band overflow, nothing gets actually rendered
			// and the offsetY remains unchanged;
			// but we need to do this because the isOverflow flag would eventually be set to false and thus the current band rendering would end,
			// bringing the band into a state ready for the next filling
			printBand = band.fill(columnFooterOffsetY - offsetY);

			fillBand(printBand);
			offsetY += printBand.getHeight();
			isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
			isCrtRecordOnColumn = isCrtRecordOnPage;
		}

		resolveBandBoundElements(band, evaluation);
	}


	/**
	 *
	 */
	protected void fillFixedBand(JRFillBand band, byte evaluation) throws JRException
	{
		fillFixedBand(band, evaluation, true);
	}


	protected void fillFixedBand(JRFillBand band, byte evaluation, boolean allowShrinking) throws JRException
	{
		band.evaluate(evaluation);

		JRPrintBand printBand = band.fill();

		fillBand(printBand);
		offsetY += allowShrinking ? printBand.getHeight() : band.getHeight();
		isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
		isCrtRecordOnColumn = isCrtRecordOnPage;

		resolveBandBoundElements(band, evaluation);
	}


	/**
	 *
	 */
	private void setNewPageColumnInBands()
	{
		title.setNewPageColumn(true);
		pageHeader.setNewPageColumn(true);
		columnHeader.setNewPageColumn(true);
		detailSection.setNewPageColumn(true);
		columnFooter.setNewPageColumn(true);
		pageFooter.setNewPageColumn(true);
		lastPageFooter.setNewPageColumn(true);
		summary.setNewPageColumn(true);
		noData.setNewPageColumn(true);

		if (groups != null && groups.length > 0)
		{
			for(int i = 0; i < groups.length; i++)
			{
				((JRFillSection)groups[i].getGroupHeaderSection()).setNewPageColumn(true);
				((JRFillSection)groups[i].getGroupFooterSection()).setNewPageColumn(true);
			}
		}
	}


	/**
	 *
	 */
	private void setNewGroupInBands(JRGroup group)
	{
		title.setNewGroup(group, true);
		pageHeader.setNewGroup(group, true);
		columnHeader.setNewGroup(group, true);
		detailSection.setNewGroup(group, true);
		columnFooter.setNewGroup(group, true);
		pageFooter.setNewGroup(group, true);
		lastPageFooter.setNewGroup(group, true);
		summary.setNewGroup(group, true);

		if (groups != null && groups.length > 0)
		{
			for(int i = 0; i < groups.length; i++)
			{
				((JRFillSection)groups[i].getGroupHeaderSection()).setNewGroup(group, true);
				((JRFillSection)groups[i].getGroupFooterSection()).setNewGroup(group, true);
			}
		}
	}


	/**
	 *
	 */
	private JRFillBand getCurrentPageFooter()
	{
		return isLastPageFooter ? lastPageFooter : pageFooter;
	}


	/**
	 *
	 */
	private void setLastPageFooter(boolean isLastPageFooter)
	{
		this.isLastPageFooter = isLastPageFooter;

		if (isLastPageFooter)
		{
			columnFooterOffsetY = lastPageColumnFooterOffsetY;
		}
	}

	/**
	 *
	 */
	private void fillNoData() throws JRException
	{
		if (log.isDebugEnabled() && !noData.isEmpty())
		{
			log.debug("Fill " + fillerId + ": noData at " + offsetY);
		}

		noData.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);

		if (noData.isToPrint())
		{
			while (noData.getBreakHeight() > pageHeight - bottomMargin - offsetY)
			{
				addPage(false);
			}

			noData.evaluate(JRExpression.EVALUATION_DEFAULT);

			JRPrintBand printBand = noData.fill(pageHeight - bottomMargin - offsetY);

			if (noData.willOverflow() && noData.isSplitPrevented() && !noData.isSplitTypePreventInhibited())
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				printBand = 
					noData.refill(
						JRExpression.EVALUATION_DEFAULT,
						pageHeight - bottomMargin - offsetY
						);
			}

			fillBand(printBand);
			offsetY += printBand.getHeight();
			isCrtRecordOnPage = true;
			isCrtRecordOnColumn = true;

			while (noData.willOverflow())
			{
				resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
				resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
				resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
				scriptlet.callBeforePageInit();
				calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
				scriptlet.callAfterPageInit();

				addPage(false);

				printBand = noData.fill(pageHeight - bottomMargin - offsetY);

				fillBand(printBand);
				offsetY += printBand.getHeight();
				isCrtRecordOnPage = true;
				isCrtRecordOnColumn = true;
			}
			resolveBandBoundElements(noData, JRExpression.EVALUATION_DEFAULT);
		}
	}

	
	/**
	 *
	 */
	private void setOffsetX()
	{
		if (columnDirection == RunDirectionEnum.RTL)
		{
			offsetX = pageWidth - rightMargin - columnWidth - columnIndex * (columnSpacing + columnWidth);
		}
		else
		{
			offsetX = leftMargin + columnIndex * (columnSpacing + columnWidth);
		}
	}

	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy