KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layout > BodyAreaContainer


1 /*
2  * $Id: BodyAreaContainer.java,v 1.10.2.4 2003/03/06 23:55:31 pietsch Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.layout;
52
53 // FOP
54
import org.apache.fop.render.Renderer;
55 import org.apache.fop.datatypes.IDReferences;
56 import org.apache.fop.apps.FOPException;
57 import org.apache.fop.fo.FObj;
58 import org.apache.fop.fo.flow.Block;
59 import org.apache.fop.fo.flow.BlockContainer;
60 import org.apache.fop.fo.properties.Span;
61
62 import org.apache.fop.fo.properties.Position;
63
64 // Java
65
import java.util.ArrayList JavaDoc;
66
67 public class BodyAreaContainer extends Area {
68
69     // dimensions for the 'region-reference-area'
70
private int xPosition; // should be able to take value 'left' and 'right' too
71
private int yPosition; // should be able to take value 'top' and 'bottom' too
72
private int position;
73
74     // the column-count and column-gap
75
private int columnCount;
76     private int columnGap;
77
78     // the 3 primary reference areas
79
private AreaContainer mainReferenceArea;
80     private AreaContainer beforeFloatReferenceArea;
81     private AreaContainer footnoteReferenceArea;
82
83     // current heights
84
private int mainRefAreaHeight;
85
86     // reference area yPositions
87
private int mainYPosition;
88     private int beforeFloatYPosition;
89     private int footnoteYPosition;
90
91     // the start FO in case of rollback
92
private FObj startFO;
93     private boolean isNewSpanArea;
94
95     // keeps track of footnote state for multiple layouts
96
private int footnoteState = 0;
97
98     public BodyAreaContainer(FontState fontState, int xPosition,
99                              int yPosition, int allocationWidth,
100                              int maxHeight, int position, int columnCount,
101                              int columnGap) {
102         super(fontState, allocationWidth, maxHeight);
103         this.xPosition = xPosition;
104         this.yPosition = yPosition;
105         this.position = position;
106         this.columnCount = columnCount;
107         this.columnGap = columnGap;
108
109         // create the primary reference areas
110
mainRefAreaHeight = maxHeight;
111         beforeFloatReferenceArea = new AreaContainer(fontState, xPosition,
112                                                      yPosition,
113                                                      allocationWidth, 0,
114                                                      Position.ABSOLUTE);
115         beforeFloatReferenceArea.setAreaName("before-float-reference-area");
116         this.addChild(beforeFloatReferenceArea);
117         mainReferenceArea = new AreaContainer(fontState, xPosition,
118                                               yPosition, allocationWidth,
119                                               mainRefAreaHeight,
120                                               Position.ABSOLUTE);
121         mainReferenceArea.setAreaName("main-reference-area");
122         this.addChild(mainReferenceArea);
123         footnoteReferenceArea = new AreaContainer(fontState, xPosition,
124                                                   yPosition - mainRefAreaHeight,
125                                                   allocationWidth, 0,
126                                                   Position.ABSOLUTE);
127         footnoteReferenceArea.setAreaName("footnote-reference-area");
128         this.addChild(footnoteReferenceArea);
129
130         // all padding and border-width must be 0
131
// setBorderAndPadding(new BorderAndPadding());
132
// setPadding(0, 0, 0, 0);
133
// setBorderWidth(0, 0, 0, 0);
134
}
135
136     public void render(Renderer renderer) {
137         renderer.renderBodyAreaContainer(this);
138     }
139
140     public int getPosition() {
141         return position;
142     }
143
144     public int getXPosition() {
145         return xPosition + getPaddingLeft() + getBorderLeftWidth();
146     }
147
148     public void setXPosition(int value) {
149         xPosition = value;
150     }
151
152     public int getYPosition() {
153         return yPosition + getPaddingTop() + getBorderTopWidth();
154     }
155
156     public void setYPosition(int value) {
157         yPosition = value;
158     }
159
160     public AreaContainer getMainReferenceArea() {
161         return mainReferenceArea;
162     }
163
164     public AreaContainer getBeforeFloatReferenceArea() {
165         return beforeFloatReferenceArea;
166     }
167
168     public AreaContainer getFootnoteReferenceArea() {
169         return footnoteReferenceArea;
170     }
171
172     public void setIDReferences(IDReferences idReferences) {
173         mainReferenceArea.setIDReferences(idReferences);
174     }
175
176     public IDReferences getIDReferences() {
177         return mainReferenceArea.getIDReferences();
178     }
179
180     /**
181      * Depending on the column-count of the next FO, determine whether
182      * a new span area needs to be constructed or not, and return the
183      * appropriate ColumnArea.
184      * The next cut of this method should also inspect the FO to see
185      * whether the area to be returned ought not to be the footnote
186      * or before-float reference area.
187      * @param fo The next formatting object
188      * @return the next column area (possibly the current one)
189      */

