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