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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  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 = new LayoutContext(0);
  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() {
  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. this.deferredAlg.getPageBreaks().size(),
  627. this.deferredOriginalList, this.deferredEffectiveList);
  628. }
  629. }
  630. private Point getAbsOffset() {
  631. int x = 0;
  632. int y = 0;
  633. if (abProps.left.getEnum() != EN_AUTO) {
  634. x = abProps.left.getValue(this);
  635. } else if (abProps.right.getEnum() != EN_AUTO
  636. && width.getEnum() != EN_AUTO) {
  637. x = getReferenceAreaIPD()
  638. - abProps.right.getValue(this) - width.getValue(this);
  639. }
  640. if (abProps.top.getEnum() != EN_AUTO) {
  641. y = abProps.top.getValue(this);
  642. } else if (abProps.bottom.getEnum() != EN_AUTO
  643. && height.getEnum() != EN_AUTO) {
  644. y = getReferenceAreaBPD()
  645. - abProps.bottom.getValue(this) - height.getValue(this);
  646. }
  647. return new Point(x, y);
  648. }
  649. /** {@inheritDoc} */
  650. @Override
  651. public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
  652. getParentArea(null);
  653. // if this will create the first block area in a page
  654. // and display-align is bottom or center, add space before
  655. if (layoutContext.getSpaceBefore() > 0) {
  656. addBlockSpacing(0.0, MinOptMax.getInstance(layoutContext.getSpaceBefore()));
  657. }
  658. LayoutManager childLM;
  659. LayoutManager lastLM = null;
  660. LayoutContext lc = new LayoutContext(0);
  661. lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
  662. // set space after in the LayoutContext for children
  663. if (layoutContext.getSpaceAfter() > 0) {
  664. lc.setSpaceAfter(layoutContext.getSpaceAfter());
  665. }
  666. BlockContainerPosition bcpos = null;
  667. PositionIterator childPosIter;
  668. // "unwrap" the NonLeafPositions stored in parentIter
  669. // and put them in a new list;
  670. List<Position> positionList = new LinkedList<Position>();
  671. Position pos;
  672. Position firstPos = null;
  673. Position lastPos = null;
  674. while (parentIter.hasNext()) {
  675. pos = parentIter.next();
  676. if (pos.getIndex() >= 0) {
  677. if (firstPos == null) {
  678. firstPos = pos;
  679. }
  680. lastPos = pos;
  681. }
  682. Position innerPosition = pos;
  683. if (pos instanceof NonLeafPosition) {
  684. innerPosition = pos.getPosition();
  685. }
  686. if (pos instanceof BlockContainerPosition) {
  687. if (bcpos != null) {
  688. throw new IllegalStateException("Only one BlockContainerPosition allowed");
  689. }
  690. bcpos = (BlockContainerPosition)pos;
  691. //Add child areas inside the reference area
  692. //bcpos.getBreaker().addContainedAreas();
  693. } else if (innerPosition == null) {
  694. //ignore (probably a Position for a simple penalty between blocks)
  695. } else if (innerPosition.getLM() == this
  696. && !(innerPosition instanceof MappingPosition)) {
  697. // pos was created by this BlockLM and was inside a penalty
  698. // allowing or forbidding a page break
  699. // nothing to do
  700. } else {
  701. // innerPosition was created by another LM
  702. positionList.add(innerPosition);
  703. lastLM = innerPosition.getLM();
  704. }
  705. }
  706. addId();
  707. addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
  708. if (bcpos == null) {
  709. // the Positions in positionList were inside the elements
  710. // created by the LineLM
  711. childPosIter = new PositionIterator(positionList.listIterator());
  712. while ((childLM = childPosIter.getNextChildLM()) != null) {
  713. // set last area flag
  714. lc.setFlags(LayoutContext.LAST_AREA,
  715. (layoutContext.isLastArea() && childLM == lastLM));
  716. lc.setStackLimitBP(layoutContext.getStackLimitBP());
  717. // Add the line areas to Area
  718. childLM.addAreas(childPosIter, lc);
  719. }
  720. } else {
  721. //Add child areas inside the reference area
  722. bcpos.getBreaker().addContainedAreas();
  723. }
  724. addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
  725. TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
  726. effSpaceBefore, effSpaceAfter);
  727. flush();
  728. viewportBlockArea = null;
  729. referenceArea = null;
  730. resetSpaces();
  731. notifyEndOfLayout();
  732. }
  733. /**
  734. * Get the parent area for children of this block container.
  735. * This returns the current block container area
  736. * and creates it if required.
  737. *
  738. * {@inheritDoc}
  739. */
  740. @Override
  741. public Area getParentArea(Area childArea) {
  742. if (referenceArea == null) {
  743. boolean switchedProgressionDirection = blockProgressionDirectionChanges();
  744. boolean allowBPDUpdate = autoHeight && !switchedProgressionDirection;
  745. int level = getBlockContainerFO().getBidiLevel();
  746. viewportBlockArea = new BlockViewport(allowBPDUpdate);
  747. viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE);
  748. if ( level >= 0 ) {
  749. viewportBlockArea.setBidiLevel ( level );
  750. }
  751. viewportBlockArea.setIPD(getContentAreaIPD());
  752. if (allowBPDUpdate) {
  753. viewportBlockArea.setBPD(0);
  754. } else {
  755. viewportBlockArea.setBPD(this.vpContentBPD);
  756. }
  757. transferForeignAttributes(viewportBlockArea);
  758. TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
  759. TraitSetter.addBorders(viewportBlockArea,
  760. getBlockContainerFO().getCommonBorderPaddingBackground(),
  761. discardBorderBefore, discardBorderAfter, false, false, this);
  762. TraitSetter.addPadding(viewportBlockArea,
  763. getBlockContainerFO().getCommonBorderPaddingBackground(),
  764. discardPaddingBefore, discardPaddingAfter, false, false, this);
  765. TraitSetter.addMargins(viewportBlockArea,
  766. getBlockContainerFO().getCommonBorderPaddingBackground(),
  767. startIndent, endIndent,
  768. this);
  769. viewportBlockArea.setCTM(absoluteCTM);
  770. viewportBlockArea.setClip(needClip());
  771. if (abProps.absolutePosition == EN_ABSOLUTE
  772. || abProps.absolutePosition == EN_FIXED) {
  773. Point offset = getAbsOffset();
  774. viewportBlockArea.setXOffset(offset.x);
  775. viewportBlockArea.setYOffset(offset.y);
  776. } else {
  777. //nop
  778. }
  779. referenceArea = new Block();
  780. referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  781. if ( level >= 0 ) {
  782. referenceArea.setBidiLevel ( level );
  783. }
  784. TraitSetter.setProducerID(referenceArea, getBlockContainerFO().getId());
  785. if (abProps.absolutePosition == EN_ABSOLUTE) {
  786. viewportBlockArea.setPositioning(Block.ABSOLUTE);
  787. } else if (abProps.absolutePosition == EN_FIXED) {
  788. viewportBlockArea.setPositioning(Block.FIXED);
  789. }
  790. // Set up dimensions
  791. // Must get dimensions from parent area
  792. /*Area parentArea =*/ parentLayoutManager.getParentArea(referenceArea);
  793. //int referenceIPD = parentArea.getIPD();
  794. referenceArea.setIPD(relDims.ipd);
  795. // Get reference IPD from parentArea
  796. setCurrentArea(viewportBlockArea); // ??? for generic operations
  797. }
  798. return referenceArea;
  799. }
  800. /**
  801. * Add the child to the block container.
  802. *
  803. * {@inheritDoc}
  804. */
  805. @Override
  806. public void addChildArea(Area childArea) {
  807. if (referenceArea != null) {
  808. referenceArea.addBlock((Block) childArea);
  809. }
  810. }
  811. /**
  812. * Force current area to be added to parent area.
  813. * {@inheritDoc}
  814. */
  815. @Override
  816. protected void flush() {
  817. viewportBlockArea.addBlock(referenceArea, autoHeight);
  818. TraitSetter.addBackground(viewportBlockArea,
  819. getBlockContainerFO().getCommonBorderPaddingBackground(),
  820. this);
  821. super.flush();
  822. }
  823. /** {@inheritDoc} */
  824. @Override
  825. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  826. // TODO Auto-generated method stub
  827. return 0;
  828. }
  829. /** {@inheritDoc} */
  830. @Override
  831. public void discardSpace(KnuthGlue spaceGlue) {
  832. // TODO Auto-generated method stub
  833. }
  834. /** {@inheritDoc} */
  835. @Override
  836. public KeepProperty getKeepTogetherProperty() {
  837. return getBlockContainerFO().getKeepTogether();
  838. }
  839. /** {@inheritDoc} */
  840. @Override
  841. public KeepProperty getKeepWithPreviousProperty() {
  842. return getBlockContainerFO().getKeepWithPrevious();
  843. }
  844. /** {@inheritDoc} */
  845. @Override
  846. public KeepProperty getKeepWithNextProperty() {
  847. return getBlockContainerFO().getKeepWithNext();
  848. }
  849. /**
  850. * @return the BlockContainer node
  851. */
  852. protected BlockContainer getBlockContainerFO() {
  853. return (BlockContainer) fobj;
  854. }
  855. // --------- Property Resolution related functions --------- //
  856. /** {@inheritDoc} */
  857. @Override
  858. public boolean getGeneratesReferenceArea() {
  859. return true;
  860. }
  861. /** {@inheritDoc} */
  862. @Override
  863. public boolean getGeneratesBlockArea() {
  864. return true;
  865. }
  866. /** {@inheritDoc} */
  867. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  868. if (RelSide.BEFORE == side) {
  869. if (log.isDebugEnabled()) {
  870. log.debug(this + ": Space " + side + ", "
  871. + this.effSpaceBefore + "-> " + effectiveLength);
  872. }
  873. this.effSpaceBefore = effectiveLength;
  874. } else {
  875. if (log.isDebugEnabled()) {
  876. log.debug(this + ": Space " + side + ", "
  877. + this.effSpaceAfter + "-> " + effectiveLength);
  878. }
  879. this.effSpaceAfter = effectiveLength;
  880. }
  881. }
  882. /** {@inheritDoc} */
  883. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  884. if (effectiveLength == null) {
  885. if (RelSide.BEFORE == side) {
  886. this.discardBorderBefore = true;
  887. } else {
  888. this.discardBorderAfter = true;
  889. }
  890. }
  891. if (log.isDebugEnabled()) {
  892. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  893. }
  894. }
  895. /** {@inheritDoc} */
  896. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  897. if (effectiveLength == null) {
  898. if (RelSide.BEFORE == side) {
  899. this.discardPaddingBefore = true;
  900. } else {
  901. this.discardPaddingAfter = true;
  902. }
  903. }
  904. if (log.isDebugEnabled()) {
  905. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  906. }
  907. }
  908. /** {@inheritDoc} */
  909. public boolean handleOverflow(int milliPoints) {
  910. if (milliPoints > this.horizontalOverflow) {
  911. this.horizontalOverflow = milliPoints;
  912. }
  913. return true;
  914. }
  915. public int getBreakBefore() {
  916. return BreakOpportunityHelper.getBreakBefore(this);
  917. }
  918. }