190     public AreaContainer getNextArea(FObj fo) throws FOPException {
191         isNewSpanArea = false;
192
193         int span = Span.NONE;
194         if (fo instanceof Block)
195             span = ((Block)fo).getSpan();
196         else if (fo instanceof BlockContainer)
197             span = ((BlockContainer)fo).getSpan();
198
199         if (this.mainReferenceArea.getChildren().isEmpty()) {
200             if (span == Span.ALL)
201                 return addSpanArea(1);
202             else
203                 return addSpanArea(columnCount);
204         }
205
206         ArrayList JavaDoc spanAreas = this.mainReferenceArea.getChildren();
207         SpanArea spanArea = (SpanArea)spanAreas.get(spanAreas.size()
208                 - 1);
209
210         if ((span == Span.ALL) && (spanArea.getColumnCount() == 1)) {
211             // return the single column area in the same span area
212
return spanArea.getCurrentColumnArea();
213         } else if ((span == Span.NONE)
214                    && (spanArea.getColumnCount() == columnCount)) {
215             // return the current column area in the same span area
216
return spanArea.getCurrentColumnArea();
217         } else if (span == Span.ALL) {
218             // create new span area with one column; return column area
219
return addSpanArea(1);
220         } else if (span == Span.NONE) {
221             // create new span area with multiple columns; return first column area
222
return addSpanArea(columnCount);
223         } else {
224             throw new FOPException("BodyAreaContainer::getNextArea(): Span attribute messed up");
225         }
226     }
227
228     /**
229      * Add a new span area with specified number of column areas.
230      * @param numColumns The number of column areas
231      * @return AreaContainer The next column area
232      */

233     private AreaContainer addSpanArea(int numColumns) {
234         resetHeights();
235         // create span area and child column-areas, using whatever
236
// height remains after existing span areas (in the main
237
// reference area).
238
int spanAreaYPosition = getYPosition()
239                                 - this.mainReferenceArea.getContentHeight();
240
241         SpanArea spanArea = new SpanArea(fontState, getXPosition(),
242                                          spanAreaYPosition, allocationWidth,
243                                          getRemainingHeight(), numColumns,
244                                          columnGap);
245         this.mainReferenceArea.addChild(spanArea);
246         spanArea.setPage(this.getPage());
247         this.isNewSpanArea = true;
248         return spanArea.getCurrentColumnArea();
249     }
250
251     /**
252      * This almost does what getNewArea() does, without actually
253      * returning an area. These 2 methods can be reworked.
254      * @param fo The next formatting object
255      * @return boolean True if we need to balance.
256      */

257     public boolean isBalancingRequired(FObj fo) {
258         if (this.mainReferenceArea.getChildren().isEmpty())
259             return false;
260
261         ArrayList JavaDoc spanAreas = this.mainReferenceArea.getChildren();
262         SpanArea spanArea = (SpanArea)spanAreas.get(spanAreas.size()
263                 - 1);
264
265         if (spanArea.isBalanced())
266             return false;
267
268         int span = Span.NONE;
269         if (fo instanceof Block)
270             span = ((Block)fo).getSpan();
271         else if (fo instanceof BlockContainer)
272             span = ((BlockContainer)fo).getSpan();
273
274         if ((span == Span.ALL) && (spanArea.getColumnCount() == 1))
275             return false;
276         else if ((span == Span.NONE)
277                  && (spanArea.getColumnCount() == columnCount))
278             return false;
279         else if (span == Span.ALL)
280             return true;
281         else if (span == Span.NONE)
282             return false;
283         else
284             return false;
285     }
286
287     /**
288      * This is where the balancing algorithm lives, or gets called.
289      * Right now it's primitive: get the total content height in all
290      * columns, divide by the column count, and add a heuristic
291      * safety factor.
292      * Then the previous (unbalanced) span area is removed, and a new
293      * one added with the computed max height.
294      */

