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

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