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

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