/**
* ===========================================
* LibLayout : a free Java layouting library
* ===========================================
*
* Project Info: http://reporting.pentaho.org/liblayout/
*
* (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------
* $Id: SimplePaginationStep.java 6653 2008-12-02 14:53:40Z tmorgner $
* ------------
* (C) Copyright 2006-2007, by Pentaho Corporation.
*/
package org.jfree.layouting.renderer.process;
import org.jfree.layouting.input.style.keys.box.DisplayRole;
import org.jfree.layouting.input.style.values.CSSConstant;
import org.jfree.layouting.renderer.model.PageAreaRenderBox;
import org.jfree.layouting.renderer.model.ParagraphPoolBox;
import org.jfree.layouting.renderer.model.ParagraphRenderBox;
import org.jfree.layouting.renderer.model.RenderBox;
import org.jfree.layouting.renderer.model.RenderNode;
import org.jfree.layouting.renderer.model.page.LogicalPageBox;
import org.jfree.layouting.renderer.model.table.TableCellRenderBox;
import org.jfree.layouting.renderer.model.table.TableColumnGroupNode;
import org.jfree.layouting.renderer.model.table.TableRenderBox;
import org.jfree.layouting.renderer.model.table.TableSectionRenderBox;
/**
* Computes the pagination for streaming outputs. The only real purpose of this
* class is to rearrange the tables so that the header-body-footer order is
* maintained.
*
* This step ignores the pageformat and does not check for pagebreaks or page
* overflows.
* <p/>
* Todo: This class needs some serious attention later.
*
* @author Thomas Morgner
*/
public class SimplePaginationStep extends IterateVisualProcessStep
{
/**
* The current shift-distance. This can increase, but never decrease.
*/
private long shift;
private long stickyMarker;
private BoxShifter boxShifter;
private long footerHeight;
private long headerHeight;
public SimplePaginationStep()
{
boxShifter = new BoxShifter();
}
public void performPagebreak(final LogicalPageBox pageBox)
{
stickyMarker = pageBox.getChangeTracker();
shift = 0;
startRootProcessing(pageBox);
}
private void startRootProcessing(final LogicalPageBox pageBox)
{
// Note: For now, we limit both the header and footer to a single physical
// page. This safes me a lot of trouble for now.
// we have to iterate using a more complex schema here.
// Step one: layout the header-section. Record that height.
final PageAreaRenderBox headerArea = pageBox.getHeaderArea();
headerHeight = headerArea.getHeight();
// Step two: The footer. For the footer, we have to traverse the whole
// thing backwards. Nonetheless, we've got the height.
final PageAreaRenderBox footerArea = pageBox.getFooterArea();
footerHeight = footerArea.getHeight();
startProcessing(pageBox);
}
protected boolean startInlineLevelBox(final RenderBox box)
{
// Skip the contents ..
return false;
}
protected void processParagraphChilds(final ParagraphRenderBox box)
{
// Process the direct childs of the paragraph
// Each direct child represents a line ..
// Todo: Include orphan and widow stuff ..
// First: Check the number of lines. (Should have been precomputed)
// Second: Check whether and where the orphans- and widows-rules apply
// Third: Shift the lines.
RenderNode node = box.getVisibleFirst();
while (node != null)
{
// all childs of the linebox container must be inline boxes. They
// represent the lines in the paragraph. Any other element here is
// a error that must be reported
if (node instanceof ParagraphPoolBox == false)
{
throw new IllegalStateException("Encountered " + node.getClass());
}
final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
if (startLine(inlineRenderBox))
{
processBoxChilds(inlineRenderBox);
}
finishLine(inlineRenderBox);
node = node.getVisibleNext();
}
}
private boolean isNodeProcessable(final RenderNode node)
{
if (node.getStickyMarker() == stickyMarker)
{
// already finished. No need to touch that thing ..
return false;
}
if (node.isIgnorableForRendering())
{
return false;
}
return true;
}
protected void processBlockLevelNode(final RenderNode node)
{
if (isNodeProcessable(node) == false)
{
return;
}
// Apply the current shift.
node.setY(node.getY() + shift);
// oh, we have to move the node downwards.
node.setY(node.getY() + shift);
// Make sure we do not move that node later on ..
node.setStickyMarker(stickyMarker);
}
protected void processTable(final TableRenderBox table)
{
final long originalShift = shift;
final long height = table.getHeight();
final long y = table.getY();
final long y2 = shift + y + height;
if (table.isIgnorableForRendering())
{
return;
}
// ordinary shifting is used ..
table.setY(table.getY() + shift);
table.setStickyMarker(stickyMarker);
processTableSection(table, DisplayRole.TABLE_HEADER_GROUP);
processTableSection(table, DisplayRole.TABLE_ROW_GROUP);
processTableSection(table, DisplayRole.TABLE_FOOTER_GROUP);
// Processing all he sections can have an effect on the height of the table
// Shifting content down increases the height of the table.
final long finalHeight = table.getHeight();
final long delta = finalHeight - height;
if (delta < 0)
{
throw new IllegalStateException("A table can/must not shrink!");
}
}
private void processTableSection(final TableRenderBox box,
final CSSConstant role)
{
RenderNode rowGroupNode = box.getFirstChild();
while (rowGroupNode != null)
{
if (rowGroupNode instanceof TableSectionRenderBox == false)
{
rowGroupNode = rowGroupNode.getNext();
continue;
}
final TableSectionRenderBox sectionBox =
(TableSectionRenderBox) rowGroupNode;
if (role.equals
(sectionBox.getDisplayRole()) == false)
{
// not a header ..
rowGroupNode = rowGroupNode.getNext();
continue;
}
startProcessing(rowGroupNode);
rowGroupNode = rowGroupNode.getNext();
}
}
protected void processBlockLevelChild(final RenderNode node)
{
if (node instanceof TableRenderBox)
{
final TableRenderBox table = (TableRenderBox) node;
processTable(table);
}
else
{
super.processBlockLevelChild(node);
}
}
protected boolean startBlockLevelBox(final RenderBox box)
{
if (isNodeProcessable(box) == false)
{
// Not shifted, as this box will not be affected. It is not part of the
// content window.
return false;
}
final long y = box.getY();
if (box instanceof TableCellRenderBox)
{
// table cells get a special treatment when the row is computed ..
box.setY(y + shift);
box.setStickyMarker(stickyMarker);
return true;
}
if (box instanceof TableColumnGroupNode)
{
throw new IllegalArgumentException("This is not expected here");
}
// At this point, we know that the box is way to large to fit the remaining
// space. But we know, that content will be processed on that page (by
// looking at the orphan-size) and that the break occurrs somewhere inside
// of the children of this box. So shall it be!
box.setY(box.getY() + shift);
box.setStickyMarker(stickyMarker);
return true;
}
protected boolean startLine(final ParagraphPoolBox box)
{
if (isNodeProcessable(box) == false)
{
return false;
}
boxShifter.shiftBox(box, shift);
return false;
}
protected void finishLine(final ParagraphPoolBox inlineRenderBox)
{
}
}