1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.layoutmgr;
-
- import java.awt.Point;
- import java.awt.geom.Rectangle2D;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Stack;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.area.Area;
- import org.apache.fop.area.Block;
- import org.apache.fop.area.BlockViewport;
- import org.apache.fop.area.CTM;
- import org.apache.fop.area.Trait;
- import org.apache.fop.datatypes.FODimension;
- import org.apache.fop.datatypes.Length;
- import org.apache.fop.fo.flow.BlockContainer;
- import org.apache.fop.fo.properties.CommonAbsolutePosition;
- import org.apache.fop.fo.properties.KeepProperty;
- import org.apache.fop.traits.MinOptMax;
- import org.apache.fop.traits.SpaceVal;
-
- /**
- * LayoutManager for a block-container FO.
- */
- public class BlockContainerLayoutManager extends BlockStackingLayoutManager
- implements ConditionalElementListener {
-
- /**
- * logging instance
- */
- private static Log log = LogFactory.getLog(BlockContainerLayoutManager.class);
-
- private BlockViewport viewportBlockArea;
- private Block referenceArea;
-
- private CommonAbsolutePosition abProps;
- private FODimension relDims;
- private CTM absoluteCTM;
- private Length width;
- private Length height;
- //private int vpContentIPD;
- private int vpContentBPD;
-
- // When viewport should grow with the content.
- private boolean autoHeight = true;
- private boolean inlineElementList = false;
-
- /* holds the (one-time use) fo:block space-before
- and -after properties. Large fo:blocks are split
- into multiple Area.Blocks to accomodate the subsequent
- regions (pages) they are placed on. space-before
- is applied at the beginning of the first
- Block and space-after at the end of the last Block
- used in rendering the fo:block.
- */
- //TODO space-before|after: handle space-resolution rules
- private MinOptMax foBlockSpaceBefore;
- private MinOptMax foBlockSpaceAfter;
-
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
-
- /**
- * Create a new block container layout manager.
- * @param node block-container node to create the layout manager for.
- */
- public BlockContainerLayoutManager(BlockContainer node) {
- super(node);
- }
-
- /** {@inheritDoc} */
- @Override
- public void initialize() {
- abProps = getBlockContainerFO().getCommonAbsolutePosition();
- foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock()
- .spaceBefore, this).getSpace();
- foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock()
- .spaceAfter, this).getSpace();
- startIndent = getBlockContainerFO().getCommonMarginBlock().startIndent.getValue(this);
- endIndent = getBlockContainerFO().getCommonMarginBlock().endIndent.getValue(this);
-
- if (blockProgressionDirectionChanges()) {
- height = getBlockContainerFO().getInlineProgressionDimension()
- .getOptimum(this).getLength();
- width = getBlockContainerFO().getBlockProgressionDimension()
- .getOptimum(this).getLength();
- } else {
- height = getBlockContainerFO().getBlockProgressionDimension()
- .getOptimum(this).getLength();
- width = getBlockContainerFO().getInlineProgressionDimension()
- .getOptimum(this).getLength();
- }
-
- // use optimum space values
- adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock()
- .spaceBefore.getSpace().getOptimum(this).getLength().getValue(this);
- adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock()
- .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this);
- }
-
- private void resetSpaces() {
- this.discardBorderBefore = false;
- this.discardBorderAfter = false;
- this.discardPaddingBefore = false;
- this.discardPaddingAfter = false;
- this.effSpaceBefore = null;
- this.effSpaceAfter = null;
- }
-
- /** @return the content IPD */
- protected int getRotatedIPD() {
- return getBlockContainerFO().getInlineProgressionDimension()
- .getOptimum(this).getLength().getValue(this);
- }
-
- private boolean needClip() {
- int overflow = getBlockContainerFO().getOverflow();
- return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW);
- }
-
- private int getBPIndents() {
- int indents = 0;
- /* TODO This is wrong isn't it?
- indents += getBlockContainerFO().getCommonMarginBlock()
- .spaceBefore.getOptimum(this).getLength().getValue(this);
- indents += getBlockContainerFO().getCommonMarginBlock()
- .spaceAfter.getOptimum(this).getLength().getValue(this);
- */
- indents += getBlockContainerFO().getCommonBorderPaddingBackground()
- .getBPPaddingAndBorder(false, this);
- return indents;
- }
-
- private boolean isAbsoluteOrFixed() {
- return (abProps.absolutePosition == EN_ABSOLUTE
- || abProps.absolutePosition == EN_FIXED);
- }
-
- private boolean isFixed() {
- return (abProps.absolutePosition == EN_FIXED);
- }
-
- /** {@inheritDoc} */
- @Override
- public int getContentAreaBPD() {
- if (autoHeight) {
- return -1;
- } else {
- return this.vpContentBPD;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public List getNextKnuthElements(LayoutContext context, int alignment) {
- return getNextKnuthElements(context, alignment, null, null, null);
- }
-
- /** {@inheritDoc} */
- @Override
- public List getNextKnuthElements // CSOK: MethodLength
- (LayoutContext context, int alignment, Stack lmStack,
- Position restartPosition, LayoutManager restartAtLM) {
- boolean isRestart = (lmStack != null);
- resetSpaces();
- if (isAbsoluteOrFixed()) {
- return getNextKnuthElementsAbsolute(context);
- }
-
- autoHeight = false;
- //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
- int maxbpd = context.getStackLimitBP().getOpt();
- int allocBPD;
- if (height.getEnum() == EN_AUTO
- || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
- //auto height when height="auto" or "if that dimension is not specified explicitly
- //(i.e., it depends on content's block-progression-dimension)" (XSL 1.0, 7.14.1)
- allocBPD = maxbpd;
- autoHeight = true;
- if (getBlockContainerFO().getReferenceOrientation() == 0) {
- //Cannot easily inline element list when ref-or="180"
- inlineElementList = true;
- }
- } else {
- allocBPD = height.getValue(this); //this is the content-height
- allocBPD += getBPIndents();
- }
- vpContentBPD = allocBPD - getBPIndents();
-
- referenceIPD = context.getRefIPD();
- if (width.getEnum() == EN_AUTO) {
- updateContentAreaIPDwithOverconstrainedAdjust();
- } else {
- int contentWidth = width.getValue(this);
- updateContentAreaIPDwithOverconstrainedAdjust(contentWidth);
- }
-
- double contentRectOffsetX = 0;
- contentRectOffsetX += getBlockContainerFO()
- .getCommonMarginBlock().startIndent.getValue(this);
- double contentRectOffsetY = 0;
- contentRectOffsetY += getBlockContainerFO()
- .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
- contentRectOffsetY += getBlockContainerFO()
- .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
-
- updateRelDims(contentRectOffsetX, contentRectOffsetY, autoHeight);
-
- int availableIPD = referenceIPD - getIPIndents();
- if (getContentAreaIPD() > availableIPD) {
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- getBlockContainerFO().getUserAgent().getEventBroadcaster());
- eventProducer.objectTooWide(this, getBlockContainerFO().getName(),
- getContentAreaIPD(), context.getRefIPD(),
- getBlockContainerFO().getLocator());
- }
-
- MinOptMax stackLimit = MinOptMax.getInstance(relDims.bpd);
-
- List<ListElement> returnedList;
- List<ListElement> contentList = new LinkedList<ListElement>();
- List<ListElement> returnList = new LinkedList<ListElement>();
-
- if (!breakBeforeServed) {
- breakBeforeServed = true;
- if (!context.suppressBreakBefore()) {
- if (addKnuthElementsForBreakBefore(returnList, context)) {
- return returnList;
- }
- }
- }
-
- if (!firstVisibleMarkServed) {
- addKnuthElementsForSpaceBefore(returnList, alignment);
- context.updateKeepWithPreviousPending(getKeepWithPrevious());
- }
-
- addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
- firstVisibleMarkServed = true;
-
- if (autoHeight && inlineElementList) {
- //Spaces, border and padding to be repeated at each break
- addPendingMarks(context);
-
- LayoutManager curLM = null; // currently active LM
- LayoutManager prevLM = null; // previously active LM
-
- LayoutContext childLC = new LayoutContext(0);
- if (isRestart) {
- if (lmStack.isEmpty()) {
- assert restartAtLM != null && restartAtLM.getParent() == this;
- curLM = restartAtLM;
- curLM.reset();
- setCurrentChildLM(curLM);
-
- childLC.copyPendingMarksFrom(context);
- childLC.setStackLimitBP(context.getStackLimitBP().minus(stackLimit));
- childLC.setRefIPD(relDims.ipd);
- childLC.setWritingMode(getBlockContainerFO().getWritingMode());
- if (curLM == this.childLMs.get(0)) {
- childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
- //Handled already by the parent (break collapsing, see above)
- }
-
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- } else {
- curLM = (LayoutManager) lmStack.pop();
- setCurrentChildLM(curLM);
-
- childLC.copyPendingMarksFrom(context);
- childLC.setStackLimitBP(context.getStackLimitBP().minus(stackLimit));
- childLC.setRefIPD(relDims.ipd);
- childLC.setWritingMode(getBlockContainerFO().getWritingMode());
- if (curLM == this.childLMs.get(0)) {
- childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
- //Handled already by the parent (break collapsing, see above)
- }
-
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment, lmStack,
- restartPosition, restartAtLM);
- }
- if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
- //Propagate keep-with-previous up from the first child
- context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- childLC.clearKeepWithPreviousPending();
- }
- if (returnedList.size() == 1
- && ElementListUtils.startsWithForcedBreak(returnedList)) {
- // a descendant of this block has break-before
- contentList.addAll(returnedList);
-
- // "wrap" the Position inside each element
- // moving the elements from contentList to returnList
- wrapPositionElements(contentList, returnList);
-
- return returnList;
- } else {
- if (prevLM != null) {
- // there is a block handled by prevLM
- // before the one handled by curLM
- addInBetweenBreak(contentList, context, childLC);
- }
- contentList.addAll(returnedList);
- if (!returnedList.isEmpty()) {
- if (ElementListUtils.endsWithForcedBreak(returnedList)) {
- // a descendant of this block has break-after
- if (curLM.isFinished() && !hasNextChildLM()) {
- // there is no other content in this block;
- // it's useless to add space after before a page break
- setFinished(true);
- }
-
- wrapPositionElements(contentList, returnList);
-
- return returnList;
- }
- }
- }
- }
-
- // propagate and clear
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
- childLC.clearKeepsPending();
- prevLM = curLM;
-
- while ((curLM = getChildLM()) != null) {
- curLM.reset();
- childLC = new LayoutContext(0);
- childLC.copyPendingMarksFrom(context);
- // curLM is a ?
- childLC.setStackLimitBP(context.getStackLimitBP().minus(stackLimit));
- childLC.setRefIPD(relDims.ipd);
- childLC.setWritingMode(getBlockContainerFO().getWritingMode());
- if (curLM == this.childLMs.get(0)) {
- childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
- //Handled already by the parent (break collapsing, see above)
- }
-
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
- //Propagate keep-with-previous up from the first child
- context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- childLC.clearKeepWithPreviousPending();
- }
- if (returnedList.size() == 1
- && ElementListUtils.startsWithForcedBreak(returnedList)) {
- // a descendant of this block has break-before
- contentList.addAll(returnedList);
-
- // "wrap" the Position inside each element
- // moving the elements from contentList to returnList
- wrapPositionElements(contentList, returnList);
-
- return returnList;
- } else {
- if (prevLM != null) {
- // there is a block handled by prevLM
- // before the one handled by curLM
- addInBetweenBreak(contentList, context, childLC);
- }
- contentList.addAll(returnedList);
- if (returnedList.isEmpty()) {
- //Avoid NoSuchElementException below (happens with empty blocks)
- continue;
- }
- if (ElementListUtils.endsWithForcedBreak(returnedList)) {
- // a descendant of this block has break-after
- if (curLM.isFinished() && !hasNextChildLM()) {
- // there is no other content in this block;
- // it's useless to add space after before a page break
- setFinished(true);
- }
-
- wrapPositionElements(contentList, returnList);
-
- return returnList;
- }
- }
- // propagate and clear
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
- childLC.clearKeepsPending();
- prevLM = curLM;
- }
-
- wrapPositionElements(contentList, returnList);
-
- } else {
- returnList.add(generateNonInlinedBox(contentRectOffsetX, contentRectOffsetY));
- }
- addKnuthElementsForBorderPaddingAfter(returnList, true);
- addKnuthElementsForSpaceAfter(returnList, alignment);
-
- //All child content is processed. Only break-after can occur now, so...
- context.clearPendingMarks();
- addKnuthElementsForBreakAfter(returnList, context);
-
- context.updateKeepWithNextPending(getKeepWithNext());
-
- setFinished(true);
- return returnList;
- }
-
- private KnuthBox generateNonInlinedBox(double contentRectOffsetX,
- double contentRectOffsetY) {
-
- MinOptMax range = MinOptMax.getInstance(relDims.ipd);
- BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
- breaker.doLayout(relDims.bpd, autoHeight);
- boolean contentOverflows = breaker.isOverflow();
- if (autoHeight) {
- //Update content BPD now that it is known
- int newHeight = breaker.deferredAlg.totalWidth;
- if (blockProgressionDirectionChanges()) {
- setContentAreaIPD(newHeight);
- } else {
- vpContentBPD = newHeight;
- }
- updateRelDims(contentRectOffsetX, contentRectOffsetY, false);
- }
-
- Position bcPosition = new BlockContainerPosition(this, breaker);
- KnuthBox knuthBox = new KnuthBox(vpContentBPD, notifyPos(bcPosition), false);
- //TODO Handle min/opt/max for block-progression-dimension
- /* These two elements will be used to add stretchability to the above box
- returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
- false, returnPosition, false));
- returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
- LINE_NUMBER_ADJUSTMENT, returnPosition, false));
- */
-
- if (contentOverflows) {
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- getBlockContainerFO().getUserAgent().getEventBroadcaster());
- boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
- eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
- breaker.getOverflowAmount(), needClip(), canRecover,
- getBlockContainerFO().getLocator());
- }
- return knuthBox;
- }
-
- private boolean blockProgressionDirectionChanges() {
- return getBlockContainerFO().getReferenceOrientation() % 180 != 0;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isRestartable() {
- return true;
- }
-
- private List getNextKnuthElementsAbsolute(LayoutContext context) {
- autoHeight = false;
-
- boolean bpDirectionChanges = blockProgressionDirectionChanges();
- Point offset = getAbsOffset();
- int allocBPD, allocIPD;
- if (height.getEnum() == EN_AUTO
- || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
- //auto height when height="auto" or "if that dimension is not specified explicitly
- //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1)
- allocBPD = 0;
- if (abProps.bottom.getEnum() != EN_AUTO) {
- int availHeight;
- if (isFixed()) {
- availHeight = (int)getCurrentPV().getViewArea().getHeight();
- } else {
- availHeight = context.getStackLimitBP().getOpt();
- }
- allocBPD = availHeight;
- allocBPD -= offset.y;
- if (abProps.bottom.getEnum() != EN_AUTO) {
- allocBPD -= abProps.bottom.getValue(this);
- if (allocBPD < 0) {
- //TODO Fix absolute b-c layout, layout may need to be defferred until
- //after page breaking when the size of the containing box is known.
- /* Warning disabled due to a interpretation mistake.
- * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
- log.error("The current combination of top and bottom properties results"
- + " in a negative extent for the block-container. 'bottom' may be"
- + " at most " + (allocBPD + abProps.bottom.getValue(this)) + " mpt,"
- + " but was actually " + abProps.bottom.getValue(this) + " mpt."
- + " The nominal available height is " + availHeight + " mpt.");
- */
- allocBPD = 0;
- }
- } else {
- if (allocBPD < 0) {
- /* Warning disabled due to a interpretation mistake.
- * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
- log.error("The current combination of top and bottom properties results"
- + " in a negative extent for the block-container. 'top' may be"
- + " at most " + availHeight + " mpt,"
- + " but was actually " + offset.y + " mpt."
- + " The nominal available height is " + availHeight + " mpt.");
- */
- allocBPD = 0;
- }
- }
- } else {
- allocBPD = context.getStackLimitBP().getOpt();
- if (!bpDirectionChanges) {
- autoHeight = true;
- }
- }
- } else {
- allocBPD = height.getValue(this); //this is the content-height
- allocBPD += getBPIndents();
- }
- if (width.getEnum() == EN_AUTO) {
- int availWidth;
- if (isFixed()) {
- availWidth = (int)getCurrentPV().getViewArea().getWidth();
- } else {
- availWidth = context.getRefIPD();
- }
- allocIPD = availWidth;
- if (abProps.left.getEnum() != EN_AUTO) {
- allocIPD -= abProps.left.getValue(this);
- }
- if (abProps.right.getEnum() != EN_AUTO) {
- allocIPD -= abProps.right.getValue(this);
- if (allocIPD < 0) {
- /* Warning disabled due to a interpretation mistake.
- * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
- log.error("The current combination of left and right properties results"
- + " in a negative extent for the block-container. 'right' may be"
- + " at most " + (allocIPD + abProps.right.getValue(this)) + " mpt,"
- + " but was actually " + abProps.right.getValue(this) + " mpt."
- + " The nominal available width is " + availWidth + " mpt.");
- */
- allocIPD = 0;
- }
- } else {
- if (allocIPD < 0) {
- /* Warning disabled due to a interpretation mistake.
- * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
- log.error("The current combination of left and right properties results"
- + " in a negative extent for the block-container. 'left' may be"
- + " at most " + allocIPD + " mpt,"
- + " but was actually " + abProps.left.getValue(this) + " mpt."
- + " The nominal available width is " + availWidth + " mpt.");
- */
- allocIPD = 0;
- }
- if (bpDirectionChanges) {
- autoHeight = true;
- }
- }
- } else {
- allocIPD = width.getValue(this); //this is the content-width
- allocIPD += getIPIndents();
- }
-
- vpContentBPD = allocBPD - getBPIndents();
- setContentAreaIPD(allocIPD - getIPIndents());
-
- updateRelDims(0, 0, autoHeight);
-
- MinOptMax range = MinOptMax.getInstance(relDims.ipd);
- BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
- breaker.doLayout((autoHeight ? 0 : relDims.bpd), autoHeight);
- boolean contentOverflows = breaker.isOverflow();
- if (autoHeight) {
- //Update content BPD now that it is known
- int newHeight = breaker.deferredAlg.totalWidth;
- if (bpDirectionChanges) {
- setContentAreaIPD(newHeight);
- } else {
- vpContentBPD = newHeight;
- }
- updateRelDims(0, 0, false);
- }
- List returnList = new LinkedList();
- if (!breaker.isEmpty()) {
- Position bcPosition = new BlockContainerPosition(this, breaker);
- returnList.add(new KnuthBox(0, notifyPos(bcPosition), false));
-
- //TODO Maybe check for page overflow when autoHeight=true
- if (!autoHeight & (contentOverflows)) {
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- getBlockContainerFO().getUserAgent().getEventBroadcaster());
- boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
- eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
- breaker.getOverflowAmount(), needClip(), canRecover,
- getBlockContainerFO().getLocator());
- }
- }
-
- setFinished(true);
- return returnList;
- }
-
- private void updateRelDims(double xOffset, double yOffset, boolean skipAutoHeight) {
- Rectangle2D rect = new Rectangle2D.Double(
- xOffset, yOffset,
- getContentAreaIPD(),
- this.vpContentBPD);
- relDims = new FODimension(0, 0);
- absoluteCTM = CTM.getCTMandRelDims(
- getBlockContainerFO().getReferenceOrientation(),
- getBlockContainerFO().getWritingMode(),
- rect, relDims);
- }
-
- private class BlockContainerPosition extends NonLeafPosition {
-
- private BlockContainerBreaker breaker;
-
- public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
- super(lm, null);
- this.breaker = breaker;
- }
-
- public BlockContainerBreaker getBreaker() {
- return this.breaker;
- }
-
- }
-
- private class BlockContainerBreaker extends AbstractBreaker {
-
- private BlockContainerLayoutManager bclm;
- private MinOptMax ipd;
-
- //Info for deferred adding of areas
- private PageBreakingAlgorithm deferredAlg;
- private BlockSequence deferredOriginalList;
- private BlockSequence deferredEffectiveList;
-
- public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
- this.bclm = bclm;
- this.ipd = ipd;
- }
-
- /** {@inheritDoc} */
- protected void observeElementList(List elementList) {
- ElementListObserver.observe(elementList, "block-container",
- bclm.getBlockContainerFO().getId());
- }
-
- /** {@inheritDoc} */
- protected boolean isPartOverflowRecoveryActivated() {
- //For block-containers, this must be disabled because of wanted overflow.
- return false;
- }
-
- /** {@inheritDoc} */
- protected boolean isSinglePartFavored() {
- return true;
- }
-
- public int getDifferenceOfFirstPart() {
- PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
- return pbp.difference;
- }
-
- public boolean isOverflow() {
- return !isEmpty()
- && ((deferredAlg.getPageBreaks().size() > 1)
- || (deferredAlg.totalWidth - deferredAlg.totalShrink)
- > deferredAlg.getLineWidth());
- }
-
- public int getOverflowAmount() {
- return (deferredAlg.totalWidth - deferredAlg.totalShrink)
- - deferredAlg.getLineWidth();
- }
-
- protected LayoutManager getTopLevelLM() {
- return bclm;
- }
-
- protected LayoutContext createLayoutContext() {
- LayoutContext lc = super.createLayoutContext();
- lc.setRefIPD(ipd.getOpt());
- lc.setWritingMode(getBlockContainerFO().getWritingMode());
- return lc;
- }
-
- protected List getNextKnuthElements(LayoutContext context, int alignment) {
- LayoutManager curLM; // currently active LM
- List returnList = new LinkedList();
-
- while ((curLM = getChildLM()) != null) {
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setRefIPD(context.getRefIPD());
- childLC.setWritingMode(getBlockContainerFO().getWritingMode());
-
- List returnedList = null;
- if (!curLM.isFinished()) {
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- }
- if (returnedList != null) {
- bclm.wrapPositionElements(returnedList, returnList);
- }
- }
- SpaceResolver.resolveElementList(returnList);
- setFinished(true);
- return returnList;
- }
-
- protected int getCurrentDisplayAlign() {
- return getBlockContainerFO().getDisplayAlign();
- }
-
- protected boolean hasMoreContent() {
- return !isFinished();
- }
-
- protected void addAreas(PositionIterator posIter, LayoutContext context) {
- AreaAdditionUtil.addAreas(bclm, posIter, context);
- }
-
- protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList, BlockSequence effectiveList) {
- //Defer adding of areas until addAreas is called by the parent LM
- this.deferredAlg = alg;
- this.deferredOriginalList = originalList;
- this.deferredEffectiveList = effectiveList;
- }
-
- protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
- //nop for bclm
- }
-
- protected LayoutManager getCurrentChildLM() {
- return curChildLM;
- }
-
- public void addContainedAreas() {
- if (isEmpty()) {
- return;
- }
- //Rendering all parts (not just the first) at once for the case where the parts that
- //overflow should be visible.
- this.deferredAlg.removeAllPageBreaks();
- this.addAreas(this.deferredAlg,
- this.deferredAlg.getPageBreaks().size(),
- this.deferredOriginalList, this.deferredEffectiveList);
- }
-
- }
-
- private Point getAbsOffset() {
- int x = 0;
- int y = 0;
- if (abProps.left.getEnum() != EN_AUTO) {
- x = abProps.left.getValue(this);
- } else if (abProps.right.getEnum() != EN_AUTO
- && width.getEnum() != EN_AUTO) {
- x = getReferenceAreaIPD()
- - abProps.right.getValue(this) - width.getValue(this);
- }
- if (abProps.top.getEnum() != EN_AUTO) {
- y = abProps.top.getValue(this);
- } else if (abProps.bottom.getEnum() != EN_AUTO
- && height.getEnum() != EN_AUTO) {
- y = getReferenceAreaBPD()
- - abProps.bottom.getValue(this) - height.getValue(this);
- }
- return new Point(x, y);
- }
-
- /** {@inheritDoc} */
- @Override
- public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
- getParentArea(null);
-
- // if this will create the first block area in a page
- // and display-align is bottom or center, add space before
- if (layoutContext.getSpaceBefore() > 0) {
- addBlockSpacing(0.0, MinOptMax.getInstance(layoutContext.getSpaceBefore()));
- }
-
- LayoutManager childLM;
- LayoutManager lastLM = null;
- LayoutContext lc = new LayoutContext(0);
- lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
- // set space after in the LayoutContext for children
- if (layoutContext.getSpaceAfter() > 0) {
- lc.setSpaceAfter(layoutContext.getSpaceAfter());
- }
- BlockContainerPosition bcpos = null;
- PositionIterator childPosIter;
-
- // "unwrap" the NonLeafPositions stored in parentIter
- // and put them in a new list;
- List<Position> positionList = new LinkedList<Position>();
- Position pos;
- Position firstPos = null;
- Position lastPos = null;
- while (parentIter.hasNext()) {
- pos = parentIter.next();
- if (pos.getIndex() >= 0) {
- if (firstPos == null) {
- firstPos = pos;
- }
- lastPos = pos;
- }
- Position innerPosition = pos;
- if (pos instanceof NonLeafPosition) {
- innerPosition = pos.getPosition();
- }
- if (pos instanceof BlockContainerPosition) {
- if (bcpos != null) {
- throw new IllegalStateException("Only one BlockContainerPosition allowed");
- }
- bcpos = (BlockContainerPosition)pos;
- //Add child areas inside the reference area
- //bcpos.getBreaker().addContainedAreas();
- } else if (innerPosition == null) {
- //ignore (probably a Position for a simple penalty between blocks)
- } else if (innerPosition.getLM() == this
- && !(innerPosition instanceof MappingPosition)) {
- // pos was created by this BlockLM and was inside a penalty
- // allowing or forbidding a page break
- // nothing to do
- } else {
- // innerPosition was created by another LM
- positionList.add(innerPosition);
- lastLM = innerPosition.getLM();
- }
- }
-
- addId();
-
- addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
-
- if (bcpos == null) {
- // the Positions in positionList were inside the elements
- // created by the LineLM
- childPosIter = new StackingIter(positionList.listIterator());
-
- while ((childLM = childPosIter.getNextChildLM()) != null) {
- // set last area flag
- lc.setFlags(LayoutContext.LAST_AREA,
- (layoutContext.isLastArea() && childLM == lastLM));
- lc.setStackLimitBP(layoutContext.getStackLimitBP());
- // Add the line areas to Area
- childLM.addAreas(childPosIter, lc);
- }
- } else {
- //Add child areas inside the reference area
- bcpos.getBreaker().addContainedAreas();
- }
-
- addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
-
- TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
- effSpaceBefore, effSpaceAfter);
- flush();
-
- viewportBlockArea = null;
- referenceArea = null;
- resetSpaces();
-
- notifyEndOfLayout();
- }
-
- /**
- * Get the parent area for children of this block container.
- * This returns the current block container area
- * and creates it if required.
- *
- * {@inheritDoc}
- */
- @Override
- public Area getParentArea(Area childArea) {
- if (referenceArea == null) {
- boolean switchedProgressionDirection = blockProgressionDirectionChanges();
- boolean allowBPDUpdate = autoHeight && !switchedProgressionDirection;
-
- viewportBlockArea = new BlockViewport(allowBPDUpdate);
- viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE);
-
- viewportBlockArea.setIPD(getContentAreaIPD());
- if (allowBPDUpdate) {
- viewportBlockArea.setBPD(0);
- } else {
- viewportBlockArea.setBPD(this.vpContentBPD);
- }
- transferForeignAttributes(viewportBlockArea);
-
- TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
- TraitSetter.addBorders(viewportBlockArea,
- getBlockContainerFO().getCommonBorderPaddingBackground(),
- discardBorderBefore, discardBorderAfter, false, false, this);
- TraitSetter.addPadding(viewportBlockArea,
- getBlockContainerFO().getCommonBorderPaddingBackground(),
- discardPaddingBefore, discardPaddingAfter, false, false, this);
- TraitSetter.addMargins(viewportBlockArea,
- getBlockContainerFO().getCommonBorderPaddingBackground(),
- startIndent, endIndent,
- this);
-
- viewportBlockArea.setCTM(absoluteCTM);
- viewportBlockArea.setClip(needClip());
-
- if (abProps.absolutePosition == EN_ABSOLUTE
- || abProps.absolutePosition == EN_FIXED) {
- Point offset = getAbsOffset();
- viewportBlockArea.setXOffset(offset.x);
- viewportBlockArea.setYOffset(offset.y);
- } else {
- //nop
- }
-
- referenceArea = new Block();
- referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
- TraitSetter.setProducerID(referenceArea, getBlockContainerFO().getId());
-
- if (abProps.absolutePosition == EN_ABSOLUTE) {
- viewportBlockArea.setPositioning(Block.ABSOLUTE);
- } else if (abProps.absolutePosition == EN_FIXED) {
- viewportBlockArea.setPositioning(Block.FIXED);
- }
-
- // Set up dimensions
- // Must get dimensions from parent area
- /*Area parentArea =*/ parentLayoutManager.getParentArea(referenceArea);
- //int referenceIPD = parentArea.getIPD();
- referenceArea.setIPD(relDims.ipd);
- // Get reference IPD from parentArea
- setCurrentArea(viewportBlockArea); // ??? for generic operations
- }
- return referenceArea;
- }
-
- /**
- * Add the child to the block container.
- *
- * {@inheritDoc}
- */
- @Override
- public void addChildArea(Area childArea) {
- if (referenceArea != null) {
- referenceArea.addBlock((Block) childArea);
- }
- }
-
- /**
- * Force current area to be added to parent area.
- * {@inheritDoc}
- */
- @Override
- protected void flush() {
- viewportBlockArea.addBlock(referenceArea, autoHeight);
-
- TraitSetter.addBackground(viewportBlockArea,
- getBlockContainerFO().getCommonBorderPaddingBackground(),
- this);
-
- super.flush();
- }
-
- /** {@inheritDoc} */
- @Override
- public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
- // TODO Auto-generated method stub
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override
- public void discardSpace(KnuthGlue spaceGlue) {
- // TODO Auto-generated method stub
- }
-
- /** {@inheritDoc} */
- @Override
- public KeepProperty getKeepTogetherProperty() {
- return getBlockContainerFO().getKeepTogether();
- }
-
- /** {@inheritDoc} */
- @Override
- public KeepProperty getKeepWithPreviousProperty() {
- return getBlockContainerFO().getKeepWithPrevious();
- }
-
- /** {@inheritDoc} */
- @Override
- public KeepProperty getKeepWithNextProperty() {
- return getBlockContainerFO().getKeepWithNext();
- }
-
- /**
- * @return the BlockContainer node
- */
- protected BlockContainer getBlockContainerFO() {
- return (BlockContainer) fobj;
- }
-
- // --------- Property Resolution related functions --------- //
-
- /** {@inheritDoc} */
- @Override
- public boolean getGeneratesReferenceArea() {
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean getGeneratesBlockArea() {
- return true;
- }
-
- /** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
- }
-
|