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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
  28. import org.apache.fop.layoutmgr.inline.WrapperLayoutManager;
  29. /**
  30. * LayoutManager for an fo:flow object.
  31. * Its parent LM is the PageSequenceLayoutManager.
  32. * This LM is responsible for getting columns of the appropriate size
  33. * and filling them with block-level areas generated by its children.
  34. * @todo Reintroduce emergency counter (generate error to avoid endless loop)
  35. */
  36. public class FlowLayoutManager extends BlockStackingLayoutManager
  37. implements BlockLevelLayoutManager {
  38. /**
  39. * logging instance
  40. */
  41. private static Log log = LogFactory.getLog(FlowLayoutManager.class);
  42. /** Array of areas currently being filled stored by area class */
  43. private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
  44. private int currentSpan = EN_NONE;
  45. /**
  46. * This is the top level layout manager.
  47. * It is created by the PageSequence FO.
  48. * @param pslm parent PageSequenceLayoutManager object
  49. * @param node Flow object
  50. */
  51. public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) {
  52. super(node);
  53. setParent(pslm);
  54. }
  55. /** {@inheritDoc} */
  56. public List getNextKnuthElements(LayoutContext context, int alignment) {
  57. // set layout dimensions
  58. int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth();
  59. int flowBPD = getCurrentPV().getBodyRegion().getBPD();
  60. // currently active LM
  61. LayoutManager curLM;
  62. List returnedList;
  63. List returnList = new LinkedList();
  64. while ((curLM = getChildLM()) != null) {
  65. if (!(curLM instanceof WrapperLayoutManager)
  66. && curLM instanceof InlineLevelLayoutManager) {
  67. log.error("inline area not allowed under flow - ignoring");
  68. curLM.setFinished(true);
  69. continue;
  70. }
  71. int span = EN_NONE;
  72. if (curLM instanceof BlockLayoutManager) {
  73. span = ((BlockLayoutManager)curLM).getBlockFO().getSpan();
  74. } else if (curLM instanceof BlockContainerLayoutManager) {
  75. span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan();
  76. }
  77. if (currentSpan != span) {
  78. log.debug("span change from " + currentSpan + " to " + span);
  79. context.signalSpanChange(span);
  80. currentSpan = span;
  81. SpaceResolver.resolveElementList(returnList);
  82. return returnList;
  83. }
  84. // Set up a LayoutContext
  85. //MinOptMax bpd = context.getStackLimit();
  86. LayoutContext childLC = new LayoutContext(0);
  87. childLC.setStackLimitBP(context.getStackLimitBP());
  88. childLC.setRefIPD(context.getRefIPD());
  89. childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
  90. // get elements from curLM
  91. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  92. //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
  93. if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) {
  94. context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
  95. childLC.clearKeepWithPreviousPending();
  96. }
  97. // "wrap" the Position inside each element
  98. List tempList = returnedList;
  99. returnedList = new LinkedList();
  100. wrapPositionElements(tempList, returnedList);
  101. if (returnedList.size() == 1
  102. && ElementListUtils.endsWithForcedBreak(returnedList)) {
  103. // a descendant of this flow has break-before
  104. returnList.addAll(returnedList);
  105. SpaceResolver.resolveElementList(returnList);
  106. return returnList;
  107. } else {
  108. if (returnList.size() > 0) {
  109. addInBetweenBreak(returnList, context, childLC);
  110. }
  111. if (returnedList.size() > 0) {
  112. returnList.addAll(returnedList);
  113. if (ElementListUtils.endsWithForcedBreak(returnList)) {
  114. if (curLM.isFinished() && !hasNextChildLM()) {
  115. //If the layout manager is finished at this point, the pending
  116. //marks become irrelevant.
  117. childLC.clearPendingMarks();
  118. //setFinished(true);
  119. break;
  120. }
  121. // a descendant of this flow has break-after
  122. SpaceResolver.resolveElementList(returnList);
  123. return returnList;
  124. }
  125. }
  126. }
  127. //Propagate and clear
  128. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  129. childLC.clearKeepWithNextPending();
  130. context.updateKeepWithNextPending(getKeepWithNextStrength());
  131. }
  132. SpaceResolver.resolveElementList(returnList);
  133. setFinished(true);
  134. if (returnList.size() > 0) {
  135. return returnList;
  136. } else {
  137. return null;
  138. }
  139. }
  140. /**
  141. * {@inheritDoc}
  142. */
  143. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  144. log.debug(" FLM.negotiateBPDAdjustment> " + adj);
  145. if (lastElement.getPosition() instanceof NonLeafPosition) {
  146. // this element was not created by this FlowLM
  147. NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
  148. lastElement.setPosition(savedPos.getPosition());
  149. int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
  150. .negotiateBPDAdjustment(adj, lastElement);
  151. lastElement.setPosition(savedPos);
  152. log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
  153. return returnValue;
  154. } else {
  155. return 0;
  156. }
  157. }
  158. /**
  159. * {@inheritDoc}
  160. */
  161. public void discardSpace(KnuthGlue spaceGlue) {
  162. log.debug(" FLM.discardSpace> ");
  163. if (spaceGlue.getPosition() instanceof NonLeafPosition) {
  164. // this element was not created by this FlowLM
  165. NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
  166. spaceGlue.setPosition(savedPos.getPosition());
  167. ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
  168. spaceGlue.setPosition(savedPos);
  169. }
  170. }
  171. /** {@inheritDoc} */
  172. public int getKeepTogetherStrength() {
  173. return KEEP_AUTO;
  174. }
  175. /** {@inheritDoc} */
  176. public int getKeepWithNextStrength() {
  177. return KEEP_AUTO;
  178. }
  179. /** {@inheritDoc} */
  180. public int getKeepWithPreviousStrength() {
  181. return KEEP_AUTO;
  182. }
  183. /** {@inheritDoc} */
  184. public List getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
  185. ListIterator oldListIterator = oldList.listIterator();
  186. KnuthElement returnedElement;
  187. List returnedList = new LinkedList();
  188. List returnList = new LinkedList();
  189. KnuthElement prevElement = null;
  190. KnuthElement currElement = null;
  191. int fromIndex = 0;
  192. // "unwrap" the Positions stored in the elements
  193. KnuthElement oldElement;
  194. while (oldListIterator.hasNext()) {
  195. oldElement = (KnuthElement)oldListIterator.next();
  196. if (oldElement.getPosition() instanceof NonLeafPosition) {
  197. // oldElement was created by a descendant of this FlowLM
  198. oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition());
  199. } else {
  200. // thisElement was created by this FlowLM, remove it
  201. oldListIterator.remove();
  202. }
  203. }
  204. // reset the iterator
  205. oldListIterator = oldList.listIterator();
  206. while (oldListIterator.hasNext()) {
  207. currElement = (KnuthElement) oldListIterator.next();
  208. if (prevElement != null
  209. && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
  210. // prevElement is the last element generated by the same LM
  211. BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
  212. prevElement.getLayoutManager();
  213. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  214. currElement.getLayoutManager();
  215. returnedList.addAll(prevLM.getChangedKnuthElements(
  216. oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment));
  217. fromIndex = oldListIterator.previousIndex();
  218. // there is another block after this one
  219. if (prevLM.mustKeepWithNext()
  220. || currLM.mustKeepWithPrevious()) {
  221. // add an infinite penalty to forbid a break between blocks
  222. returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
  223. new Position(this), false));
  224. } else if (!((KnuthElement) returnedList.get(returnedList
  225. .size() - 1)).isGlue()) {
  226. // add a null penalty to allow a break between blocks
  227. returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  228. }
  229. }
  230. prevElement = currElement;
  231. }
  232. if (currElement != null) {
  233. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  234. currElement.getLayoutManager();
  235. returnedList.addAll(currLM.getChangedKnuthElements(
  236. oldList.subList(fromIndex, oldList.size()), alignment));
  237. }
  238. // "wrap" the Position stored in each element of returnedList
  239. // and add elements to returnList
  240. ListIterator listIter = returnedList.listIterator();
  241. while (listIter.hasNext()) {
  242. returnedElement = (KnuthElement)listIter.next();
  243. if (returnedElement.getLayoutManager() != this) {
  244. returnedElement.setPosition(
  245. new NonLeafPosition(this, returnedElement.getPosition()));
  246. }
  247. returnList.add(returnedElement);
  248. }
  249. return returnList;
  250. }
  251. /**
  252. * {@inheritDoc}
  253. */
  254. public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
  255. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  256. flush();
  257. }
  258. /**
  259. * Add child area to a the correct container, depending on its
  260. * area class. A Flow can fill at most one area container of any class
  261. * at any one time. The actual work is done by BlockStackingLM.
  262. *
  263. * @param childArea the area to add
  264. */
  265. public void addChildArea(Area childArea) {
  266. getParentArea(childArea);
  267. addChildToArea(childArea,
  268. this.currentAreas[childArea.getAreaClass()]);
  269. }
  270. /**
  271. * {@inheritDoc}
  272. */
  273. public Area getParentArea(Area childArea) {
  274. BlockParent parentArea = null;
  275. int aclass = childArea.getAreaClass();
  276. if (aclass == Area.CLASS_NORMAL) {
  277. parentArea = getCurrentPV().getCurrentFlow();
  278. } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
  279. parentArea = getCurrentPV().getBodyRegion().getBeforeFloat();
  280. } else if (aclass == Area.CLASS_FOOTNOTE) {
  281. parentArea = getCurrentPV().getBodyRegion().getFootnote();
  282. } else {
  283. throw new IllegalStateException("(internal error) Invalid "
  284. + "area class (" + aclass + ") requested.");
  285. }
  286. this.currentAreas[aclass] = parentArea;
  287. setCurrentArea(parentArea);
  288. return parentArea;
  289. }
  290. /**
  291. * Returns the IPD of the content area
  292. * @return the IPD of the content area
  293. */
  294. public int getContentAreaIPD() {
  295. return getCurrentPV().getCurrentSpan().getColumnWidth();
  296. }
  297. /**
  298. * Returns the BPD of the content area
  299. * @return the BPD of the content area
  300. */
  301. public int getContentAreaBPD() {
  302. return (int) getCurrentPV().getBodyRegion().getBPD();
  303. }
  304. }