You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BodyAreaContainer.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /* $Id$
  2. * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
  3. * For details on use and redistribution please refer to the
  4. * LICENSE file included with these sources.
  5. */
  6. package org.apache.fop.layout;
  7. // FOP
  8. import org.apache.fop.render.Renderer;
  9. import org.apache.fop.fo.properties.*;
  10. import org.apache.fop.datatypes.IDReferences;
  11. import org.apache.fop.apps.FOPException;
  12. import org.apache.fop.fo.FObj;
  13. import org.apache.fop.fo.flow.Block;
  14. import org.apache.fop.fo.flow.BlockContainer;
  15. // Java
  16. import java.util.Vector;
  17. import java.util.Enumeration;
  18. public class BodyAreaContainer extends Area {
  19. // dimensions for the 'region-reference-area'
  20. private int xPosition; // should be able to take value 'left' and 'right' too
  21. private int yPosition; // should be able to take value 'top' and 'bottom' too
  22. private int position;
  23. // the column-count and column-gap
  24. private int columnCount;
  25. private int columnGap;
  26. // the 3 primary reference areas
  27. private AreaContainer mainReferenceArea;
  28. private AreaContainer beforeFloatReferenceArea;
  29. private AreaContainer footnoteReferenceArea;
  30. // current heights
  31. private int mainRefAreaHeight;
  32. private int beforeFloatRefAreaHeight;
  33. private int footnoteRefAreaHeight;
  34. // reference area yPositions
  35. private int mainYPosition;
  36. private int beforeFloatYPosition;
  37. private int footnoteYPosition;
  38. // the start FO in case of rollback
  39. private FObj startFO;
  40. private boolean isNewSpanArea;
  41. // keeps track of footnote state for multiple layouts
  42. private int footnoteState = 0;
  43. public BodyAreaContainer(FontState fontState, int xPosition, int yPosition,
  44. int allocationWidth, int maxHeight, int position,
  45. int columnCount, int columnGap) {
  46. super(fontState, allocationWidth, maxHeight);
  47. this.xPosition = xPosition;
  48. this.yPosition = yPosition;
  49. this.position = position;
  50. this.columnCount = columnCount;
  51. this.columnGap = columnGap;
  52. // create the primary reference areas
  53. beforeFloatRefAreaHeight = 0;
  54. footnoteRefAreaHeight = 0;
  55. mainRefAreaHeight = maxHeight - beforeFloatRefAreaHeight - footnoteRefAreaHeight;
  56. beforeFloatReferenceArea = new AreaContainer(fontState, xPosition, yPosition,
  57. allocationWidth, beforeFloatRefAreaHeight, Position.ABSOLUTE);
  58. this.addChild(beforeFloatReferenceArea);
  59. mainReferenceArea = new AreaContainer(fontState, xPosition, yPosition,
  60. allocationWidth, mainRefAreaHeight, Position.ABSOLUTE);
  61. this.addChild(mainReferenceArea);
  62. int footnoteRefAreaYPosition = yPosition - mainRefAreaHeight;
  63. footnoteReferenceArea = new AreaContainer(fontState, xPosition, footnoteRefAreaYPosition,
  64. allocationWidth, footnoteRefAreaHeight, Position.ABSOLUTE);
  65. this.addChild(footnoteReferenceArea);
  66. // all padding and border-width must be 0
  67. //setBorderAndPadding(new BorderAndPadding());
  68. // setPadding(0, 0, 0, 0);
  69. // setBorderWidth(0, 0, 0, 0);
  70. }
  71. public void render(Renderer renderer) {
  72. renderer.renderBodyAreaContainer(this);
  73. }
  74. public int getPosition() {
  75. return position;
  76. }
  77. public int getXPosition() {
  78. return xPosition + getPaddingLeft() + getBorderLeftWidth();
  79. }
  80. public void setXPosition(int value)
  81. {
  82. xPosition=value;
  83. }
  84. public int getYPosition() {
  85. return yPosition + getPaddingTop() + getBorderTopWidth();
  86. }
  87. public void setYPosition(int value)
  88. {
  89. yPosition=value;
  90. }
  91. public AreaContainer getMainReferenceArea()
  92. {
  93. return mainReferenceArea;
  94. }
  95. public AreaContainer getBeforeFloatReferenceArea()
  96. {
  97. return beforeFloatReferenceArea;
  98. }
  99. public AreaContainer getFootnoteReferenceArea()
  100. {
  101. return footnoteReferenceArea;
  102. }
  103. public void setIDReferences(IDReferences idReferences) {
  104. mainReferenceArea.setIDReferences(idReferences);
  105. }
  106. public IDReferences getIDReferences() {
  107. return mainReferenceArea.getIDReferences();
  108. }
  109. /**
  110. * Depending on the column-count of the next FO, determine whether
  111. * a new span area needs to be constructed or not, and return the
  112. * appropriate ColumnArea.
  113. * The next cut of this method should also inspect the FO to see
  114. * whether the area to be returned ought not to be the footnote
  115. * or before-float reference area.
  116. * @param fo The next formatting object
  117. * @returns the next column area (possibly the current one)
  118. */
  119. public AreaContainer getNextArea(FObj fo)
  120. throws FOPException
  121. {
  122. isNewSpanArea = false;
  123. int span = Span.NONE;
  124. if (fo instanceof Block)
  125. span = ((Block)fo).getSpan();
  126. else if (fo instanceof BlockContainer)
  127. span = ((BlockContainer)fo).getSpan();
  128. if (this.mainReferenceArea.getChildren().isEmpty())
  129. {
  130. if (span == Span.ALL)
  131. return addSpanArea(1);
  132. else
  133. return addSpanArea(columnCount);
  134. }
  135. Vector spanAreas = this.mainReferenceArea.getChildren();
  136. SpanArea spanArea = (SpanArea)spanAreas.elementAt(spanAreas.size()-1);
  137. if ((span == Span.ALL) && (spanArea.getColumnCount() == 1))
  138. {
  139. // return the single column area in the same span area
  140. return spanArea.getCurrentColumnArea();
  141. }
  142. else if ((span == Span.NONE) && (spanArea.getColumnCount() == columnCount))
  143. {
  144. // return the current column area in the same span area
  145. return spanArea.getCurrentColumnArea();
  146. }
  147. else if (span == Span.ALL)
  148. {
  149. // create new span area with one column; return column area
  150. return addSpanArea(1);
  151. }
  152. else if (span == Span.NONE)
  153. {
  154. // create new span area with multiple columns; return first column area
  155. return addSpanArea(columnCount);
  156. }
  157. else
  158. {
  159. throw new FOPException("BodyAreaContainer::getNextArea(): Span attribute messed up");
  160. }
  161. }
  162. /**
  163. * Add a new span area with specified number of column areas.
  164. * @param numColumns The number of column areas
  165. * @returns AreaContainer The next column area
  166. */
  167. private AreaContainer addSpanArea( int numColumns )
  168. {
  169. resetHeights();
  170. // create span area and child column-areas, using whatever
  171. // height remains after existing span areas (in the main
  172. // reference area).
  173. int spanAreaYPosition = getYPosition() - this.mainReferenceArea.getContentHeight();
  174. SpanArea spanArea = new SpanArea(fontState,
  175. getXPosition(), spanAreaYPosition,
  176. allocationWidth, getRemainingHeight(),
  177. numColumns, columnGap);
  178. this.mainReferenceArea.addChild(spanArea);
  179. spanArea.setPage(this.getPage());
  180. this.isNewSpanArea = true;
  181. return spanArea.getCurrentColumnArea();
  182. }
  183. /**
  184. * This almost does what getNewArea() does, without actually
  185. * returning an area. These 2 methods can be reworked.
  186. * @param fo The next formatting object
  187. * @returns boolean True if we need to balance.
  188. */
  189. public boolean isBalancingRequired(FObj fo)
  190. {
  191. if (this.mainReferenceArea.getChildren().isEmpty())
  192. return false;
  193. Vector spanAreas = this.mainReferenceArea.getChildren();
  194. SpanArea spanArea = (SpanArea)spanAreas.elementAt(spanAreas.size()-1);
  195. if (spanArea.isBalanced())
  196. return false;
  197. int span = Span.NONE;
  198. if (fo instanceof Block)
  199. span = ((Block)fo).getSpan();
  200. else if (fo instanceof BlockContainer)
  201. span = ((BlockContainer)fo).getSpan();
  202. if ((span == Span.ALL) && (spanArea.getColumnCount() == 1))
  203. return false;
  204. else if ((span == Span.NONE) && (spanArea.getColumnCount() == columnCount))
  205. return false;
  206. else if (span == Span.ALL)
  207. return true;
  208. else if (span == Span.NONE)
  209. return false;
  210. else
  211. return false;
  212. }
  213. /**
  214. * This is where the balancing algorithm lives, or gets called.
  215. * Right now it's primitive: get the total content height in all
  216. * columns, divide by the column count, and add a heuristic
  217. * safety factor.
  218. * Then the previous (unbalanced) span area is removed, and a new
  219. * one added with the computed max height.
  220. */
  221. public void resetSpanArea()
  222. {
  223. Vector spanAreas = this.mainReferenceArea.getChildren();
  224. SpanArea spanArea = (SpanArea)spanAreas.elementAt(spanAreas.size()-1);
  225. if (!spanArea.isBalanced())
  226. {
  227. // span area maintains a record of the total height of
  228. // laid-out content in the previous (first) attempt
  229. int newHeight = spanArea.getTotalContentHeight() / spanArea.getColumnCount();
  230. newHeight += 2*15600; // ???
  231. this.mainReferenceArea.removeChild(spanArea);
  232. resetHeights();
  233. SpanArea newSpanArea = new SpanArea(fontState,
  234. getXPosition(), spanArea.getYPosition(),
  235. allocationWidth, newHeight,
  236. spanArea.getColumnCount(), columnGap);
  237. this.mainReferenceArea.addChild(newSpanArea);
  238. newSpanArea.setPage(this.getPage());
  239. newSpanArea.setIsBalanced();
  240. this.isNewSpanArea = true;
  241. }
  242. else
  243. {
  244. throw new IllegalStateException("Trying to balance balanced area");
  245. }
  246. }
  247. /**
  248. * Determine remaining height for new span area. Needs to be
  249. * modified for footnote and before-float reference areas when
  250. * those are supported.
  251. * @returns int The remaining available height in millipoints.
  252. */
  253. public int getRemainingHeight()
  254. {
  255. return this.mainReferenceArea.getMaxHeight() -
  256. this.mainReferenceArea.getContentHeight();
  257. }
  258. /**
  259. * Used by resetSpanArea() and addSpanArea() to adjust the main
  260. * reference area height before creating a new span.
  261. */
  262. private void resetHeights()
  263. {
  264. int totalHeight = 0;
  265. for (Enumeration e = this.mainReferenceArea.getChildren().elements(); e.hasMoreElements(); )
  266. {
  267. SpanArea spanArea = (SpanArea)e.nextElement();
  268. int spanContentHeight = spanArea.getMaxContentHeight();
  269. int spanMaxHeight = spanArea.getMaxHeight();
  270. totalHeight += (spanContentHeight < spanMaxHeight) ? spanContentHeight: spanMaxHeight;
  271. }
  272. this.mainReferenceArea.setHeight(totalHeight);
  273. }
  274. /**
  275. * Used in Flow when layout returns incomplete.
  276. * @returns boolean Is this the last column in this span?
  277. */
  278. public boolean isLastColumn()
  279. {
  280. Vector spanAreas = this.mainReferenceArea.getChildren();
  281. SpanArea spanArea = (SpanArea)spanAreas.elementAt(spanAreas.size()-1);
  282. return spanArea.isLastColumn();
  283. }
  284. /**
  285. * This variable is unset by getNextArea(), is set by addSpanArea(),
  286. * and <i>may</i> be set by resetSpanArea().
  287. * @returns boolean Is the span area new or not?
  288. */
  289. public boolean isNewSpanArea()
  290. {
  291. return isNewSpanArea;
  292. }
  293. public AreaContainer getCurrentColumnArea()
  294. {
  295. Vector spanAreas = this.mainReferenceArea.getChildren();
  296. SpanArea spanArea = (SpanArea)spanAreas.elementAt(spanAreas.size()-1);
  297. return spanArea.getCurrentColumnArea();
  298. }
  299. public int getFootnoteState()
  300. {
  301. return footnoteState;
  302. }
  303. public boolean needsFootnoteAdjusting()
  304. {
  305. footnoteYPosition = footnoteReferenceArea.getYPosition();
  306. switch(footnoteState) {
  307. case 0:
  308. resetHeights();
  309. if(footnoteReferenceArea.getHeight() > 0
  310. && mainYPosition + mainReferenceArea.getHeight() > footnoteYPosition) {
  311. return true;
  312. }
  313. case 1:
  314. break;
  315. }
  316. return false;
  317. }
  318. public void adjustFootnoteArea()
  319. {
  320. footnoteState++;
  321. if(footnoteState == 1) {
  322. mainReferenceArea.setMaxHeight(footnoteReferenceArea.getYPosition() - mainYPosition);
  323. footnoteYPosition = footnoteReferenceArea.getYPosition();
  324. footnoteReferenceArea.setMaxHeight(footnoteReferenceArea.getHeight());
  325. Vector childs = footnoteReferenceArea.getChildren();
  326. for(Enumeration en = childs.elements(); en.hasMoreElements(); ) {
  327. Object obj = en.nextElement();
  328. if(obj instanceof Area) {
  329. Area childArea = (Area)obj;
  330. footnoteReferenceArea.removeChild(childArea);
  331. }
  332. }
  333. getPage().setPendingFootnotes(null);
  334. }
  335. }
  336. protected static void resetMaxHeight(Area ar, int change) {
  337. ar.setMaxHeight(change);
  338. Vector childs = ar.getChildren();
  339. for(Enumeration en = childs.elements(); en.hasMoreElements(); ) {
  340. Object obj = en.nextElement();
  341. if(obj instanceof Area) {
  342. Area childArea = (Area)obj;
  343. resetMaxHeight(childArea, change);
  344. }
  345. }
  346. }
  347. }