001: /**
002: * =========================================================
003: * Pentaho-Reporting-Classic : a free Java reporting library
004: * =========================================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * FlowPaginationStep.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process;
030:
031: import org.jfree.report.layout.ModelPrinter;
032: import org.jfree.report.layout.model.BreakMarkerRenderBox;
033: import org.jfree.report.layout.model.LogicalPageBox;
034: import org.jfree.report.layout.model.PageBreakPositionList;
035: import org.jfree.report.layout.model.ParagraphRenderBox;
036: import org.jfree.report.layout.model.RenderBox;
037: import org.jfree.report.layout.model.RenderNode;
038:
039: /**
040: * The flow-pagination is a pagination step, where the page-boundaries cannot be determined beforehand. It only works
041: * for infinite size pages and ignores all page-header and footers.
042: * <p/>
043: * The page-break list is updated on the fly while the report is paginated. A new break will only be added if the old
044: * list did not contain the new break. It is guaranteed that only one break is added on every run.
045: * <p/>
046: * If complex compound layouts are required, this pagination step must be followed by a classical pagination step so
047: * that boxes that overlap a break-position get shifted accordingly.
048: *
049: * @author Thomas Morgner
050: */
051: public final class FlowPaginationStep extends IterateVisualProcessStep {
052: private boolean breakPending;
053: private PageBreakPositionList breakUtility;
054: private boolean breakAdded;
055: private Object finalVisibleState;
056: private long pageEnd;
057:
058: public FlowPaginationStep() {
059: }
060:
061: public PaginationResult performPagebreak(
062: final LogicalPageBox pageBox) {
063: final RenderNode lastChild = pageBox.getLastChild();
064: if (lastChild != null) {
065: final long lastChildY2 = lastChild.getY()
066: + lastChild.getHeight();
067: if (lastChildY2 < pageBox.getHeight()) {
068: throw new IllegalStateException(
069: "Assertation failed: Block layouting did not proceed: "
070: + lastChildY2 + " < "
071: + pageBox.getHeight());
072: }
073: }
074:
075: this .breakPending = false;
076: this .breakAdded = false;
077: this .finalVisibleState = null;
078: this .pageEnd = pageBox.getHeight();
079:
080: try {
081: final PageBreakPositionList allPreviousBreak = pageBox
082: .getAllVerticalBreaks();
083:
084: // Note: For now, we limit both the header and footer to a single physical
085: // page. This safes me a lot of trouble for now.
086: breakUtility = new PageBreakPositionList(allPreviousBreak,
087: 1);
088:
089: // now process all the other content (excluding the header and footer area)
090: if (startBlockLevelBox(pageBox)) {
091: processBoxChilds(pageBox);
092: }
093: finishBlockLevelBox(pageBox);
094:
095: if (lastChild != null) {
096: final long lastChildY2 = lastChild.getY()
097: + lastChild.getHeight();
098: if (lastChildY2 < pageBox.getHeight()) {
099: throw new IllegalStateException(
100: "Assertation failed: Pagination violated block-constraints: "
101: + lastChildY2 + " < "
102: + pageBox.getHeight());
103: }
104: }
105:
106: final long masterBreak = breakUtility.getLastMasterBreak();
107: final boolean nextPageContainsContent = (pageBox
108: .getHeight() > masterBreak);
109: return new PaginationResult(breakUtility, breakAdded,
110: nextPageContainsContent, finalVisibleState);
111: } finally {
112: breakUtility = null;
113: }
114: }
115:
116: protected void processParagraphChilds(final ParagraphRenderBox box) {
117: processBoxChilds(box);
118: }
119:
120: protected boolean startBlockLevelBox(final RenderBox box) {
121: final int breakIndicator = box.getManualBreakIndicator();
122:
123: if (box instanceof BreakMarkerRenderBox) {
124: final long boxY = box.getY();
125: if (breakAdded == false &&
126: // boxY != pageEnd &&
127: boxY > breakUtility.getLastMasterBreak()) {
128: // This box will cause a new break. Add it.
129: breakUtility.addMajorBreak(box.getY(), 0);
130: }
131: breakAdded = (box.getY() == pageEnd);
132: return false;
133: }
134:
135: // First check the simple cases:
136: // If the box wants to break, then there's no point in waiting: Shift the box and continue.
137: if (breakIndicator == RenderBox.DIRECT_MANUAL_BREAK
138: || breakPending) {
139: // find the next major break and shift the box to this position.
140: // update the 'shift' to reflect this new change. Process the contents of this box as well, as the box may
141: // have additional breaks inside (or may overflow, or whatever ..).
142: final long boxY = box.getY();
143: if (breakAdded == false && boxY != pageEnd && // damn! todo
144: boxY > breakUtility.getLastMasterBreak()) {
145: if (boxY != pageEnd) {
146: // This box will cause a new break. Add it.
147: breakUtility.addMajorBreak(box.getY(), 0);
148: }
149: breakAdded = true;
150: }
151: breakPending = false;
152: return true;
153: }
154:
155: if (breakIndicator == RenderBox.NO_MANUAL_BREAK) {
156: // As neither this box nor any of the children will cause a pagebreak, skip the processing of the childs.
157: if (breakAdded == false) {
158: updateStateKeyDeep(box);
159: }
160: return false;
161: }
162:
163: // One of the children of this box will cause a manual pagebreak. We have to dive deeper into this child.
164: // for now, we will only apply the ordinary shift.
165: if (breakAdded == false) {
166: updateStateKey(box);
167: }
168: return true;
169: }
170:
171: private void updateStateKey(final RenderBox box) {
172: final long y = box.getY();
173: if (y < (pageEnd)) {
174: final Object stateKey = box.getStateKey();
175: if (stateKey != null) {
176: // Log.debug ("Updating state key: " + stateKey);
177: this .finalVisibleState = stateKey;
178: }
179: // else
180: // {
181: // Log.debug ("No key: " + y + " <= " + (pageOffset + pageHeight));
182: // }
183: }
184: // else
185: // {
186: // Log.debug ("Not in Range: " + y + " <= " + (pageOffset + pageHeight));
187: // }
188: }
189:
190: private boolean updateStateKeyDeep(final RenderBox box) {
191: final long y = box.getY();
192: if (y < (pageEnd)) {
193: final Object stateKey = box.getStateKey();
194: if (stateKey != null) {
195: // Log.debug ("Deep: Updating state key: " + stateKey);
196: this .finalVisibleState = stateKey;
197: return true;
198: } else {
199: RenderNode lastChild = box.getLastChild();
200: while (lastChild != null) {
201: if (lastChild instanceof RenderBox == false) {
202: lastChild = lastChild.getPrev();
203: continue;
204: }
205: final RenderBox lastBox = (RenderBox) lastChild;
206: if (updateStateKeyDeep(lastBox)) {
207: return true;
208: }
209: lastChild = lastBox.getPrev();
210: }
211: return false;
212: }
213: } else {
214: // Log.debug ("Deep: Not in Range: " + y + " <= " + (pageOffset + pageHeight));
215: return false;
216: }
217: }
218:
219: protected void finishBlockLevelBox(final RenderBox box) {
220: if (breakPending == false && box.isBreakAfter()) {
221: breakPending = true;
222: }
223: }
224:
225: protected boolean startInlineLevelBox(final RenderBox box) {
226: return false;
227: }
228:
229: // At a later point, we have to do some real page-breaking here. We should check, whether the box fits, and should
230: // shift the box if it doesnt.
231: protected boolean startCanvasLevelBox(final RenderBox box) {
232: return false;
233: }
234: }
|