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

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