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.

BlockContainerLayoutManager.java 45KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. /*
  2. * Copyright 1999-2006 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.layoutmgr;
  18. import java.util.LinkedList;
  19. import java.util.List;
  20. import java.util.ListIterator;
  21. import java.awt.Point;
  22. import java.awt.geom.Rectangle2D;
  23. import org.apache.fop.area.Area;
  24. import org.apache.fop.area.BlockViewport;
  25. import org.apache.fop.area.Block;
  26. import org.apache.fop.area.Trait;
  27. import org.apache.fop.fo.FONode;
  28. import org.apache.fop.fo.flow.BlockContainer;
  29. import org.apache.fop.fo.properties.CommonAbsolutePosition;
  30. import org.apache.fop.area.CTM;
  31. import org.apache.fop.datatypes.FODimension;
  32. import org.apache.fop.datatypes.Length;
  33. import org.apache.fop.traits.MinOptMax;
  34. import org.apache.fop.traits.SpaceVal;
  35. /**
  36. * LayoutManager for a block-container FO.
  37. */
  38. public class BlockContainerLayoutManager extends BlockStackingLayoutManager
  39. implements ConditionalElementListener {
  40. private BlockViewport viewportBlockArea;
  41. private Block referenceArea;
  42. private CommonAbsolutePosition abProps;
  43. private FODimension relDims;
  44. private CTM absoluteCTM;
  45. private Length width;
  46. private Length height;
  47. //private int vpContentIPD;
  48. private int vpContentBPD;
  49. // When viewport should grow with the content.
  50. private boolean autoHeight = true;
  51. /* holds the (one-time use) fo:block space-before
  52. and -after properties. Large fo:blocks are split
  53. into multiple Area.Blocks to accomodate the subsequent
  54. regions (pages) they are placed on. space-before
  55. is applied at the beginning of the first
  56. Block and space-after at the end of the last Block
  57. used in rendering the fo:block.
  58. */
  59. //TODO space-before|after: handle space-resolution rules
  60. private MinOptMax foBlockSpaceBefore;
  61. private MinOptMax foBlockSpaceAfter;
  62. private boolean discardBorderBefore;
  63. private boolean discardBorderAfter;
  64. private boolean discardPaddingBefore;
  65. private boolean discardPaddingAfter;
  66. private MinOptMax effSpaceBefore;
  67. private MinOptMax effSpaceAfter;
  68. /**
  69. * Create a new block container layout manager.
  70. * @param node block-container node to create the layout manager for.
  71. */
  72. public BlockContainerLayoutManager(BlockContainer node) {
  73. super(node);
  74. }
  75. /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
  76. public void initialize() {
  77. abProps = getBlockContainerFO().getCommonAbsolutePosition();
  78. foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock()
  79. .spaceBefore, this).getSpace();
  80. foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock()
  81. .spaceAfter, this).getSpace();
  82. startIndent = getBlockContainerFO().getCommonMarginBlock().startIndent.getValue(this);
  83. endIndent = getBlockContainerFO().getCommonMarginBlock().endIndent.getValue(this);
  84. boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
  85. if (rotated) {
  86. height = getBlockContainerFO().getInlineProgressionDimension()
  87. .getOptimum(this).getLength();
  88. width = getBlockContainerFO().getBlockProgressionDimension()
  89. .getOptimum(this).getLength();
  90. } else {
  91. height = getBlockContainerFO().getBlockProgressionDimension()
  92. .getOptimum(this).getLength();
  93. width = getBlockContainerFO().getInlineProgressionDimension()
  94. .getOptimum(this).getLength();
  95. }
  96. bpUnit = 0; //layoutProps.blockProgressionUnit;
  97. if (bpUnit == 0) {
  98. // use optimum space values
  99. adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock()
  100. .spaceBefore.getSpace().getOptimum(this).getLength().getValue(this);
  101. adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock()
  102. .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this);
  103. } else {
  104. // use minimum space values
  105. adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock()
  106. .spaceBefore.getSpace().getMinimum(this).getLength().getValue(this);
  107. adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock()
  108. .spaceAfter.getSpace().getMinimum(this).getLength().getValue(this);
  109. }
  110. }
  111. private void resetSpaces() {
  112. this.discardBorderBefore = false;
  113. this.discardBorderAfter = false;
  114. this.discardPaddingBefore = false;
  115. this.discardPaddingAfter = false;
  116. this.effSpaceBefore = null;
  117. this.effSpaceAfter = null;
  118. }
  119. /** @return the content IPD */
  120. protected int getRotatedIPD() {
  121. return getBlockContainerFO().getInlineProgressionDimension()
  122. .getOptimum(this).getLength().getValue(this);
  123. }
  124. private boolean needClip() {
  125. int overflow = getBlockContainerFO().getOverflow();
  126. return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW);
  127. }
  128. private int getSpaceBefore() {
  129. return foBlockSpaceBefore.opt;
  130. }
  131. private int getBPIndents() {
  132. int indents = 0;
  133. /* TODO This is wrong isn't it?
  134. indents += getBlockContainerFO().getCommonMarginBlock()
  135. .spaceBefore.getOptimum(this).getLength().getValue(this);
  136. indents += getBlockContainerFO().getCommonMarginBlock()
  137. .spaceAfter.getOptimum(this).getLength().getValue(this);
  138. */
  139. indents += getBlockContainerFO().getCommonBorderPaddingBackground()
  140. .getBPPaddingAndBorder(false, this);
  141. return indents;
  142. }
  143. private boolean isAbsoluteOrFixed() {
  144. return (abProps.absolutePosition == EN_ABSOLUTE)
  145. || (abProps.absolutePosition == EN_FIXED);
  146. }
  147. private boolean isFixed() {
  148. return (abProps.absolutePosition == EN_FIXED);
  149. }
  150. /** @see org.apache.fop.layoutmgr.LayoutManager#getContentAreaBPD() */
  151. public int getContentAreaBPD() {
  152. if (autoHeight) {
  153. return -1;
  154. } else {
  155. return this.vpContentBPD;
  156. }
  157. }
  158. /** @see org.apache.fop.layoutmgr.LayoutManager */
  159. public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  160. resetSpaces();
  161. if (isAbsoluteOrFixed()) {
  162. return getNextKnuthElementsAbsolute(context, alignment);
  163. }
  164. autoHeight = false;
  165. //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
  166. int maxbpd = context.getStackLimit().opt;
  167. int allocBPD;
  168. if (height.getEnum() == EN_AUTO
  169. || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
  170. //auto height when height="auto" or "if that dimension is not specified explicitly
  171. //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1)
  172. allocBPD = maxbpd;
  173. autoHeight = true;
  174. } else {
  175. allocBPD = height.getValue(this); //this is the content-height
  176. allocBPD += getBPIndents();
  177. }
  178. vpContentBPD = allocBPD - getBPIndents();
  179. referenceIPD = context.getRefIPD();
  180. if (width.getEnum() == EN_AUTO) {
  181. updateContentAreaIPDwithOverconstrainedAdjust();
  182. } else {
  183. int contentWidth = width.getValue(this);
  184. updateContentAreaIPDwithOverconstrainedAdjust(contentWidth);
  185. }
  186. double contentRectOffsetX = 0;
  187. contentRectOffsetX += getBlockContainerFO()
  188. .getCommonMarginBlock().startIndent.getValue(this);
  189. double contentRectOffsetY = 0;
  190. contentRectOffsetY += getBlockContainerFO()
  191. .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
  192. contentRectOffsetY += getBlockContainerFO()
  193. .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
  194. Rectangle2D rect = new Rectangle2D.Double(
  195. contentRectOffsetX, contentRectOffsetY,
  196. getContentAreaIPD(), getContentAreaBPD());
  197. relDims = new FODimension(0, 0);
  198. absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
  199. getBlockContainerFO().getWritingMode(), rect, relDims);
  200. int availableIPD = referenceIPD - getIPIndents();
  201. if (rect.getWidth() > availableIPD) {
  202. log.warn(FONode.decorateWithContextInfo(
  203. "The extent in inline-progression-direction (width) of a block-container is"
  204. + " bigger than the available space ("
  205. + rect.getWidth() + "mpt > " + context.getRefIPD() + "mpt)",
  206. getBlockContainerFO()));
  207. }
  208. MinOptMax stackLimit = new MinOptMax(relDims.bpd);
  209. LinkedList returnedList = null;
  210. LinkedList contentList = new LinkedList();
  211. LinkedList returnList = new LinkedList();
  212. if (!breakBeforeServed) {
  213. try {
  214. if (addKnuthElementsForBreakBefore(returnList, context)) {
  215. return returnList;
  216. }
  217. } finally {
  218. breakBeforeServed = true;
  219. }
  220. }
  221. if (!firstVisibleMarkServed) {
  222. addKnuthElementsForSpaceBefore(returnList, alignment);
  223. }
  224. addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
  225. firstVisibleMarkServed = true;
  226. if (autoHeight) {
  227. //Spaces, border and padding to be repeated at each break
  228. addPendingMarks(context);
  229. BlockLevelLayoutManager curLM; // currently active LM
  230. BlockLevelLayoutManager prevLM = null; // previously active LM
  231. while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
  232. LayoutContext childLC = new LayoutContext(0);
  233. childLC.copyPendingMarksFrom(context);
  234. // curLM is a ?
  235. childLC.setStackLimit(MinOptMax.subtract(context
  236. .getStackLimit(), stackLimit));
  237. childLC.setRefIPD(relDims.ipd);
  238. childLC.setWritingMode(getBlockContainerFO().getWritingMode());
  239. // get elements from curLM
  240. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  241. if (returnedList.size() == 1
  242. && ((ListElement)returnedList.getFirst()).isForcedBreak()) {
  243. // a descendant of this block has break-before
  244. /*
  245. if (returnList.size() == 0) {
  246. // the first child (or its first child ...) has
  247. // break-before;
  248. // all this block, including space before, will be put in
  249. // the
  250. // following page
  251. bSpaceBeforeServed = false;
  252. }*/
  253. contentList.addAll(returnedList);
  254. // "wrap" the Position inside each element
  255. // moving the elements from contentList to returnList
  256. returnedList = new LinkedList();
  257. wrapPositionElements(contentList, returnList);
  258. return returnList;
  259. } else {
  260. if (prevLM != null) {
  261. // there is a block handled by prevLM
  262. // before the one handled by curLM
  263. if (mustKeepTogether()
  264. || prevLM.mustKeepWithNext()
  265. || curLM.mustKeepWithPrevious()) {
  266. // add an infinite penalty to forbid a break between
  267. // blocks
  268. contentList.add(new BreakElement(
  269. new Position(this), KnuthElement.INFINITE, context));
  270. } else if (!((ListElement) contentList.getLast()).isGlue()) {
  271. // add a null penalty to allow a break between blocks
  272. contentList.add(new BreakElement(
  273. new Position(this), 0, context));
  274. } else {
  275. // the last element in contentList is a glue;
  276. // it is a feasible breakpoint, there is no need to add
  277. // a penalty
  278. }
  279. }
  280. contentList.addAll(returnedList);
  281. if (returnedList.size() == 0) {
  282. //Avoid NoSuchElementException below (happens with empty blocks)
  283. continue;
  284. }
  285. if (((ListElement)returnedList.getLast()).isForcedBreak()) {
  286. // a descendant of this block has break-after
  287. if (curLM.isFinished()) {
  288. // there is no other content in this block;
  289. // it's useless to add space after before a page break
  290. setFinished(true);
  291. }
  292. returnedList = new LinkedList();
  293. wrapPositionElements(contentList, returnList);
  294. return returnList;
  295. }
  296. }
  297. prevLM = curLM;
  298. }
  299. returnedList = new LinkedList();
  300. wrapPositionElements(contentList, returnList);
  301. } else {
  302. MinOptMax range = new MinOptMax(relDims.ipd);
  303. BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
  304. breaker.doLayout(relDims.bpd, autoHeight);
  305. boolean contentOverflows = false;
  306. if (!breaker.isEmpty()) {
  307. contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
  308. }
  309. Position bcPosition = new BlockContainerPosition(this, breaker);
  310. returnList.add(new KnuthBox(vpContentBPD, notifyPos(bcPosition), false));
  311. //TODO Handle min/opt/max for block-progression-dimension
  312. /* These two elements will be used to add stretchability to the above box
  313. returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
  314. false, returnPosition, false));
  315. returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
  316. LINE_NUMBER_ADJUSTMENT, returnPosition, false));
  317. */
  318. if (contentOverflows) {
  319. log.warn("Contents overflow block-container viewport: clipping");
  320. if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
  321. //TODO Throw layout exception
  322. }
  323. }
  324. }
  325. addKnuthElementsForBorderPaddingAfter(returnList, true);
  326. addKnuthElementsForSpaceAfter(returnList, alignment);
  327. addKnuthElementsForBreakAfter(returnList, context);
  328. setFinished(true);
  329. return returnList;
  330. }
  331. private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
  332. autoHeight = false;
  333. Point offset = getAbsOffset();
  334. int allocBPD, allocIPD;
  335. if (height.getEnum() == EN_AUTO
  336. || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
  337. //auto height when height="auto" or "if that dimension is not specified explicitly
  338. //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1)
  339. allocBPD = 0;
  340. if (abProps.bottom.getEnum() != EN_AUTO) {
  341. int availHeight;
  342. if (isFixed()) {
  343. availHeight = (int)getCurrentPV().getViewArea().getHeight();
  344. } else {
  345. availHeight = context.getStackLimit().opt;
  346. }
  347. allocBPD = availHeight;
  348. allocBPD -= offset.y;
  349. if (abProps.bottom.getEnum() != EN_AUTO) {
  350. allocBPD -= abProps.bottom.getValue(this);
  351. if (allocBPD < 0) {
  352. //TODO Fix absolute b-c layout, layout may need to be defferred until
  353. //after page breaking when the size of the containing box is known.
  354. /* Warning disabled due to a interpretation mistake.
  355. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
  356. log.error("The current combination of top and bottom properties results"
  357. + " in a negative extent for the block-container. 'bottom' may be"
  358. + " at most " + (allocBPD + abProps.bottom.getValue(this)) + " mpt,"
  359. + " but was actually " + abProps.bottom.getValue(this) + " mpt."
  360. + " The nominal available height is " + availHeight + " mpt.");
  361. */
  362. allocBPD = 0;
  363. }
  364. } else {
  365. if (allocBPD < 0) {
  366. /* Warning disabled due to a interpretation mistake.
  367. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
  368. log.error("The current combination of top and bottom properties results"
  369. + " in a negative extent for the block-container. 'top' may be"
  370. + " at most " + availHeight + " mpt,"
  371. + " but was actually " + offset.y + " mpt."
  372. + " The nominal available height is " + availHeight + " mpt.");
  373. */
  374. allocBPD = 0;
  375. }
  376. }
  377. } else {
  378. autoHeight = true;
  379. }
  380. } else {
  381. allocBPD = height.getValue(this); //this is the content-height
  382. allocBPD += getBPIndents();
  383. }
  384. if (width.getEnum() == EN_AUTO) {
  385. int availWidth;
  386. if (isFixed()) {
  387. availWidth = (int)getCurrentPV().getViewArea().getWidth();
  388. } else {
  389. availWidth = context.getRefIPD();
  390. }
  391. allocIPD = availWidth;
  392. if (abProps.left.getEnum() != EN_AUTO) {
  393. allocIPD -= abProps.left.getValue(this);
  394. }
  395. if (abProps.right.getEnum() != EN_AUTO) {
  396. allocIPD -= abProps.right.getValue(this);
  397. if (allocIPD < 0) {
  398. /* Warning disabled due to a interpretation mistake.
  399. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
  400. log.error("The current combination of left and right properties results"
  401. + " in a negative extent for the block-container. 'right' may be"
  402. + " at most " + (allocIPD + abProps.right.getValue(this)) + " mpt,"
  403. + " but was actually " + abProps.right.getValue(this) + " mpt."
  404. + " The nominal available width is " + availWidth + " mpt.");
  405. */
  406. allocIPD = 0;
  407. }
  408. } else {
  409. if (allocIPD < 0) {
  410. /* Warning disabled due to a interpretation mistake.
  411. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
  412. log.error("The current combination of left and right properties results"
  413. + " in a negative extent for the block-container. 'left' may be"
  414. + " at most " + allocIPD + " mpt,"
  415. + " but was actually " + abProps.left.getValue(this) + " mpt."
  416. + " The nominal available width is " + availWidth + " mpt.");
  417. */
  418. allocIPD = 0;
  419. }
  420. }
  421. } else {
  422. allocIPD = width.getValue(this); //this is the content-width
  423. allocIPD += getIPIndents();
  424. }
  425. vpContentBPD = allocBPD - getBPIndents();
  426. setContentAreaIPD(allocIPD - getIPIndents());
  427. double contentRectOffsetX = offset.getX();
  428. contentRectOffsetX += getBlockContainerFO()
  429. .getCommonMarginBlock().startIndent.getValue(this);
  430. double contentRectOffsetY = offset.getY();
  431. contentRectOffsetY += getSpaceBefore(); //TODO Uhm, is that necessary?
  432. contentRectOffsetY += getBlockContainerFO()
  433. .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
  434. contentRectOffsetY += getBlockContainerFO()
  435. .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
  436. Rectangle2D rect = new Rectangle2D.Double(
  437. contentRectOffsetX, contentRectOffsetY,
  438. getContentAreaIPD(), vpContentBPD);
  439. relDims = new FODimension(0, 0);
  440. absoluteCTM = CTM.getCTMandRelDims(
  441. getBlockContainerFO().getReferenceOrientation(),
  442. getBlockContainerFO().getWritingMode(),
  443. rect, relDims);
  444. MinOptMax range = new MinOptMax(relDims.ipd);
  445. BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
  446. breaker.doLayout(relDims.bpd, autoHeight);
  447. boolean contentOverflows = breaker.isOverflow();
  448. LinkedList returnList = new LinkedList();
  449. if (!breaker.isEmpty()) {
  450. Position bcPosition = new BlockContainerPosition(this, breaker);
  451. returnList.add(new KnuthBox(0, notifyPos(bcPosition), false));
  452. //TODO Maybe check for page overflow when autoHeight=true
  453. if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) {
  454. log.warn("Contents overflow block-container viewport: clipping");
  455. if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
  456. //TODO Throw layout exception
  457. }
  458. }
  459. }
  460. setFinished(true);
  461. return returnList;
  462. }
  463. private class BlockContainerPosition extends NonLeafPosition {
  464. private BlockContainerBreaker breaker;
  465. public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
  466. super(lm, null);
  467. this.breaker = breaker;
  468. }
  469. public BlockContainerBreaker getBreaker() {
  470. return this.breaker;
  471. }
  472. }
  473. private class BlockContainerBreaker extends AbstractBreaker {
  474. private BlockContainerLayoutManager bclm;
  475. private MinOptMax ipd;
  476. //Info for deferred adding of areas
  477. private PageBreakingAlgorithm deferredAlg;
  478. private BlockSequence deferredOriginalList;
  479. private BlockSequence deferredEffectiveList;
  480. public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
  481. this.bclm = bclm;
  482. this.ipd = ipd;
  483. }
  484. /** @see org.apache.fop.layoutmgr.AbstractBreaker#observeElementList(java.util.List) */
  485. protected void observeElementList(List elementList) {
  486. ElementListObserver.observe(elementList, "block-container",
  487. bclm.getBlockContainerFO().getId());
  488. }
  489. /** @see org.apache.fop.layoutmgr.AbstractBreaker#isPartOverflowRecoveryActivated() */
  490. protected boolean isPartOverflowRecoveryActivated() {
  491. //For block-containers, this must be disabled because of wanted overflow.
  492. return false;
  493. }
  494. /** @see org.apache.fop.layoutmgr.AbstractBreaker#isSinglePartFavored() */
  495. protected boolean isSinglePartFavored() {
  496. return true;
  497. }
  498. public int getDifferenceOfFirstPart() {
  499. PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
  500. return pbp.difference;
  501. }
  502. public boolean isOverflow() {
  503. if (isEmpty()) {
  504. return false;
  505. } else {
  506. return (deferredAlg.getPageBreaks().size() > 1);
  507. }
  508. }
  509. protected LayoutManager getTopLevelLM() {
  510. return bclm;
  511. }
  512. protected LayoutContext createLayoutContext() {
  513. LayoutContext lc = super.createLayoutContext();
  514. lc.setRefIPD(ipd.opt);
  515. lc.setWritingMode(getBlockContainerFO().getWritingMode());
  516. return lc;
  517. }
  518. protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  519. LayoutManager curLM; // currently active LM
  520. LinkedList returnList = new LinkedList();
  521. while ((curLM = getChildLM()) != null) {
  522. LayoutContext childLC = new LayoutContext(0);
  523. childLC.setStackLimit(context.getStackLimit());
  524. childLC.setRefIPD(context.getRefIPD());
  525. childLC.setWritingMode(getBlockContainerFO().getWritingMode());
  526. LinkedList returnedList = null;
  527. if (!curLM.isFinished()) {
  528. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  529. }
  530. if (returnedList != null) {
  531. bclm.wrapPositionElements(returnedList, returnList);
  532. }
  533. }
  534. SpaceResolver.resolveElementList(returnList);
  535. setFinished(true);
  536. return returnList;
  537. }
  538. protected int getCurrentDisplayAlign() {
  539. return getBlockContainerFO().getDisplayAlign();
  540. }
  541. protected boolean hasMoreContent() {
  542. return !isFinished();
  543. }
  544. protected void addAreas(PositionIterator posIter, LayoutContext context) {
  545. AreaAdditionUtil.addAreas(bclm, posIter, context);
  546. }
  547. protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
  548. BlockSequence originalList, BlockSequence effectiveList) {
  549. //Defer adding of areas until addAreas is called by the parent LM
  550. this.deferredAlg = alg;
  551. this.deferredOriginalList = originalList;
  552. this.deferredEffectiveList = effectiveList;
  553. }
  554. protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
  555. //nop for bclm
  556. }
  557. protected LayoutManager getCurrentChildLM() {
  558. return curChildLM;
  559. }
  560. public void addContainedAreas() {
  561. if (isEmpty()) {
  562. return;
  563. }
  564. //Rendering all parts (not just the first) at once for the case where the parts that
  565. //overflow should be visible.
  566. //TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
  567. this.addAreas(this.deferredAlg,
  568. /*1*/ this.deferredAlg.getPageBreaks().size(),
  569. this.deferredOriginalList, this.deferredEffectiveList);
  570. }
  571. }
  572. private Point getAbsOffset() {
  573. int x = 0;
  574. int y = 0;
  575. if (abProps.left.getEnum() != EN_AUTO) {
  576. x = abProps.left.getValue(this);
  577. }
  578. if (abProps.top.getEnum() != EN_AUTO) {
  579. y = abProps.top.getValue(this);
  580. }
  581. return new Point(x, y);
  582. }
  583. /** @see org.apache.fop.layoutmgr.LayoutManager */
  584. public void addAreas(PositionIterator parentIter,
  585. LayoutContext layoutContext) {
  586. getParentArea(null);
  587. // if this will create the first block area in a page
  588. // and display-align is bottom or center, add space before
  589. if (layoutContext.getSpaceBefore() > 0) {
  590. addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
  591. }
  592. LayoutManager childLM = null;
  593. LayoutManager lastLM = null;
  594. LayoutContext lc = new LayoutContext(0);
  595. lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
  596. // set space after in the LayoutContext for children
  597. if (layoutContext.getSpaceAfter() > 0) {
  598. lc.setSpaceAfter(layoutContext.getSpaceAfter());
  599. }
  600. BlockContainerPosition bcpos = null;
  601. PositionIterator childPosIter;
  602. // "unwrap" the NonLeafPositions stored in parentIter
  603. // and put them in a new list;
  604. LinkedList positionList = new LinkedList();
  605. Position pos;
  606. boolean bSpaceBefore = false;
  607. boolean bSpaceAfter = false;
  608. Position firstPos = null;
  609. Position lastPos = null;
  610. while (parentIter.hasNext()) {
  611. pos = (Position) parentIter.next();
  612. if (pos.getIndex() >= 0) {
  613. if (firstPos == null) {
  614. firstPos = pos;
  615. }
  616. lastPos = pos;
  617. }
  618. Position innerPosition = pos;
  619. if (pos instanceof NonLeafPosition) {
  620. innerPosition = ((NonLeafPosition)pos).getPosition();
  621. }
  622. if (pos instanceof BlockContainerPosition) {
  623. if (bcpos != null) {
  624. throw new IllegalStateException("Only one BlockContainerPosition allowed");
  625. }
  626. bcpos = (BlockContainerPosition)pos;
  627. //Add child areas inside the reference area
  628. //bcpos.getBreaker().addContainedAreas();
  629. } else if (innerPosition == null) {
  630. if (pos instanceof NonLeafPosition) {
  631. // pos was created by this BCLM and was inside an element
  632. // representing space before or after
  633. // this means the space was not discarded
  634. if (positionList.size() == 0 && bcpos == null) {
  635. // pos was in the element representing space-before
  636. bSpaceBefore = true;
  637. } else {
  638. // pos was in the element representing space-after
  639. bSpaceAfter = true;
  640. }
  641. } else {
  642. //ignore (probably a Position for a simple penalty between blocks)
  643. }
  644. } else if (innerPosition.getLM() == this
  645. && !(innerPosition instanceof MappingPosition)) {
  646. // pos was created by this BlockLM and was inside a penalty
  647. // allowing or forbidding a page break
  648. // nothing to do
  649. } else {
  650. // innerPosition was created by another LM
  651. positionList.add(innerPosition);
  652. lastLM = innerPosition.getLM();
  653. }
  654. }
  655. getPSLM().addIDToPage(getBlockContainerFO().getId());
  656. if (markers != null) {
  657. getCurrentPV().addMarkers(markers, true, isFirst(firstPos), isLast(lastPos));
  658. }
  659. if (bcpos == null) {
  660. if (bpUnit == 0) {
  661. // the Positions in positionList were inside the elements
  662. // created by the LineLM
  663. childPosIter = new StackingIter(positionList.listIterator());
  664. } else {
  665. // the Positions in positionList were inside the elements
  666. // created by the BCLM in the createUnitElements() method
  667. //if (((Position) positionList.getLast()) instanceof
  668. // LeafPosition) {
  669. // // the last item inside positionList is a LeafPosition
  670. // // (a LineBreakPosition, more precisely); this means that
  671. // // the whole paragraph is on the same page
  672. // childPosIter = new KnuthPossPosIter(storedList, 0,
  673. // storedList.size());
  674. //} else {
  675. // // the last item inside positionList is a Position;
  676. // // this means that the paragraph has been split
  677. // // between consecutive pages
  678. LinkedList splitList = new LinkedList();
  679. int splitLength = 0;
  680. int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
  681. int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
  682. // copy from storedList to splitList all the elements from
  683. // iFirst to iLast
  684. ListIterator storedListIterator = storedList.listIterator(iFirst);
  685. while (storedListIterator.nextIndex() <= iLast) {
  686. KnuthElement element = (KnuthElement) storedListIterator
  687. .next();
  688. // some elements in storedList (i.e. penalty items) were created
  689. // by this BlockLM, and must be ignored
  690. if (element.getLayoutManager() != this) {
  691. splitList.add(element);
  692. splitLength += element.getW();
  693. lastLM = element.getLayoutManager();
  694. }
  695. }
  696. //log.debug("Adding areas from " + iFirst + " to " + iLast);
  697. //log.debug("splitLength= " + splitLength
  698. // + " (" + neededUnits(splitLength) + " units') "
  699. // + (neededUnits(splitLength) * bpUnit - splitLength)
  700. // + " spacing");
  701. // add space before and / or after the paragraph
  702. // to reach a multiple of bpUnit
  703. if (bSpaceBefore && bSpaceAfter) {
  704. foBlockSpaceBefore = new SpaceVal(getBlockContainerFO()
  705. .getCommonMarginBlock().spaceBefore, this).getSpace();
  706. foBlockSpaceAfter = new SpaceVal(getBlockContainerFO()
  707. .getCommonMarginBlock().spaceAfter, this).getSpace();
  708. adjustedSpaceBefore = (neededUnits(splitLength
  709. + foBlockSpaceBefore.min
  710. + foBlockSpaceAfter.min)
  711. * bpUnit - splitLength) / 2;
  712. adjustedSpaceAfter = neededUnits(splitLength
  713. + foBlockSpaceBefore.min
  714. + foBlockSpaceAfter.min)
  715. * bpUnit - splitLength - adjustedSpaceBefore;
  716. } else if (bSpaceBefore) {
  717. adjustedSpaceBefore = neededUnits(splitLength
  718. + foBlockSpaceBefore.min)
  719. * bpUnit - splitLength;
  720. } else {
  721. adjustedSpaceAfter = neededUnits(splitLength
  722. + foBlockSpaceAfter.min)
  723. * bpUnit - splitLength;
  724. }
  725. //log.debug("space before = " + adjustedSpaceBefore
  726. // + " space after = " + adjustedSpaceAfter + " total = " +
  727. // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
  728. childPosIter = new KnuthPossPosIter(splitList, 0, splitList
  729. .size());
  730. //}
  731. }
  732. while ((childLM = childPosIter.getNextChildLM()) != null) {
  733. // set last area flag
  734. lc.setFlags(LayoutContext.LAST_AREA,
  735. (layoutContext.isLastArea() && childLM == lastLM));
  736. /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
  737. // Add the line areas to Area
  738. childLM.addAreas(childPosIter, lc);
  739. }
  740. } else {
  741. //Add child areas inside the reference area
  742. bcpos.getBreaker().addContainedAreas();
  743. }
  744. if (markers != null) {
  745. getCurrentPV().addMarkers(markers, false, isFirst(firstPos), isLast(lastPos));
  746. }
  747. TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
  748. effSpaceBefore, effSpaceAfter);
  749. flush();
  750. viewportBlockArea = null;
  751. referenceArea = null;
  752. resetSpaces();
  753. getPSLM().notifyEndOfLayout(((BlockContainer)getFObj()).getId());
  754. }
  755. /**
  756. * Get the parent area for children of this block container.
  757. * This returns the current block container area
  758. * and creates it if required.
  759. *
  760. * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
  761. */
  762. public Area getParentArea(Area childArea) {
  763. if (referenceArea == null) {
  764. viewportBlockArea = new BlockViewport();
  765. viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE);
  766. viewportBlockArea.setIPD(getContentAreaIPD());
  767. if (autoHeight) {
  768. viewportBlockArea.setBPD(0);
  769. } else {
  770. viewportBlockArea.setBPD(getContentAreaBPD());
  771. }
  772. TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
  773. TraitSetter.addBorders(viewportBlockArea,
  774. getBlockContainerFO().getCommonBorderPaddingBackground(),
  775. discardBorderBefore, discardBorderAfter, false, false, this);
  776. TraitSetter.addPadding(viewportBlockArea,
  777. getBlockContainerFO().getCommonBorderPaddingBackground(),
  778. discardPaddingBefore, discardPaddingAfter, false, false, this);
  779. // TraitSetter.addBackground(viewportBlockArea,
  780. // getBlockContainerFO().getCommonBorderPaddingBackground(),
  781. // this);
  782. TraitSetter.addMargins(viewportBlockArea,
  783. getBlockContainerFO().getCommonBorderPaddingBackground(),
  784. startIndent, endIndent,
  785. this);
  786. viewportBlockArea.setCTM(absoluteCTM);
  787. viewportBlockArea.setClip(needClip());
  788. /*
  789. if (getSpaceBefore() != 0) {
  790. viewportBlockArea.addTrait(Trait.SPACE_BEFORE, new Integer(getSpaceBefore()));
  791. }
  792. if (foBlockSpaceAfter.opt != 0) {
  793. viewportBlockArea.addTrait(Trait.SPACE_AFTER, new Integer(foBlockSpaceAfter.opt));
  794. }*/
  795. if (abProps.absolutePosition == EN_ABSOLUTE
  796. || abProps.absolutePosition == EN_FIXED) {
  797. Point offset = getAbsOffset();
  798. viewportBlockArea.setXOffset(offset.x);
  799. viewportBlockArea.setYOffset(offset.y);
  800. } else {
  801. //nop
  802. }
  803. referenceArea = new Block();
  804. referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  805. TraitSetter.setProducerID(referenceArea, getBlockContainerFO().getId());
  806. if (abProps.absolutePosition == EN_ABSOLUTE) {
  807. viewportBlockArea.setPositioning(Block.ABSOLUTE);
  808. } else if (abProps.absolutePosition == EN_FIXED) {
  809. viewportBlockArea.setPositioning(Block.FIXED);
  810. }
  811. // Set up dimensions
  812. // Must get dimensions from parent area
  813. /*Area parentArea =*/ parentLM.getParentArea(referenceArea);
  814. //int referenceIPD = parentArea.getIPD();
  815. referenceArea.setIPD(relDims.ipd);
  816. // Get reference IPD from parentArea
  817. setCurrentArea(viewportBlockArea); // ??? for generic operations
  818. }
  819. return referenceArea;
  820. }
  821. /**
  822. * Add the child to the block container.
  823. *
  824. * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
  825. */
  826. public void addChildArea(Area childArea) {
  827. if (referenceArea != null) {
  828. referenceArea.addBlock((Block) childArea);
  829. }
  830. }
  831. /**
  832. * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(org.apache.fop.layoutmgr.Position)
  833. */
  834. public void resetPosition(Position resetPos) {
  835. if (resetPos == null) {
  836. reset(null);
  837. }
  838. }
  839. /**
  840. * Force current area to be added to parent area.
  841. * @see org.apache.fop.layoutmgr.BlockStackingLayoutManager#flush()
  842. */
  843. protected void flush() {
  844. viewportBlockArea.addBlock(referenceArea, autoHeight);
  845. TraitSetter.addBackground(viewportBlockArea,
  846. getBlockContainerFO().getCommonBorderPaddingBackground(),
  847. this);
  848. // Fake a 0 height for absolute positioned blocks.
  849. int saveBPD = viewportBlockArea.getBPD();
  850. if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
  851. viewportBlockArea.setBPD(0);
  852. }
  853. super.flush();
  854. // Restore the right height.
  855. if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
  856. viewportBlockArea.setBPD(saveBPD);
  857. }
  858. }
  859. /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
  860. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  861. // TODO Auto-generated method stub
  862. return 0;
  863. }
  864. /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
  865. public void discardSpace(KnuthGlue spaceGlue) {
  866. // TODO Auto-generated method stub
  867. }
  868. /**
  869. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
  870. */
  871. public boolean mustKeepTogether() {
  872. //TODO Keeps will have to be more sophisticated sooner or later
  873. return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
  874. || !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto()
  875. || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto();
  876. }
  877. /**
  878. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
  879. */
  880. public boolean mustKeepWithPrevious() {
  881. return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto()
  882. || !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto();
  883. }
  884. /**
  885. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
  886. */
  887. public boolean mustKeepWithNext() {
  888. return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto()
  889. || !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto();
  890. }
  891. /**
  892. * @return the BlockContainer node
  893. */
  894. protected BlockContainer getBlockContainerFO() {
  895. return (BlockContainer) fobj;
  896. }
  897. // --------- Property Resolution related functions --------- //
  898. /**
  899. * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesReferenceArea
  900. */
  901. public boolean getGeneratesReferenceArea() {
  902. return true;
  903. }
  904. /**
  905. * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea
  906. */
  907. public boolean getGeneratesBlockArea() {
  908. return true;
  909. }
  910. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  911. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  912. if (RelSide.BEFORE == side) {
  913. if (log.isDebugEnabled()) {
  914. log.debug(this + ": Space " + side + ", "
  915. + this.effSpaceBefore + "-> " + effectiveLength);
  916. }
  917. this.effSpaceBefore = effectiveLength;
  918. } else {
  919. if (log.isDebugEnabled()) {
  920. log.debug(this + ": Space " + side + ", "
  921. + this.effSpaceAfter + "-> " + effectiveLength);
  922. }
  923. this.effSpaceAfter = effectiveLength;
  924. }
  925. }
  926. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  927. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  928. if (effectiveLength == null) {
  929. if (RelSide.BEFORE == side) {
  930. this.discardBorderBefore = true;
  931. } else {
  932. this.discardBorderAfter = true;
  933. }
  934. }
  935. if (log.isDebugEnabled()) {
  936. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  937. }
  938. }
  939. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  940. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  941. if (effectiveLength == null) {
  942. if (RelSide.BEFORE == side) {
  943. this.discardPaddingBefore = true;
  944. } else {
  945. this.discardPaddingAfter = true;
  946. }
  947. }
  948. if (log.isDebugEnabled()) {
  949. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  950. }
  951. }
  952. }