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

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