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

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