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.

FlowLayoutManager.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.layoutmgr;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.ListIterator;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.apache.fop.area.Area;
  25. import org.apache.fop.area.BlockParent;
  26. import org.apache.fop.fo.pagination.Flow;
  27. /**
  28. * LayoutManager for an fo:flow object.
  29. * Its parent LM is the PageSequenceLayoutManager.
  30. * This LM is responsible for getting columns of the appropriate size
  31. * and filling them with block-level areas generated by its children.
  32. * @todo Reintroduce emergency counter (generate error to avoid endless loop)
  33. */
  34. public class FlowLayoutManager extends BlockStackingLayoutManager
  35. implements BlockLevelLayoutManager {
  36. /**
  37. * logging instance
  38. */
  39. private static Log log = LogFactory.getLog(FlowLayoutManager.class);
  40. /** Array of areas currently being filled stored by area class */
  41. private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
  42. /**
  43. * This is the top level layout manager.
  44. * It is created by the PageSequence FO.
  45. * @param pslm parent PageSequenceLayoutManager object
  46. * @param node Flow object
  47. */
  48. public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) {
  49. super(node);
  50. setParent(pslm);
  51. }
  52. /** {@inheritDoc} */
  53. public List getNextKnuthElements(LayoutContext context, int alignment) {
  54. List elements = new LinkedList();
  55. LayoutManager currentChildLM;
  56. while ((currentChildLM = getChildLM()) != null) {
  57. if (handleSpanChange(currentChildLM, elements, context)) {
  58. SpaceResolver.resolveElementList(elements);
  59. return elements;
  60. }
  61. LayoutContext childLC = new LayoutContext(0);
  62. List childrenElements = getNextChildElements(currentChildLM, context, childLC,
  63. alignment);
  64. if (elements.isEmpty()) {
  65. context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
  66. }
  67. if (!elements.isEmpty()
  68. && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
  69. addInBetweenBreak(elements, context, childLC);
  70. }
  71. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  72. elements.addAll(childrenElements);
  73. if (ElementListUtils.endsWithForcedBreak(elements)) {
  74. // a descendant of this flow has break-before or break-after
  75. if (currentChildLM.isFinished() && !hasNextChildLM()) {
  76. setFinished(true);
  77. }
  78. SpaceResolver.resolveElementList(elements);
  79. return elements;
  80. }
  81. }
  82. SpaceResolver.resolveElementList(elements);
  83. setFinished(true);
  84. assert !elements.isEmpty();
  85. return elements;
  86. }
  87. private boolean handleSpanChange(LayoutManager childLM, List elements, LayoutContext context) {
  88. int span = EN_NONE;
  89. int disableColumnBalancing = EN_FALSE;
  90. if (childLM instanceof BlockLayoutManager) {
  91. span = ((BlockLayoutManager)childLM).getBlockFO().getSpan();
  92. disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO()
  93. .getDisableColumnBalancing();
  94. } else if (childLM instanceof BlockContainerLayoutManager) {
  95. span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan();
  96. disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO()
  97. .getDisableColumnBalancing();
  98. }
  99. int currentSpan = context.getCurrentSpan();
  100. if (currentSpan != span) {
  101. if (span == EN_ALL) {
  102. context.setDisableColumnBalancing(disableColumnBalancing);
  103. }
  104. log.debug("span change from " + currentSpan + " to " + span);
  105. context.signalSpanChange(span);
  106. return true;
  107. } else {
  108. return false;
  109. }
  110. }
  111. private List getNextChildElements(LayoutManager childLM, LayoutContext context,
  112. LayoutContext childLC, int alignment) {
  113. childLC.setStackLimitBP(context.getStackLimitBP());
  114. childLC.setRefIPD(context.getRefIPD());
  115. childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
  116. // get elements from curLM
  117. List childrenElements = childLM.getNextKnuthElements(childLC, alignment);
  118. assert !childrenElements.isEmpty();
  119. // "wrap" the Position inside each element
  120. List tempList = childrenElements;
  121. childrenElements = new LinkedList();
  122. wrapPositionElements(tempList, childrenElements);
  123. return childrenElements;
  124. }
  125. /**
  126. * {@inheritDoc}
  127. */
  128. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  129. log.debug(" FLM.negotiateBPDAdjustment> " + adj);
  130. if (lastElement.getPosition() instanceof NonLeafPosition) {
  131. // this element was not created by this FlowLM
  132. NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
  133. lastElement.setPosition(savedPos.getPosition());
  134. int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
  135. .negotiateBPDAdjustment(adj, lastElement);
  136. lastElement.setPosition(savedPos);
  137. log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
  138. return returnValue;
  139. } else {
  140. return 0;
  141. }
  142. }
  143. /**
  144. * {@inheritDoc}
  145. */
  146. public void discardSpace(KnuthGlue spaceGlue) {
  147. log.debug(" FLM.discardSpace> ");
  148. if (spaceGlue.getPosition() instanceof NonLeafPosition) {
  149. // this element was not created by this FlowLM
  150. NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
  151. spaceGlue.setPosition(savedPos.getPosition());
  152. ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
  153. spaceGlue.setPosition(savedPos);
  154. }
  155. }
  156. /** {@inheritDoc} */
  157. public int getKeepTogetherStrength() {
  158. return KEEP_AUTO;
  159. }
  160. /** {@inheritDoc} */
  161. public int getKeepWithNextStrength() {
  162. return KEEP_AUTO;
  163. }
  164. /** {@inheritDoc} */
  165. public int getKeepWithPreviousStrength() {
  166. return KEEP_AUTO;
  167. }
  168. /** {@inheritDoc} */
  169. public List getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
  170. ListIterator oldListIterator = oldList.listIterator();
  171. KnuthElement returnedElement;
  172. List returnedList = new LinkedList();
  173. List returnList = new LinkedList();
  174. KnuthElement prevElement = null;
  175. KnuthElement currElement = null;
  176. int fromIndex = 0;
  177. // "unwrap" the Positions stored in the elements
  178. KnuthElement oldElement;
  179. while (oldListIterator.hasNext()) {
  180. oldElement = (KnuthElement)oldListIterator.next();
  181. if (oldElement.getPosition() instanceof NonLeafPosition) {
  182. // oldElement was created by a descendant of this FlowLM
  183. oldElement.setPosition((oldElement.getPosition()).getPosition());
  184. } else {
  185. // thisElement was created by this FlowLM, remove it
  186. oldListIterator.remove();
  187. }
  188. }
  189. // reset the iterator
  190. oldListIterator = oldList.listIterator();
  191. while (oldListIterator.hasNext()) {
  192. currElement = (KnuthElement) oldListIterator.next();
  193. if (prevElement != null
  194. && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
  195. // prevElement is the last element generated by the same LM
  196. BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
  197. prevElement.getLayoutManager();
  198. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  199. currElement.getLayoutManager();
  200. returnedList.addAll(prevLM.getChangedKnuthElements(
  201. oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment));
  202. fromIndex = oldListIterator.previousIndex();
  203. // there is another block after this one
  204. if (prevLM.mustKeepWithNext()
  205. || currLM.mustKeepWithPrevious()) {
  206. // add an infinite penalty to forbid a break between blocks
  207. returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
  208. new Position(this), false));
  209. } else if (!((KnuthElement) returnedList.get(returnedList
  210. .size() - 1)).isGlue()) {
  211. // add a null penalty to allow a break between blocks
  212. returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  213. }
  214. }
  215. prevElement = currElement;
  216. }
  217. if (currElement != null) {
  218. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  219. currElement.getLayoutManager();
  220. returnedList.addAll(currLM.getChangedKnuthElements(
  221. oldList.subList(fromIndex, oldList.size()), alignment));
  222. }
  223. // "wrap" the Position stored in each element of returnedList
  224. // and add elements to returnList
  225. ListIterator listIter = returnedList.listIterator();
  226. while (listIter.hasNext()) {
  227. returnedElement = (KnuthElement)listIter.next();
  228. if (returnedElement.getLayoutManager() != this) {
  229. returnedElement.setPosition(
  230. new NonLeafPosition(this, returnedElement.getPosition()));
  231. }
  232. returnList.add(returnedElement);
  233. }
  234. return returnList;
  235. }
  236. /**
  237. * {@inheritDoc}
  238. */
  239. public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
  240. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  241. flush();
  242. }
  243. /**
  244. * Add child area to a the correct container, depending on its
  245. * area class. A Flow can fill at most one area container of any class
  246. * at any one time. The actual work is done by BlockStackingLM.
  247. *
  248. * @param childArea the area to add
  249. */
  250. public void addChildArea(Area childArea) {
  251. getParentArea(childArea);
  252. addChildToArea(childArea,
  253. this.currentAreas[childArea.getAreaClass()]);
  254. }
  255. /**
  256. * {@inheritDoc}
  257. */
  258. public Area getParentArea(Area childArea) {
  259. BlockParent parentArea = null;
  260. int aclass = childArea.getAreaClass();
  261. if (aclass == Area.CLASS_NORMAL) {
  262. parentArea = getCurrentPV().getCurrentFlow();
  263. } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
  264. parentArea = getCurrentPV().getBodyRegion().getBeforeFloat();
  265. } else if (aclass == Area.CLASS_FOOTNOTE) {
  266. parentArea = getCurrentPV().getBodyRegion().getFootnote();
  267. } else {
  268. throw new IllegalStateException("(internal error) Invalid "
  269. + "area class (" + aclass + ") requested.");
  270. }
  271. this.currentAreas[aclass] = parentArea;
  272. setCurrentArea(parentArea);
  273. return parentArea;
  274. }
  275. /**
  276. * Returns the IPD of the content area
  277. * @return the IPD of the content area
  278. */
  279. public int getContentAreaIPD() {
  280. return getCurrentPV().getCurrentSpan().getColumnWidth();
  281. }
  282. /**
  283. * Returns the BPD of the content area
  284. * @return the BPD of the content area
  285. */
  286. public int getContentAreaBPD() {
  287. return getCurrentPV().getBodyRegion().getBPD();
  288. }
  289. }