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

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