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

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