295     public void resetSpanArea() {
296         ArrayList JavaDoc spanAreas = this.mainReferenceArea.getChildren();
297         SpanArea spanArea = (SpanArea)spanAreas.get(spanAreas.size() - 1);
298
299         if (!spanArea.isBalanced()) {
300             // span area maintains a record of the total height of
301
// laid-out content in the previous (first) attempt
302
int newHeight = spanArea.getTotalContentHeight()
303                             / spanArea.getColumnCount();
304             newHeight += 2 * 15600; // ???
305

306             this.mainReferenceArea.removeChild(spanArea);
307             resetHeights();
308             SpanArea newSpanArea = new SpanArea(fontState, getXPosition(),
309                                                 spanArea.getYPosition(),
310                                                 allocationWidth, newHeight,
311                                                 spanArea.getColumnCount(),
312                                                 columnGap);
313             this.mainReferenceArea.addChild(newSpanArea);
314             newSpanArea.setPage(this.getPage());
315             newSpanArea.setIsBalanced();
316             this.isNewSpanArea = true;
317         } else {
318             throw new IllegalStateException JavaDoc("Trying to balance balanced area");
319         }
320     }
321
322     /**
323      * Determine remaining height for new span area. Needs to be
324      * modified for footnote and before-float reference areas when
325      * those are supported.
326      * @return int The remaining available height in millipoints.
327      */

328     public int getRemainingHeight() {
329         return this.mainReferenceArea.getMaxHeight()
330                - this.mainReferenceArea.getContentHeight();
331     }
332
333     /**
334      * Used by resetSpanArea() and addSpanArea() to adjust the main
335      * reference area height before creating a new span.
336      */

337     private void resetHeights() {
338         int totalHeight = 0;
339         ArrayList JavaDoc children = this.mainReferenceArea.getChildren();
340         for (int i = 0; i < children.size(); i++) {
341             SpanArea spanArea = (SpanArea)children.get(i);
342             int spanContentHeight = spanArea.getMaxContentHeight();
343             int spanMaxHeight = spanArea.getMaxHeight();
344
345             totalHeight += (spanContentHeight < spanMaxHeight)
346                            ? spanContentHeight : spanMaxHeight;
347         }
348         this.mainReferenceArea.setHeight(totalHeight);
349     }
350
351     /**
352      * Used in Flow when layout returns incomplete.
353      * @return boolean Is this the last column in this span?
354      */

355     public boolean isLastColumn() {
356         ArrayList JavaDoc spanAreas = this.mainReferenceArea.getChildren();
357         SpanArea spanArea = (SpanArea)spanAreas.get(spanAreas.size() - 1);
358         return spanArea.isLastColumn();
359     }
360
361     /**
362      * This variable is unset by getNextArea(), is set by addSpanArea(),
363      * and <i>may</i> be set by resetSpanArea().
364      * @return boolean Is the span area new or not?
365      */

366     public boolean isNewSpanArea() {
367         return isNewSpanArea;
368     }
369
370     public AreaContainer getCurrentColumnArea() {
371         ArrayList JavaDoc spanAreas = this.mainReferenceArea.getChildren();
372         SpanArea spanArea = (SpanArea)spanAreas.get(spanAreas.size() - 1);
373         return spanArea.getCurrentColumnArea();
374     }
375
376     public int getFootnoteState() {
377         return footnoteState;
378     }
379
380     public boolean needsFootnoteAdjusting() {
381         footnoteYPosition = footnoteReferenceArea.getYPosition();
382         switch (footnoteState) {
383         case 0:
384             resetHeights();
385             if (footnoteReferenceArea.getHeight() > 0
386                     && mainYPosition + mainReferenceArea.getHeight()
387                        > footnoteYPosition) {
388                 return true;
389             }
390         case 1:
391             break;
392         }
393         return false;
394     }
395
396     public void adjustFootnoteArea() {
397         footnoteState++;
398         if (footnoteState == 1) {
399             mainReferenceArea.setMaxHeight(footnoteReferenceArea.getYPosition()
400                                            - mainYPosition);
401             footnoteYPosition = footnoteReferenceArea.getYPosition();
402             footnoteReferenceArea.setMaxHeight(footnoteReferenceArea.getHeight());
403
404             ArrayList JavaDoc children = footnoteReferenceArea.getChildren();
405             for (int i = 0; i < children.size(); i++) {
406                 Object JavaDoc obj = children.get(i);
407                 if (obj instanceof Area) {
408                     Area childArea = (Area)obj;
409                     footnoteReferenceArea.removeChild(childArea);
410                 }
411             }
412
413             getPage().setPendingFootnotes(null);
414         }
415     }
416
417     protected static void resetMaxHeight(Area ar, int change) {
418         ar.setMaxHeight(change);
419         ArrayList JavaDoc childs = ar.getChildren();
420         for (int i = 0; i < childs.size(); i++) {
421             Object JavaDoc obj = childs.get(i);
422             if (obj instanceof Area) {
423                 Area childArea = (Area)obj;
424                 resetMaxHeight(childArea, change);
425             }
426         }
427     }
428
429 }
430
Popular Tags