Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

FlowLayoutManager.java 16KB

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