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 14KB

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