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.

BlockStackingLayoutManager.java 51KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256
  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.Block;
  27. import org.apache.fop.area.BlockParent;
  28. import org.apache.fop.fo.Constants;
  29. import org.apache.fop.fo.FObj;
  30. import org.apache.fop.fo.properties.BreakPropertySet;
  31. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  32. import org.apache.fop.fo.properties.KeepProperty;
  33. import org.apache.fop.fo.properties.SpaceProperty;
  34. import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager;
  35. import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
  36. import org.apache.fop.traits.MinOptMax;
  37. import org.apache.fop.util.ListUtil;
  38. /**
  39. * Base LayoutManager class for all areas which stack their child
  40. * areas in the block-progression direction, such as Flow, Block, ListBlock.
  41. */
  42. public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
  43. implements BlockLevelLayoutManager {
  44. /** logging instance */
  45. private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class);
  46. /** parent area */
  47. protected BlockParent parentArea;
  48. /** Value of the block-progression-unit (non-standard property) */
  49. protected int bpUnit;
  50. /** space-before value adjusted for block-progression-unit handling */
  51. protected int adjustedSpaceBefore;
  52. /** space-after value adjusted for block-progression-unit handling */
  53. protected int adjustedSpaceAfter;
  54. /** Only used to store the original list when createUnitElements is called */
  55. protected List<KnuthElement> storedList;
  56. /** Indicates whether break before has been served or not */
  57. protected boolean breakBeforeServed;
  58. /** Indicates whether the first visible mark has been returned by this LM, yet */
  59. protected boolean firstVisibleMarkServed;
  60. /** Reference IPD available */
  61. protected int referenceIPD;
  62. /** the effective start-indent value */
  63. protected int startIndent;
  64. /** the effective end-indent value */
  65. protected int endIndent;
  66. /**
  67. * Holds the (one-time use) fo:block space-before
  68. * and -after properties. Large fo:blocks are split
  69. * into multiple Area. Blocks to accomodate the subsequent
  70. * regions (pages) they are placed on. space-before
  71. * is applied at the beginning of the first
  72. * Block and space-after at the end of the last Block
  73. * used in rendering the fo:block.
  74. */
  75. protected MinOptMax foSpaceBefore;
  76. /** see foSpaceBefore */
  77. protected MinOptMax foSpaceAfter;
  78. private Position auxiliaryPosition;
  79. private int contentAreaIPD;
  80. /**
  81. * @param node the fo this LM deals with
  82. */
  83. public BlockStackingLayoutManager(FObj node) {
  84. super(node);
  85. setGeneratesBlockArea(true);
  86. }
  87. /**
  88. * @return current area being filled
  89. */
  90. protected BlockParent getCurrentArea() {
  91. return this.parentArea;
  92. }
  93. /**
  94. * Set the current area being filled.
  95. * @param parentArea the current area to be filled
  96. */
  97. protected void setCurrentArea(BlockParent parentArea) {
  98. this.parentArea = parentArea;
  99. }
  100. /**
  101. * Add a block spacer for space before and space after a block.
  102. * This adds an empty Block area that acts as a block space.
  103. *
  104. * @param adjust the adjustment value
  105. * @param minoptmax the min/opt/max value of the spacing
  106. */
  107. public void addBlockSpacing(double adjust, MinOptMax minoptmax) {
  108. int sp = TraitSetter.getEffectiveSpace(adjust, minoptmax);
  109. if (sp != 0) {
  110. Block spacer = new Block();
  111. spacer.setBPD(sp);
  112. parentLayoutManager.addChildArea(spacer);
  113. }
  114. }
  115. /**
  116. * Add the childArea to the passed area.
  117. * Called by child LayoutManager when it has filled one of its areas.
  118. * The LM should already have an Area in which to put the child.
  119. * See if the area will fit in the current area.
  120. * If so, add it. Otherwise initiate breaking.
  121. * @param childArea the area to add: will be some block-stacked Area.
  122. * @param parentArea the area in which to add the childArea
  123. */
  124. protected void addChildToArea(Area childArea,
  125. BlockParent parentArea) {
  126. // This should be a block-level Area (Block in the generic sense)
  127. if (!(childArea instanceof Block)) {
  128. //log.error("Child not a Block in BlockStackingLM!");
  129. }
  130. parentArea.addBlock((Block) childArea);
  131. flush(); // hand off current area to parent
  132. }
  133. /**
  134. * Add the childArea to the current area.
  135. * Called by child LayoutManager when it has filled one of its areas.
  136. * The LM should already have an Area in which to put the child.
  137. * See if the area will fit in the current area.
  138. * If so, add it. Otherwise initiate breaking.
  139. * @param childArea the area to add: will be some block-stacked Area.
  140. */
  141. @Override
  142. public void addChildArea(Area childArea) {
  143. addChildToArea(childArea, getCurrentArea());
  144. }
  145. /**
  146. * Force current area to be added to parent area.
  147. */
  148. protected void flush() {
  149. if (getCurrentArea() != null) {
  150. parentLayoutManager.addChildArea(getCurrentArea());
  151. }
  152. }
  153. /** @return a cached auxiliary Position instance used for things like spaces. */
  154. protected Position getAuxiliaryPosition() {
  155. if (this.auxiliaryPosition == null) {
  156. this.auxiliaryPosition = new NonLeafPosition(this, null);
  157. }
  158. return this.auxiliaryPosition;
  159. }
  160. /**
  161. * @param len length in millipoints to span with bp units
  162. * @return the minimum integer n such that n * bpUnit >= len
  163. */
  164. protected int neededUnits(int len) {
  165. return (int) Math.ceil((float)len / bpUnit);
  166. }
  167. /**
  168. * Determines and sets the content area IPD based on available reference area IPD, start- and
  169. * end-indent properties.
  170. * end-indent is adjusted based on overconstrained geometry rules, if necessary.
  171. *
  172. * @return the resulting content area IPD
  173. */
  174. protected int updateContentAreaIPDwithOverconstrainedAdjust() {
  175. int ipd = referenceIPD - (startIndent + endIndent);
  176. if (ipd < 0) {
  177. //5.3.4, XSL 1.0, Overconstrained Geometry
  178. log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
  179. BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
  180. getFObj().getUserAgent().getEventBroadcaster());
  181. eventProducer.overconstrainedAdjustEndIndent(this,
  182. getFObj().getName(), ipd, getFObj().getLocator());
  183. endIndent += ipd;
  184. ipd = 0;
  185. //TODO Should we skip layout for a block that has ipd=0?
  186. }
  187. setContentAreaIPD(ipd);
  188. return ipd;
  189. }
  190. /**
  191. * Sets the content area IPD by directly supplying the value.
  192. * end-indent is adjusted based on overconstrained geometry rules, if necessary.
  193. * @param contentIPD the IPD of the content
  194. * @return the resulting content area IPD
  195. */
  196. protected int updateContentAreaIPDwithOverconstrainedAdjust(int contentIPD) {
  197. int ipd = referenceIPD - (contentIPD + (startIndent + endIndent));
  198. if (ipd < 0) {
  199. //5.3.4, XSL 1.0, Overconstrained Geometry
  200. log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
  201. BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
  202. getFObj().getUserAgent().getEventBroadcaster());
  203. eventProducer.overconstrainedAdjustEndIndent(this,
  204. getFObj().getName(), ipd, getFObj().getLocator());
  205. endIndent += ipd;
  206. }
  207. setContentAreaIPD(contentIPD);
  208. return contentIPD;
  209. }
  210. /** {@inheritDoc} */
  211. @Override
  212. public List getNextKnuthElements(LayoutContext context, int alignment) {
  213. return getNextKnuthElements(context, alignment, null, null, null);
  214. }
  215. /** {@inheritDoc} */
  216. @Override
  217. public List getNextKnuthElements(LayoutContext context, int alignment,
  218. Stack lmStack, Position restartPosition, LayoutManager restartAtLM) {
  219. referenceIPD = context.getRefIPD();
  220. updateContentAreaIPDwithOverconstrainedAdjust();
  221. boolean isRestart = (lmStack != null);
  222. boolean emptyStack = (!isRestart || lmStack.isEmpty());
  223. List<ListElement> contentList = new LinkedList<ListElement>();
  224. List<ListElement> elements = new LinkedList<ListElement>();
  225. if (!breakBeforeServed(context, elements)) {
  226. // if this FO has break-before specified, and it
  227. // has not yet been processed, return now
  228. return elements;
  229. }
  230. addFirstVisibleMarks(elements, context, alignment);
  231. //Used to indicate a special break-after case when all content has already been generated.
  232. BreakElement forcedBreakAfterLast = null;
  233. LayoutContext childLC;
  234. List<ListElement> childElements;
  235. LayoutManager currentChildLM;
  236. if (isRestart) {
  237. if (emptyStack) {
  238. assert restartAtLM != null && restartAtLM.getParent() == this;
  239. currentChildLM = restartAtLM;
  240. } else {
  241. currentChildLM = (LayoutManager) lmStack.pop();
  242. }
  243. setCurrentChildLM(currentChildLM);
  244. } else {
  245. currentChildLM = getChildLM();
  246. }
  247. while (currentChildLM != null) {
  248. childLC = makeChildLayoutContext(context);
  249. if (!isRestart || emptyStack) {
  250. if (isRestart) {
  251. currentChildLM.reset(); // TODO won't work with forced breaks
  252. }
  253. childElements = getNextChildElements(currentChildLM, context, childLC, alignment,
  254. null, null, null);
  255. } else {
  256. // restart && non-empty LM stack
  257. childElements = getNextChildElements(currentChildLM, context, childLC, alignment,
  258. lmStack, restartPosition, restartAtLM);
  259. // once encountered, irrelevant for following child LMs
  260. emptyStack = true;
  261. }
  262. if (contentList.isEmpty()) {
  263. // propagate keep-with-previous up from the first child
  264. context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
  265. }
  266. // handle non-empty child
  267. if (childElements != null && !childElements.isEmpty()) {
  268. if (!contentList.isEmpty()
  269. && !ElementListUtils.startsWithForcedBreak(childElements)) {
  270. // there is a block handled by prevLM before the one
  271. // handled by curLM, and the one handled
  272. // by the current LM does not begin with a break
  273. addInBetweenBreak(contentList, context, childLC);
  274. }
  275. if (childElements.size() == 1
  276. && ElementListUtils.startsWithForcedBreak(childElements)) {
  277. // a descendant of this block has break-before
  278. if (currentChildLM.isFinished() && !hasNextChildLM()) {
  279. // if there is no more content, make sure pending
  280. // marks are cleared
  281. forcedBreakAfterLast = (BreakElement) childElements.get(0);
  282. context.clearPendingMarks();
  283. // break without adding the child elements
  284. break;
  285. }
  286. if (contentList.isEmpty()) {
  287. // empty fo:block: zero-length box makes sure the IDs and/or markers
  288. // are registered and borders/padding are painted.
  289. elements.add(makeAuxiliaryZeroWidthBox());
  290. }
  291. // add the forced break
  292. contentList.addAll(childElements);
  293. // wrap position and return
  294. wrapPositionElements(contentList, elements);
  295. return elements;
  296. } else {
  297. // add all accumulated child elements
  298. contentList.addAll(childElements);
  299. if (ElementListUtils.endsWithForcedBreak(childElements)) {
  300. // a descendant of this block has break-after
  301. if (currentChildLM.isFinished() && !hasNextChildLM()) {
  302. // if there is no more content, make sure any
  303. // pending marks are cleared
  304. forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
  305. context.clearPendingMarks();
  306. break;
  307. }
  308. //wrap positions and return
  309. wrapPositionElements(contentList, elements);
  310. return elements;
  311. }
  312. }
  313. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  314. }
  315. currentChildLM = getChildLM();
  316. }
  317. if (contentList.isEmpty()) {
  318. if (forcedBreakAfterLast == null) {
  319. // empty fo:block: zero-length box makes sure the IDs and/or markers
  320. // are registered.
  321. elements.add(makeAuxiliaryZeroWidthBox());
  322. }
  323. } else {
  324. // wrap child positions
  325. wrapPositionElements(contentList, elements);
  326. }
  327. addLastVisibleMarks(elements, context, alignment);
  328. if (forcedBreakAfterLast == null) {
  329. addKnuthElementsForBreakAfter(elements, context);
  330. } else {
  331. forcedBreakAfterLast.clearPendingMarks();
  332. elements.add(forcedBreakAfterLast);
  333. }
  334. context.updateKeepWithNextPending(getKeepWithNext());
  335. setFinished(true);
  336. return elements;
  337. }
  338. /**
  339. * Creates and initializes a {@link LayoutContext} to pass to the child LM
  340. * @param context the parent {@link LayoutContext}
  341. * @return a new child layout context
  342. */
  343. protected LayoutContext makeChildLayoutContext(LayoutContext context) {
  344. LayoutContext childLC = LayoutContext.newInstance();
  345. childLC.copyPendingMarksFrom(context);
  346. childLC.setStackLimitBP(context.getStackLimitBP());
  347. childLC.setRefIPD(referenceIPD);
  348. return childLC;
  349. }
  350. /**
  351. * Checks if this LM's first "visible marks" (= borders, padding, spaces) have
  352. * already been processed, and if necessary, adds corresponding elements to
  353. * the specified list, and updates the given layout context accordingly.
  354. * @param elements the element list
  355. * @param context the layout context
  356. * @param alignment the vertical alignment
  357. */
  358. protected void addFirstVisibleMarks(List<ListElement> elements,
  359. LayoutContext context, int alignment) {
  360. if (!firstVisibleMarkServed) {
  361. addKnuthElementsForSpaceBefore(elements, alignment);
  362. context.updateKeepWithPreviousPending(getKeepWithPrevious());
  363. }
  364. addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
  365. firstVisibleMarkServed = true;
  366. //Spaces, border and padding to be repeated at each break
  367. addPendingMarks(context);
  368. }
  369. /**
  370. * Adds elements the LM's last/closing marks to the specified list, and
  371. * updates the layout context accordingly.
  372. * @param elements the element list
  373. * @param context the layout context
  374. * @param alignment the vertical alignment
  375. */
  376. protected void addLastVisibleMarks(List<ListElement> elements,
  377. LayoutContext context, int alignment) {
  378. addKnuthElementsForBorderPaddingAfter(elements, true);
  379. addKnuthElementsForSpaceAfter(elements, alignment);
  380. // All child content processed. Only break-after can occur now, so...
  381. context.clearPendingMarks();
  382. }
  383. /**
  384. * Check whether there is a break-before condition. If so, and
  385. * the specified {@code context} allows it, add the necessary elements
  386. * to the given {@code elements} list.
  387. * @param context the layout context
  388. * @param elements the element list
  389. * @return {@code false} if there is a break-before condition, and it has not been served;
  390. * {@code true} otherwise
  391. */
  392. protected boolean breakBeforeServed(LayoutContext context, List<ListElement> elements) {
  393. if (!breakBeforeServed) {
  394. breakBeforeServed = true;
  395. if (!context.suppressBreakBefore()) {
  396. if (addKnuthElementsForBreakBefore(elements, context)) {
  397. return false;
  398. }
  399. }
  400. }
  401. return breakBeforeServed;
  402. }
  403. private KnuthBox makeZeroWidthBox() {
  404. return new KnuthBox(0, new NonLeafPosition(this, null), false);
  405. }
  406. private KnuthBox makeAuxiliaryZeroWidthBox() {
  407. return new KnuthBox(0, notifyPos(new Position(this)), true);
  408. }
  409. private KnuthPenalty makeZeroWidthPenalty(int penaltyValue) {
  410. return new KnuthPenalty(0, penaltyValue, false, new NonLeafPosition(this, null), false);
  411. }
  412. private KnuthGlue makeSpaceAdjustmentGlue(int width, Adjustment adjustmentClass,
  413. boolean isAuxiliary) {
  414. return new KnuthGlue(width, 0, 0,
  415. adjustmentClass,
  416. new NonLeafPosition(this, null),
  417. isAuxiliary);
  418. }
  419. /**
  420. * Gets the next set of child elements for the given childLM.
  421. * The default implementation basically copies the pending marks to the child layout context,
  422. * and subsequently calls the appropriate variant of {@code childLM.getNextKnuthElements()},
  423. * passing it all relevant parameters.
  424. * @param childLM the current child LM
  425. * @param context the layout context
  426. * @param childLC the child layout context
  427. * @param alignment the vertical alignment
  428. * @param lmStack the stack of currently active LMs (if any)
  429. * @param restartPosition the position to restart from (if any)
  430. * @param restartAtLM the LM to restart from (if any)
  431. * @return list of elements corresponding to the content generated by childLM
  432. */
  433. protected List<ListElement> getNextChildElements(LayoutManager childLM, LayoutContext context,
  434. LayoutContext childLC, int alignment, Stack<LayoutManager> lmStack,
  435. Position restartPosition, LayoutManager restartAtLM) {
  436. if (childLM == this.childLMs.get(0)) {
  437. childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
  438. //Handled already by the parent (break collapsing, see above)
  439. }
  440. if (lmStack == null) {
  441. // route to default implementation, in case childLM does not provide
  442. // an override similar to this class
  443. return childLM.getNextKnuthElements(childLC, alignment);
  444. } else {
  445. return childLM.getNextKnuthElements(childLC, alignment, lmStack,
  446. restartPosition, restartAtLM);
  447. }
  448. }
  449. /**
  450. * Adds a break element to the content list between individual child elements.
  451. * @param contentList the content list
  452. * @param parentLC the parent layout context
  453. * @param childLC the currently active child layout context
  454. */
  455. protected void addInBetweenBreak(List<ListElement> contentList, LayoutContext parentLC,
  456. LayoutContext childLC) {
  457. if (mustKeepTogether()
  458. || parentLC.isKeepWithNextPending()
  459. || childLC.isKeepWithPreviousPending()) {
  460. Keep keep = getKeepTogether();
  461. //Handle pending keep-with-next
  462. keep = keep.compare(parentLC.getKeepWithNextPending());
  463. parentLC.clearKeepWithNextPending();
  464. //Handle pending keep-with-previous from child LM
  465. keep = keep.compare(childLC.getKeepWithPreviousPending());
  466. childLC.clearKeepWithPreviousPending();
  467. // add a penalty to forbid or discourage a break between blocks
  468. contentList.add(new BreakElement(
  469. new Position(this), keep.getPenalty(),
  470. keep.getContext(), parentLC));
  471. return;
  472. }
  473. ListElement last = ListUtil.getLast(contentList);
  474. if (last.isGlue()) {
  475. // the last element in contentList is a glue;
  476. // it is a feasible breakpoint, there is no need to add
  477. // a penalty
  478. log.warn("glue-type break possibility not handled properly, yet");
  479. //TODO Does this happen? If yes, need to deal with border and padding
  480. //at the break possibility
  481. } else if (!ElementListUtils.endsWithNonInfinitePenalty(contentList)) {
  482. // TODO vh: this is hacky
  483. // The getNextKnuthElements method of TableCellLM must not be called
  484. // twice, otherwise some settings like indents or borders will be
  485. // counted several times and lead to a wrong output. Anyway the
  486. // getNextKnuthElements methods should be called only once eventually
  487. // (i.e., when multi-threading the code), even when there are forced
  488. // breaks.
  489. // If we add a break possibility after a forced break the
  490. // AreaAdditionUtil.addAreas method will act on a sequence starting
  491. // with a SpaceResolver.SpaceHandlingBreakPosition element, having no
  492. // LM associated to it. Thus it will stop early instead of adding
  493. // areas for following Positions. The above test aims at preventing
  494. // such a situation from occurring. add a null penalty to allow a break
  495. // between blocks
  496. // add a null penalty to allow a break between blocks
  497. contentList.add(new BreakElement(
  498. new Position(this), 0, Constants.EN_AUTO, parentLC));
  499. }
  500. }
  501. /** {@inheritDoc} */
  502. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  503. assert (lastElement != null && lastElement.getPosition() != null);
  504. Position innerPosition = lastElement.getPosition().getPosition();
  505. if (innerPosition == null && lastElement.isGlue()) {
  506. // this adjustment applies to space-before or space-after of this block
  507. if (((KnuthGlue) lastElement).getAdjustmentClass()
  508. == Adjustment.SPACE_BEFORE_ADJUSTMENT) {
  509. // this adjustment applies to space-before
  510. adjustedSpaceBefore += adj;
  511. } else {
  512. // this adjustment applies to space-after
  513. adjustedSpaceAfter += adj;
  514. }
  515. return adj;
  516. } else if (innerPosition instanceof MappingPosition) {
  517. // this block has block-progression-unit > 0: the adjustment can concern
  518. // - the space-before or space-after of this block,
  519. // - the line number of a descendant of this block
  520. MappingPosition mappingPos = (MappingPosition)innerPosition;
  521. if (lastElement.isGlue()) {
  522. // lastElement is a glue
  523. ListIterator storedListIterator = storedList.listIterator(
  524. mappingPos.getFirstIndex());
  525. int newAdjustment = 0;
  526. while (storedListIterator.nextIndex() <= mappingPos.getLastIndex()) {
  527. KnuthElement storedElement = (KnuthElement)storedListIterator.next();
  528. if (storedElement.isGlue()) {
  529. newAdjustment += ((BlockLevelLayoutManager)storedElement
  530. .getLayoutManager()).negotiateBPDAdjustment(
  531. adj - newAdjustment, storedElement);
  532. }
  533. }
  534. newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
  535. : -bpUnit * neededUnits(-newAdjustment));
  536. return newAdjustment;
  537. } else {
  538. // lastElement is a penalty: this means that the paragraph
  539. // has been split between consecutive pages:
  540. // this may involve a change in the number of lines
  541. KnuthPenalty storedPenalty = (KnuthPenalty)
  542. storedList.get(mappingPos.getLastIndex());
  543. if (storedPenalty.getWidth() > 0) {
  544. // the original penalty has width > 0
  545. return ((BlockLevelLayoutManager)storedPenalty.getLayoutManager())
  546. .negotiateBPDAdjustment(storedPenalty.getWidth(),
  547. storedPenalty);
  548. } else {
  549. // the original penalty has width = 0
  550. // the adjustment involves only the spaces before and after
  551. return adj;
  552. }
  553. }
  554. } else if (innerPosition != null && innerPosition.getLM() != this) {
  555. // this adjustment concerns another LM
  556. NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
  557. lastElement.setPosition(innerPosition);
  558. int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
  559. .negotiateBPDAdjustment(adj, lastElement);
  560. lastElement.setPosition(savedPos);
  561. return returnValue;
  562. } else {
  563. // this should never happen
  564. log.error("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
  565. return 0;
  566. }
  567. }
  568. /** {@inheritDoc} */
  569. public void discardSpace(KnuthGlue spaceGlue) {
  570. assert (spaceGlue != null && spaceGlue.getPosition() != null);
  571. Position innerPosition = spaceGlue.getPosition().getPosition();
  572. if (innerPosition == null || innerPosition.getLM() == this) {
  573. // if this block has block-progression-unit > 0, innerPosition can be
  574. // a MappingPosition
  575. // spaceGlue represents space before or space after of this block
  576. if (spaceGlue.getAdjustmentClass() == Adjustment.SPACE_BEFORE_ADJUSTMENT) {
  577. // space-before must be discarded
  578. adjustedSpaceBefore = 0;
  579. foSpaceBefore = MinOptMax.ZERO;
  580. } else {
  581. // space-after must be discarded
  582. adjustedSpaceAfter = 0;
  583. foSpaceAfter = MinOptMax.ZERO;
  584. //TODO Why are both cases handled in the same way?
  585. }
  586. } else {
  587. // this element was not created by this BlockLM
  588. NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
  589. spaceGlue.setPosition(innerPosition);
  590. ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
  591. spaceGlue.setPosition(savedPos);
  592. }
  593. }
  594. /** {@inheritDoc} */
  595. @Override
  596. public List getChangedKnuthElements(List oldList, int alignment) {
  597. ListIterator<KnuthElement> oldListIterator = oldList.listIterator();
  598. KnuthElement currElement = null;
  599. KnuthElement prevElement = null;
  600. List<KnuthElement> returnedList = new LinkedList<KnuthElement>();
  601. List<KnuthElement> returnList = new LinkedList<KnuthElement>();
  602. int fromIndex = 0;
  603. // "unwrap" the Positions stored in the elements
  604. KnuthElement oldElement;
  605. while (oldListIterator.hasNext()) {
  606. oldElement = oldListIterator.next();
  607. assert oldElement.getPosition() != null;
  608. Position innerPosition = oldElement.getPosition().getPosition();
  609. if (innerPosition != null) {
  610. // oldElement was created by a descendant
  611. oldElement.setPosition(innerPosition);
  612. } else {
  613. // oldElement was created by this LM:
  614. // modify its position in order to recognize it was not created
  615. // by a child
  616. oldElement.setPosition(new Position(this));
  617. }
  618. }
  619. // create the iterator
  620. ListIterator<KnuthElement> workListIterator = oldList.listIterator();
  621. while (workListIterator.hasNext()) {
  622. currElement = workListIterator.next();
  623. if (prevElement != null
  624. && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
  625. // prevElement is the last element generated by the same LM
  626. BlockLevelLayoutManager prevLM
  627. = (BlockLevelLayoutManager)prevElement.getLayoutManager();
  628. BlockLevelLayoutManager currLM
  629. = (BlockLevelLayoutManager)currElement.getLayoutManager();
  630. boolean somethingAdded = false;
  631. if (prevLM != this) {
  632. returnedList.addAll(
  633. prevLM.getChangedKnuthElements(
  634. oldList.subList(fromIndex, workListIterator.previousIndex()),
  635. alignment));
  636. somethingAdded = true;
  637. } else {
  638. // do nothing
  639. }
  640. fromIndex = workListIterator.previousIndex();
  641. /*
  642. * TODO: why are KnuthPenalties added here,
  643. * while in getNextKE they were changed to BreakElements?
  644. */
  645. // there is another block after this one
  646. if (somethingAdded
  647. && (this.mustKeepTogether()
  648. || prevLM.mustKeepWithNext()
  649. || currLM.mustKeepWithPrevious())) {
  650. // add an infinite penalty to forbid a break between blocks
  651. returnedList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
  652. } else if (somethingAdded
  653. && !ListUtil.getLast(returnedList).isGlue()) {
  654. // add a null penalty to allow a break between blocks
  655. returnedList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
  656. }
  657. }
  658. prevElement = currElement;
  659. }
  660. if (currElement != null) {
  661. LayoutManager currLM = currElement.getLayoutManager();
  662. if (currLM != this) {
  663. returnedList.addAll(currLM.getChangedKnuthElements(
  664. oldList.subList(fromIndex, oldList.size()), alignment));
  665. } else {
  666. // there are no more elements to add
  667. // remove the last penalty added to returnedList
  668. if (!returnedList.isEmpty()) {
  669. ListUtil.removeLast(returnedList);
  670. }
  671. }
  672. }
  673. // append elements representing space-before
  674. boolean spaceBeforeIsConditional = true;
  675. if (fobj instanceof org.apache.fop.fo.flow.Block) {
  676. spaceBeforeIsConditional = getSpaceBeforeProperty().isDiscard();
  677. }
  678. if (adjustedSpaceBefore != 0) {
  679. if (!spaceBeforeIsConditional) {
  680. // add elements to prevent the glue to be discarded
  681. returnList.add(makeZeroWidthBox());
  682. returnList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
  683. }
  684. returnList.add(makeSpaceAdjustmentGlue(adjustedSpaceBefore,
  685. Adjustment.SPACE_BEFORE_ADJUSTMENT,
  686. false));
  687. }
  688. // "wrap" the Position stored in each element of returnedList
  689. // and add elements to returnList
  690. for (KnuthElement el : returnedList) {
  691. el.setPosition(new NonLeafPosition(this, el.getPosition()));
  692. returnList.add(el);
  693. }
  694. // append elements representing space-after
  695. boolean spaceAfterIsConditional = true;
  696. if (fobj instanceof org.apache.fop.fo.flow.Block) {
  697. spaceAfterIsConditional = getSpaceAfterProperty().isDiscard();
  698. }
  699. if (adjustedSpaceAfter != 0) {
  700. if (!spaceAfterIsConditional) {
  701. returnList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
  702. }
  703. returnList.add(makeSpaceAdjustmentGlue(adjustedSpaceAfter,
  704. Adjustment.SPACE_AFTER_ADJUSTMENT,
  705. spaceAfterIsConditional));
  706. if (!spaceAfterIsConditional) {
  707. returnList.add(makeZeroWidthBox());
  708. }
  709. }
  710. return returnList;
  711. }
  712. /**
  713. * Retrieves and returns the keep-together strength from the parent element.
  714. * @return the keep-together strength
  715. */
  716. protected Keep getParentKeepTogether() {
  717. Keep keep = Keep.KEEP_AUTO;
  718. if (getParent() instanceof BlockLevelLayoutManager) {
  719. keep = ((BlockLevelLayoutManager)getParent()).getKeepTogether();
  720. } else if (getParent() instanceof InlineLayoutManager) {
  721. if (((InlineLayoutManager) getParent()).mustKeepTogether()) {
  722. keep = Keep.KEEP_ALWAYS;
  723. }
  724. //TODO Fix me
  725. //strength = ((InlineLayoutManager) getParent()).getKeepTogetherStrength();
  726. }
  727. return keep;
  728. }
  729. /** {@inheritDoc} */
  730. public boolean mustKeepTogether() {
  731. return !getKeepTogether().isAuto();
  732. }
  733. /** {@inheritDoc} */
  734. public boolean mustKeepWithPrevious() {
  735. return !getKeepWithPrevious().isAuto();
  736. }
  737. /** {@inheritDoc} */
  738. public boolean mustKeepWithNext() {
  739. return !getKeepWithNext().isAuto();
  740. }
  741. /** {@inheritDoc} */
  742. public Keep getKeepTogether() {
  743. Keep keep = Keep.getKeep(getKeepTogetherProperty());
  744. keep = keep.compare(getParentKeepTogether());
  745. return keep;
  746. }
  747. /** {@inheritDoc} */
  748. public Keep getKeepWithPrevious() {
  749. return Keep.getKeep(getKeepWithPreviousProperty());
  750. }
  751. /** {@inheritDoc} */
  752. public Keep getKeepWithNext() {
  753. return Keep.getKeep(getKeepWithNextProperty());
  754. }
  755. /**
  756. * {@inheritDoc}
  757. * Default implementation throws a {@link IllegalStateException}.
  758. * Must be implemented by the subclass, if applicable.
  759. */
  760. public KeepProperty getKeepTogetherProperty() {
  761. throw new IllegalStateException();
  762. }
  763. /**
  764. * {@inheritDoc}
  765. * Default implementation throws a {@link IllegalStateException}.
  766. * Must be implemented by the subclass, if applicable.
  767. */
  768. public KeepProperty getKeepWithPreviousProperty() {
  769. throw new IllegalStateException();
  770. }
  771. /**
  772. * {@inheritDoc}
  773. * Default implementation throws a {@link IllegalStateException}.
  774. * Must be implemented by the subclass, if applicable.
  775. */
  776. public KeepProperty getKeepWithNextProperty() {
  777. throw new IllegalStateException();
  778. }
  779. /**
  780. * Adds the unresolved elements for border and padding to a layout context so break
  781. * possibilities can be properly constructed.
  782. * @param context the layout context
  783. */
  784. protected void addPendingMarks(LayoutContext context) {
  785. CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
  786. if (borderAndPadding != null) {
  787. if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
  788. context.addPendingBeforeMark(new BorderElement(
  789. getAuxiliaryPosition(),
  790. borderAndPadding.getBorderInfo(
  791. CommonBorderPaddingBackground.BEFORE).getWidth(),
  792. RelSide.BEFORE,
  793. false, false, this));
  794. }
  795. if (borderAndPadding.getPaddingBefore(false, this) > 0) {
  796. context.addPendingBeforeMark(new PaddingElement(
  797. getAuxiliaryPosition(),
  798. borderAndPadding.getPaddingLengthProperty(
  799. CommonBorderPaddingBackground.BEFORE),
  800. RelSide.BEFORE,
  801. false, false, this));
  802. }
  803. if (borderAndPadding.getBorderAfterWidth(false) > 0) {
  804. context.addPendingAfterMark(new BorderElement(
  805. getAuxiliaryPosition(),
  806. borderAndPadding.getBorderInfo(
  807. CommonBorderPaddingBackground.AFTER).getWidth(),
  808. RelSide.AFTER,
  809. false, false, this));
  810. }
  811. if (borderAndPadding.getPaddingAfter(false, this) > 0) {
  812. context.addPendingAfterMark(new PaddingElement(
  813. getAuxiliaryPosition(),
  814. borderAndPadding.getPaddingLengthProperty(
  815. CommonBorderPaddingBackground.AFTER),
  816. RelSide.AFTER,
  817. false, false, this));
  818. }
  819. }
  820. }
  821. /** @return the border, padding and background info structure */
  822. private CommonBorderPaddingBackground getBorderPaddingBackground() {
  823. if (fobj instanceof org.apache.fop.fo.flow.Block) {
  824. return ((org.apache.fop.fo.flow.Block)fobj)
  825. .getCommonBorderPaddingBackground();
  826. } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
  827. return ((org.apache.fop.fo.flow.BlockContainer)fobj)
  828. .getCommonBorderPaddingBackground();
  829. } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
  830. return ((org.apache.fop.fo.flow.ListBlock)fobj)
  831. .getCommonBorderPaddingBackground();
  832. } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
  833. return ((org.apache.fop.fo.flow.ListItem)fobj)
  834. .getCommonBorderPaddingBackground();
  835. } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
  836. return ((org.apache.fop.fo.flow.table.Table)fobj)
  837. .getCommonBorderPaddingBackground();
  838. } else {
  839. return null;
  840. }
  841. }
  842. /** @return the space-before property */
  843. protected SpaceProperty getSpaceBeforeProperty() {
  844. if (fobj instanceof org.apache.fop.fo.flow.Block) {
  845. return ((org.apache.fop.fo.flow.Block)fobj)
  846. .getCommonMarginBlock().spaceBefore;
  847. } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
  848. return ((org.apache.fop.fo.flow.BlockContainer)fobj)
  849. .getCommonMarginBlock().spaceBefore;
  850. } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
  851. return ((org.apache.fop.fo.flow.ListBlock)fobj)
  852. .getCommonMarginBlock().spaceBefore;
  853. } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
  854. return ((org.apache.fop.fo.flow.ListItem)fobj)
  855. .getCommonMarginBlock().spaceBefore;
  856. } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
  857. return ((org.apache.fop.fo.flow.table.Table)fobj)
  858. .getCommonMarginBlock().spaceBefore;
  859. } else {
  860. return null;
  861. }
  862. }
  863. /** @return the space-after property */
  864. protected SpaceProperty getSpaceAfterProperty() {
  865. if (fobj instanceof org.apache.fop.fo.flow.Block) {
  866. return ((org.apache.fop.fo.flow.Block)fobj)
  867. .getCommonMarginBlock().spaceAfter;
  868. } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
  869. return ((org.apache.fop.fo.flow.BlockContainer)fobj)
  870. .getCommonMarginBlock().spaceAfter;
  871. } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
  872. return ((org.apache.fop.fo.flow.ListBlock)fobj)
  873. .getCommonMarginBlock().spaceAfter;
  874. } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
  875. return ((org.apache.fop.fo.flow.ListItem)fobj)
  876. .getCommonMarginBlock().spaceAfter;
  877. } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
  878. return ((org.apache.fop.fo.flow.table.Table)fobj)
  879. .getCommonMarginBlock().spaceAfter;
  880. } else {
  881. return null;
  882. }
  883. }
  884. /**
  885. * Creates Knuth elements for before border padding and adds them to the return list.
  886. * @param returnList return list to add the additional elements to
  887. * @param isFirst true if this is the first time a layout manager instance needs to generate
  888. * border and padding
  889. */
  890. protected void addKnuthElementsForBorderPaddingBefore(List returnList, boolean isFirst) {
  891. //Border and Padding (before)
  892. CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
  893. if (borderAndPadding != null) {
  894. if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
  895. returnList.add(new BorderElement(
  896. getAuxiliaryPosition(),
  897. borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.BEFORE)
  898. .getWidth(),
  899. RelSide.BEFORE, isFirst, false, this));
  900. }
  901. if (borderAndPadding.getPaddingBefore(false, this) > 0) {
  902. returnList.add(new PaddingElement(
  903. getAuxiliaryPosition(),
  904. borderAndPadding.getPaddingLengthProperty(
  905. CommonBorderPaddingBackground.BEFORE),
  906. RelSide.BEFORE, isFirst, false, this));
  907. }
  908. }
  909. }
  910. /**
  911. * Creates Knuth elements for after border padding and adds them to the return list.
  912. * @param returnList return list to add the additional elements to
  913. * @param isLast true if this is the last time a layout manager instance needs to generate
  914. * border and padding
  915. */
  916. protected void addKnuthElementsForBorderPaddingAfter(List returnList, boolean isLast) {
  917. //Border and Padding (after)
  918. CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
  919. if (borderAndPadding != null) {
  920. if (borderAndPadding.getPaddingAfter(false, this) > 0) {
  921. returnList.add(new PaddingElement(
  922. getAuxiliaryPosition(),
  923. borderAndPadding.getPaddingLengthProperty(
  924. CommonBorderPaddingBackground.AFTER),
  925. RelSide.AFTER, false, isLast, this));
  926. }
  927. if (borderAndPadding.getBorderAfterWidth(false) > 0) {
  928. returnList.add(new BorderElement(
  929. getAuxiliaryPosition(),
  930. borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.AFTER)
  931. .getWidth(),
  932. RelSide.AFTER, false, isLast, this));
  933. }
  934. }
  935. }
  936. /**
  937. * Creates Knuth elements for break-before and adds them to the return list.
  938. * @param returnList return list to add the additional elements to
  939. * @param context the layout context
  940. * @return true if an element has been added due to a break-before.
  941. */
  942. protected boolean addKnuthElementsForBreakBefore(List returnList, LayoutContext context) {
  943. int breakBefore = getBreakBefore();
  944. if (breakBefore == EN_PAGE
  945. || breakBefore == EN_COLUMN
  946. || breakBefore == EN_EVEN_PAGE
  947. || breakBefore == EN_ODD_PAGE) {
  948. // return a penalty element, representing a forced page break
  949. returnList.add(new BreakElement(getAuxiliaryPosition(),
  950. 0, -KnuthElement.INFINITE, breakBefore, context));
  951. return true;
  952. } else {
  953. return false;
  954. }
  955. }
  956. /**
  957. * Returns the highest priority break-before value on this layout manager or its
  958. * relevant descendants.
  959. *
  960. * @return the break-before value (Constants.EN_*)
  961. * @see BreakOpportunity#getBreakBefore()
  962. */
  963. public int getBreakBefore() {
  964. return BreakOpportunityHelper.getBreakBefore(this);
  965. }
  966. /**
  967. * Creates Knuth elements for break-after and adds them to the return list.
  968. * @param returnList return list to add the additional elements to
  969. * @param context the layout context
  970. * @return true if an element has been added due to a break-after.
  971. */
  972. protected boolean addKnuthElementsForBreakAfter(List returnList, LayoutContext context) {
  973. int breakAfter = -1;
  974. if (fobj instanceof BreakPropertySet) {
  975. breakAfter = ((BreakPropertySet)fobj).getBreakAfter();
  976. }
  977. if (breakAfter == EN_PAGE
  978. || breakAfter == EN_COLUMN
  979. || breakAfter == EN_EVEN_PAGE
  980. || breakAfter == EN_ODD_PAGE) {
  981. // add a penalty element, representing a forced page break
  982. returnList.add(new BreakElement(getAuxiliaryPosition(),
  983. 0, -KnuthElement.INFINITE, breakAfter, context));
  984. return true;
  985. } else {
  986. return false;
  987. }
  988. }
  989. /**
  990. * Creates Knuth elements for space-before and adds them to the return list.
  991. * @param returnList return list to add the additional elements to
  992. * @param alignment vertical alignment
  993. */
  994. protected void addKnuthElementsForSpaceBefore(List returnList, int alignment) {
  995. SpaceProperty spaceBefore = getSpaceBeforeProperty();
  996. // append elements representing space-before
  997. if (spaceBefore != null
  998. && !(spaceBefore.getMinimum(this).getLength().getValue(this) == 0
  999. && spaceBefore.getMaximum(this).getLength().getValue(this) == 0)) {
  1000. returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceBefore,
  1001. RelSide.BEFORE,
  1002. true, false, this));
  1003. }
  1004. }
  1005. /**
  1006. * Creates Knuth elements for space-after and adds them to the return list.
  1007. * @param returnList return list to add the additional elements to
  1008. * @param alignment vertical alignment
  1009. */
  1010. protected void addKnuthElementsForSpaceAfter(List returnList, int alignment) {
  1011. SpaceProperty spaceAfter = getSpaceAfterProperty();
  1012. // append elements representing space-after
  1013. if (spaceAfter != null
  1014. && !(spaceAfter.getMinimum(this).getLength().getValue(this) == 0
  1015. && spaceAfter.getMaximum(this).getLength().getValue(this) == 0)) {
  1016. returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceAfter,
  1017. RelSide.AFTER,
  1018. false, true, this));
  1019. }
  1020. }
  1021. /** A mapping position. */
  1022. protected static class MappingPosition extends Position {
  1023. private int firstIndex;
  1024. private int lastIndex;
  1025. /**
  1026. * Construct mapping position.
  1027. * @param lm layout manager
  1028. * @param first position
  1029. * @param last position
  1030. */
  1031. public MappingPosition(LayoutManager lm, int first, int last) {
  1032. super(lm);
  1033. firstIndex = first;
  1034. lastIndex = last;
  1035. }
  1036. /** @return first index */
  1037. public int getFirstIndex() {
  1038. return firstIndex;
  1039. }
  1040. /** @return last index */
  1041. public int getLastIndex() {
  1042. return lastIndex;
  1043. }
  1044. }
  1045. /**
  1046. * "wrap" the Position inside each element moving the elements from
  1047. * SourceList to targetList
  1048. * @param sourceList source list
  1049. * @param targetList target list receiving the wrapped position elements
  1050. */
  1051. protected void wrapPositionElements(List sourceList, List targetList) {
  1052. wrapPositionElements(sourceList, targetList, false);
  1053. }
  1054. /**
  1055. * "wrap" the Position inside each element moving the elements from
  1056. * SourceList to targetList
  1057. * @param sourceList source list
  1058. * @param targetList target list receiving the wrapped position elements
  1059. * @param force if true, every Position is wrapped regardless of its LM of origin
  1060. */
  1061. protected void wrapPositionElements(List sourceList, List targetList, boolean force) {
  1062. ListIterator listIter = sourceList.listIterator();
  1063. Object tempElement;
  1064. while (listIter.hasNext()) {
  1065. tempElement = listIter.next();
  1066. if (tempElement instanceof ListElement) {
  1067. wrapPositionElement(
  1068. (ListElement) tempElement,
  1069. targetList,
  1070. force);
  1071. } else if (tempElement instanceof List) {
  1072. wrapPositionElements(
  1073. (List) tempElement,
  1074. targetList,
  1075. force);
  1076. }
  1077. }
  1078. }
  1079. /**
  1080. * "wrap" the Position inside the given element and add it to the target list.
  1081. * @param el the list element
  1082. * @param targetList target list receiving the wrapped position elements
  1083. * @param force if true, every Position is wrapped regardless of its LM of origin
  1084. */
  1085. protected void wrapPositionElement(ListElement el, List targetList, boolean force) {
  1086. if (force || el.getLayoutManager() != this) {
  1087. el.setPosition(notifyPos(new NonLeafPosition(this, el.getPosition())));
  1088. }
  1089. targetList.add(el);
  1090. }
  1091. /** @return the sum of start-indent and end-indent */
  1092. protected int getIPIndents() {
  1093. return startIndent + endIndent;
  1094. }
  1095. /**
  1096. * Returns the IPD of the content area
  1097. * @return the IPD of the content area
  1098. */
  1099. @Override
  1100. public int getContentAreaIPD() {
  1101. return contentAreaIPD;
  1102. }
  1103. /**
  1104. * Sets the IPD of the content area
  1105. * @param contentAreaIPD the IPD of the content area
  1106. */
  1107. protected void setContentAreaIPD(int contentAreaIPD) {
  1108. this.contentAreaIPD = contentAreaIPD;
  1109. }
  1110. /**
  1111. * Returns the BPD of the content area
  1112. * @return the BPD of the content area
  1113. */
  1114. @Override
  1115. public int getContentAreaBPD() {
  1116. return -1;
  1117. }
  1118. /** {@inheritDoc} */
  1119. @Override
  1120. public void reset() {
  1121. super.reset();
  1122. breakBeforeServed = false;
  1123. firstVisibleMarkServed = false;
  1124. // TODO startIndent, endIndent
  1125. }
  1126. /**
  1127. * Whether this LM can handle horizontal overflow error messages (only a BlockContainerLayoutManager can).
  1128. * @param milliPoints horizontal overflow
  1129. * @return true if handled by a BlockContainerLayoutManager
  1130. */
  1131. public boolean handleOverflow(int milliPoints) {
  1132. if (getParent() instanceof BlockStackingLayoutManager) {
  1133. return ((BlockStackingLayoutManager) getParent()).handleOverflow(milliPoints);
  1134. } else if (getParent() instanceof InlineContainerLayoutManager) {
  1135. return ((InlineContainerLayoutManager) getParent()).handleOverflow(milliPoints);
  1136. }
  1137. return false;
  1138. }
  1139. }