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

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