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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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 java.util.Stack;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.apache.fop.area.Area;
  26. import org.apache.fop.area.BlockParent;
  27. import org.apache.fop.fo.pagination.Flow;
  28. import org.apache.fop.util.ListUtil;
  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. @Override
  56. public List getNextKnuthElements(LayoutContext context, int alignment) {
  57. return getNextKnuthElements(context, alignment, null, null);
  58. }
  59. /**
  60. * Get a sequence of KnuthElements representing the content
  61. * of the node assigned to the LM.
  62. * @param context the LayoutContext used to store layout information
  63. * @param alignment the desired text alignment
  64. * @param restartPosition {@link Position} to restart from
  65. * @param restartLM {@link LayoutManager} to restart from
  66. * @return the list of KnuthElements
  67. * @see LayoutManager#getNextKnuthElements(LayoutContext,int)
  68. */
  69. List getNextKnuthElements(LayoutContext context, int alignment,
  70. Position restartPosition, LayoutManager restartLM) {
  71. List<ListElement> elements = new LinkedList<ListElement>();
  72. boolean isRestart = (restartPosition != null);
  73. LayoutManager currentChildLM;
  74. if (isRestart) {
  75. currentChildLM = restartPosition.getLM();
  76. if (currentChildLM == null) {
  77. throw new IllegalStateException(
  78. "Cannot find layout manager from where to re-start layout after IPD change");
  79. }
  80. if (restartLM != null && restartLM.getParent() == this) {
  81. currentChildLM = restartLM;
  82. setCurrentChildLM(currentChildLM);
  83. currentChildLM.reset();
  84. if (addChildElements(elements, currentChildLM, context, alignment) != null) {
  85. return elements;
  86. }
  87. } else {
  88. Stack<LayoutManager> lmStack = new Stack<LayoutManager>();
  89. while (currentChildLM.getParent() != this) {
  90. lmStack.push(currentChildLM);
  91. currentChildLM = currentChildLM.getParent();
  92. }
  93. setCurrentChildLM(currentChildLM);
  94. if (addChildElements(elements, currentChildLM, context, alignment, lmStack,
  95. restartPosition, restartLM) != null) {
  96. return elements;
  97. }
  98. }
  99. }
  100. while ((currentChildLM = getChildLM()) != null) {
  101. if (isRestart) {
  102. currentChildLM.reset(); // TODO won't work with forced breaks
  103. }
  104. if (addChildElements(elements, currentChildLM, context, alignment) != null) {
  105. return elements;
  106. }
  107. }
  108. SpaceResolver.resolveElementList(elements);
  109. setFinished(true);
  110. assert !elements.isEmpty();
  111. return elements;
  112. }
  113. private List<ListElement> addChildElements(List<ListElement> elements,
  114. LayoutManager childLM, LayoutContext context, int alignment) {
  115. return addChildElements(elements, childLM, context, alignment, null, null, null);
  116. }
  117. private List<ListElement> addChildElements(List<ListElement> elements,
  118. LayoutManager childLM, LayoutContext context, int alignment,
  119. Stack<LayoutManager> lmStack, Position position, LayoutManager restartAtLM) {
  120. if (handleSpanChange(childLM, context)) {
  121. SpaceResolver.resolveElementList(elements);
  122. return elements;
  123. }
  124. LayoutContext childLC = new LayoutContext(0);
  125. List<ListElement> childElements
  126. = getNextChildElements(childLM, context, childLC, alignment, lmStack,
  127. position, restartAtLM);
  128. if (elements.isEmpty()) {
  129. context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
  130. }
  131. if (!elements.isEmpty()
  132. && !ElementListUtils.startsWithForcedBreak(childElements)) {
  133. addInBetweenBreak(elements, context, childLC);
  134. }
  135. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  136. elements.addAll(childElements);
  137. if (ElementListUtils.endsWithForcedBreak(elements)) {
  138. // a descendant of this flow has break-before or break-after
  139. if (childLM.isFinished() && !hasNextChildLM()) {
  140. setFinished(true);
  141. }
  142. SpaceResolver.resolveElementList(elements);
  143. return elements;
  144. }
  145. return null;
  146. }
  147. private boolean handleSpanChange(LayoutManager childLM, LayoutContext context) {
  148. int span = EN_NONE;
  149. int disableColumnBalancing = EN_FALSE;
  150. if (childLM instanceof BlockLayoutManager) {
  151. span = ((BlockLayoutManager)childLM).getBlockFO().getSpan();
  152. disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO()
  153. .getDisableColumnBalancing();
  154. } else if (childLM instanceof BlockContainerLayoutManager) {
  155. span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan();
  156. disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO()
  157. .getDisableColumnBalancing();
  158. }
  159. int currentSpan = context.getCurrentSpan();
  160. if (currentSpan != span) {
  161. if (span == EN_ALL) {
  162. context.setDisableColumnBalancing(disableColumnBalancing);
  163. }
  164. log.debug("span change from " + currentSpan + " to " + span);
  165. context.signalSpanChange(span);
  166. return true;
  167. } else {
  168. return false;
  169. }
  170. }
  171. private List<ListElement> getNextChildElements(LayoutManager childLM, LayoutContext context,
  172. LayoutContext childLC, int alignment, Stack<LayoutManager> lmStack,
  173. Position restartPosition, LayoutManager restartLM) {
  174. childLC.setStackLimitBP(context.getStackLimitBP());
  175. childLC.setRefIPD(context.getRefIPD());
  176. childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
  177. List<ListElement> childElements;
  178. if (lmStack == null) {
  179. childElements = childLM.getNextKnuthElements(childLC, alignment);
  180. } else {
  181. childElements = childLM.getNextKnuthElements(childLC, alignment,
  182. lmStack, restartPosition, restartLM);
  183. }
  184. assert !childElements.isEmpty();
  185. // "wrap" the Position inside each element
  186. List tempList = childElements;
  187. childElements = new LinkedList<ListElement>();
  188. wrapPositionElements(tempList, childElements);
  189. return childElements;
  190. }
  191. /** {@inheritDoc} */
  192. @Override
  193. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  194. log.debug(" FLM.negotiateBPDAdjustment> " + adj);
  195. if (lastElement.getPosition() instanceof NonLeafPosition) {
  196. // this element was not created by this FlowLM
  197. NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
  198. lastElement.setPosition(savedPos.getPosition());
  199. int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
  200. .negotiateBPDAdjustment(adj, lastElement);
  201. lastElement.setPosition(savedPos);
  202. log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
  203. return returnValue;
  204. } else {
  205. return 0;
  206. }
  207. }
  208. /** {@inheritDoc} */
  209. @Override
  210. public void discardSpace(KnuthGlue spaceGlue) {
  211. log.debug(" FLM.discardSpace> ");
  212. if (spaceGlue.getPosition() instanceof NonLeafPosition) {
  213. // this element was not created by this FlowLM
  214. NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
  215. spaceGlue.setPosition(savedPos.getPosition());
  216. ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
  217. spaceGlue.setPosition(savedPos);
  218. }
  219. }
  220. /** {@inheritDoc} */
  221. @Override
  222. public Keep getKeepTogether() {
  223. return Keep.KEEP_AUTO;
  224. }
  225. /** {@inheritDoc} */
  226. @Override
  227. public Keep getKeepWithNext() {
  228. return Keep.KEEP_AUTO;
  229. }
  230. /** {@inheritDoc} */
  231. @Override
  232. public Keep getKeepWithPrevious() {
  233. return Keep.KEEP_AUTO;
  234. }
  235. /** {@inheritDoc} */
  236. @Override
  237. public List<KnuthElement> getChangedKnuthElements(List oldList, int alignment) {
  238. ListIterator<KnuthElement> oldListIterator = oldList.listIterator();
  239. KnuthElement returnedElement;
  240. List<KnuthElement> returnedList = new LinkedList<KnuthElement>();
  241. List<KnuthElement> returnList = new LinkedList<KnuthElement>();
  242. KnuthElement prevElement = null;
  243. KnuthElement currElement = null;
  244. int fromIndex = 0;
  245. // "unwrap" the Positions stored in the elements
  246. KnuthElement oldElement;
  247. while (oldListIterator.hasNext()) {
  248. oldElement = oldListIterator.next();
  249. if (oldElement.getPosition() instanceof NonLeafPosition) {
  250. // oldElement was created by a descendant of this FlowLM
  251. oldElement.setPosition((oldElement.getPosition()).getPosition());
  252. } else {
  253. // thisElement was created by this FlowLM, remove it
  254. oldListIterator.remove();
  255. }
  256. }
  257. // reset the iterator
  258. oldListIterator = oldList.listIterator();
  259. while (oldListIterator.hasNext()) {
  260. currElement = oldListIterator.next();
  261. if (prevElement != null
  262. && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
  263. // prevElement is the last element generated by the same LM
  264. BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
  265. prevElement.getLayoutManager();
  266. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  267. currElement.getLayoutManager();
  268. returnedList.addAll(prevLM.getChangedKnuthElements(
  269. oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment));
  270. fromIndex = oldListIterator.previousIndex();
  271. // there is another block after this one
  272. if (prevLM.mustKeepWithNext()
  273. || currLM.mustKeepWithPrevious()) {
  274. // add an infinite penalty to forbid a break between blocks
  275. returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
  276. new Position(this), false));
  277. } else if (!ListUtil.getLast(returnedList).isGlue()) {
  278. // add a null penalty to allow a break between blocks
  279. returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  280. }
  281. }
  282. prevElement = currElement;
  283. }
  284. if (currElement != null) {
  285. BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
  286. currElement.getLayoutManager();
  287. returnedList.addAll(currLM.getChangedKnuthElements(
  288. oldList.subList(fromIndex, oldList.size()), alignment));
  289. }
  290. // "wrap" the Position stored in each element of returnedList
  291. // and add elements to returnList
  292. ListIterator<KnuthElement> listIter = returnedList.listIterator();
  293. while (listIter.hasNext()) {
  294. returnedElement = listIter.next();
  295. if (returnedElement.getLayoutManager() != this) {
  296. returnedElement.setPosition(
  297. new NonLeafPosition(this, returnedElement.getPosition()));
  298. }
  299. returnList.add(returnedElement);
  300. }
  301. return returnList;
  302. }
  303. /** {@inheritDoc} */
  304. @Override
  305. public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
  306. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  307. flush();
  308. }
  309. /**
  310. * Add child area to a the correct container, depending on its
  311. * area class. A Flow can fill at most one area container of any class
  312. * at any one time. The actual work is done by BlockStackingLM.
  313. *
  314. * @param childArea the area to add
  315. */
  316. @Override
  317. public void addChildArea(Area childArea) {
  318. getParentArea(childArea);
  319. addChildToArea(childArea,
  320. this.currentAreas[childArea.getAreaClass()]);
  321. }
  322. /** {@inheritDoc} */
  323. @Override
  324. public Area getParentArea(Area childArea) {
  325. BlockParent parentArea = null;
  326. int aclass = childArea.getAreaClass();
  327. if (aclass == Area.CLASS_NORMAL) {
  328. parentArea = getCurrentPV().getCurrentFlow();
  329. } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
  330. parentArea = getCurrentPV().getBodyRegion().getBeforeFloat();
  331. } else if (aclass == Area.CLASS_FOOTNOTE) {
  332. parentArea = getCurrentPV().getBodyRegion().getFootnote();
  333. } else {
  334. throw new IllegalStateException("(internal error) Invalid "
  335. + "area class (" + aclass + ") requested.");
  336. }
  337. this.currentAreas[aclass] = parentArea;
  338. setCurrentArea(parentArea);
  339. return parentArea;
  340. }
  341. /**
  342. * Returns the IPD of the content area
  343. * @return the IPD of the content area
  344. */
  345. @Override
  346. public int getContentAreaIPD() {
  347. return getCurrentPV().getCurrentSpan().getColumnWidth();
  348. }
  349. /**
  350. * Returns the BPD of the content area
  351. * @return the BPD of the content area
  352. */
  353. @Override
  354. public int getContentAreaBPD() {
  355. return getCurrentPV().getBodyRegion().getBPD();
  356. }
  357. /** {@inheritDoc} */
  358. @Override
  359. public boolean isRestartable() {
  360. return true;
  361. }
  362. }