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

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