1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258 |
- /*
- * 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.util.LinkedList;
- import java.util.List;
- import java.util.ListIterator;
- 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.BlockParent;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.FObj;
- import org.apache.fop.fo.properties.BreakPropertySet;
- import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
- import org.apache.fop.fo.properties.KeepProperty;
- import org.apache.fop.fo.properties.SpaceProperty;
- import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
- import org.apache.fop.traits.MinOptMax;
- import org.apache.fop.util.BreakUtil;
- import org.apache.fop.util.ListUtil;
-
- /**
- * Base LayoutManager class for all areas which stack their child
- * areas in the block-progression direction, such as Flow, Block, ListBlock.
- */
- public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
- implements BlockLevelLayoutManager {
-
- /** logging instance */
- private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class);
-
- /** parent area */
- protected BlockParent parentArea;
-
- /** Value of the block-progression-unit (non-standard property) */
- protected int bpUnit;
- /** space-before value adjusted for block-progression-unit handling */
- protected int adjustedSpaceBefore;
- /** space-after value adjusted for block-progression-unit handling */
- protected int adjustedSpaceAfter;
- /** Only used to store the original list when createUnitElements is called */
- protected List<KnuthElement> storedList;
- /** Indicates whether break before has been served or not */
- protected boolean breakBeforeServed;
- /** Indicates whether the first visible mark has been returned by this LM, yet */
- protected boolean firstVisibleMarkServed;
- /** Reference IPD available */
- protected int referenceIPD;
- /** the effective start-indent value */
- protected int startIndent;
- /** the effective end-indent value */
- protected int endIndent;
- /**
- * 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.
- */
- protected MinOptMax foSpaceBefore;
- /** see foSpaceBefore */
- protected MinOptMax foSpaceAfter;
-
- private Position auxiliaryPosition;
-
- private int contentAreaIPD;
-
- /**
- * @param node the fo this LM deals with
- */
- public BlockStackingLayoutManager(FObj node) {
- super(node);
- setGeneratesBlockArea(true);
- }
-
- /**
- * @return current area being filled
- */
- protected BlockParent getCurrentArea() {
- return this.parentArea;
- }
-
-
- /**
- * Set the current area being filled.
- * @param parentArea the current area to be filled
- */
- protected void setCurrentArea(BlockParent parentArea) {
- this.parentArea = parentArea;
- }
-
- /**
- * Add a block spacer for space before and space after a block.
- * This adds an empty Block area that acts as a block space.
- *
- * @param adjust the adjustment value
- * @param minoptmax the min/opt/max value of the spacing
- */
- public void addBlockSpacing(double adjust, MinOptMax minoptmax) {
- int sp = TraitSetter.getEffectiveSpace(adjust, minoptmax);
- if (sp != 0) {
- Block spacer = new Block();
- spacer.setBPD(sp);
- parentLayoutManager.addChildArea(spacer);
- }
- }
-
- /**
- * Add the childArea to the passed area.
- * Called by child LayoutManager when it has filled one of its areas.
- * The LM should already have an Area in which to put the child.
- * See if the area will fit in the current area.
- * If so, add it. Otherwise initiate breaking.
- * @param childArea the area to add: will be some block-stacked Area.
- * @param parentArea the area in which to add the childArea
- */
- protected void addChildToArea(Area childArea,
- BlockParent parentArea) {
- // This should be a block-level Area (Block in the generic sense)
- if (!(childArea instanceof Block)) {
- //log.error("Child not a Block in BlockStackingLM!");
- }
-
- parentArea.addBlock((Block) childArea);
- flush(); // hand off current area to parent
- }
-
-
- /**
- * Add the childArea to the current area.
- * Called by child LayoutManager when it has filled one of its areas.
- * The LM should already have an Area in which to put the child.
- * See if the area will fit in the current area.
- * If so, add it. Otherwise initiate breaking.
- * @param childArea the area to add: will be some block-stacked Area.
- */
- @Override
- public void addChildArea(Area childArea) {
- addChildToArea(childArea, getCurrentArea());
- }
-
- /**
- * Force current area to be added to parent area.
- */
- protected void flush() {
- if (getCurrentArea() != null) {
- parentLayoutManager.addChildArea(getCurrentArea());
- }
- }
-
- /** @return a cached auxiliary Position instance used for things like spaces. */
- protected Position getAuxiliaryPosition() {
- if (this.auxiliaryPosition == null) {
- this.auxiliaryPosition = new NonLeafPosition(this, null);
- }
- return this.auxiliaryPosition;
- }
-
- /**
- * @param len length in millipoints to span with bp units
- * @return the minimum integer n such that n * bpUnit >= len
- */
- protected int neededUnits(int len) {
- return (int) Math.ceil((float)len / bpUnit);
- }
-
- /**
- * Determines and sets the content area IPD based on available reference area IPD, start- and
- * end-indent properties.
- * end-indent is adjusted based on overconstrained geometry rules, if necessary.
- *
- * @return the resulting content area IPD
- */
- protected int updateContentAreaIPDwithOverconstrainedAdjust() {
- int ipd = referenceIPD - (startIndent + endIndent);
- if (ipd < 0) {
- //5.3.4, XSL 1.0, Overconstrained Geometry
- log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- getFObj().getUserAgent().getEventBroadcaster());
- eventProducer.overconstrainedAdjustEndIndent(this,
- getFObj().getName(), ipd, getFObj().getLocator());
- endIndent += ipd;
- ipd = 0;
- //TODO Should we skip layout for a block that has ipd=0?
- }
- setContentAreaIPD(ipd);
- return ipd;
- }
-
- /**
- * Sets the content area IPD by directly supplying the value.
- * end-indent is adjusted based on overconstrained geometry rules, if necessary.
- * @param contentIPD the IPD of the content
- * @return the resulting content area IPD
- */
- protected int updateContentAreaIPDwithOverconstrainedAdjust(int contentIPD) {
- int ipd = referenceIPD - (contentIPD + (startIndent + endIndent));
- if (ipd < 0) {
- //5.3.4, XSL 1.0, Overconstrained Geometry
- log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- getFObj().getUserAgent().getEventBroadcaster());
- eventProducer.overconstrainedAdjustEndIndent(this,
- getFObj().getName(), ipd, getFObj().getLocator());
- endIndent += ipd;
- }
- setContentAreaIPD(contentIPD);
- return contentIPD;
- }
-
- /** {@inheritDoc} */
- @Override
- public List getNextKnuthElements(LayoutContext context, int alignment) {
- return getNextKnuthElements(context, alignment, null, null, null);
- }
-
- /** {@inheritDoc} */
- @Override
- public List getNextKnuthElements(LayoutContext context, int alignment,
- Stack lmStack, Position restartPosition, LayoutManager restartAtLM) {
- referenceIPD = context.getRefIPD();
- updateContentAreaIPDwithOverconstrainedAdjust();
-
- boolean isRestart = (lmStack != null);
- boolean emptyStack = (!isRestart || lmStack.isEmpty());
- List<ListElement> contentList = new LinkedList<ListElement>();
- List<ListElement> elements = new LinkedList<ListElement>();
-
- if (!breakBeforeServed(context, elements)) {
- // if this FO has break-before specified, and it
- // has not yet been processed, return now
- return elements;
- }
-
- addFirstVisibleMarks(elements, context, alignment);
-
- //Used to indicate a special break-after case when all content has already been generated.
- BreakElement forcedBreakAfterLast = null;
-
- LayoutContext childLC;
- List<ListElement> childElements;
- LayoutManager currentChildLM;
- // always reset in case of a restart (exception: see below)
- boolean doReset = isRestart;
- if (isRestart) {
- if (emptyStack) {
- assert restartAtLM != null && restartAtLM.getParent() == this;
- currentChildLM = restartAtLM;
- } else {
- currentChildLM = (LayoutManager) lmStack.pop();
- // make sure the initial child LM is not reset
- doReset = false;
- }
- setCurrentChildLM(currentChildLM);
- } else {
- currentChildLM = getChildLM();
- }
-
- while (currentChildLM != null) {
- if (doReset) {
- currentChildLM.reset(); // TODO won't work with forced breaks
- }
-
- childLC = makeChildLayoutContext(context);
-
- if (!isRestart || emptyStack) {
- childElements = getNextChildElements(currentChildLM, context, childLC, alignment,
- null, null, null);
- } else {
- // restart && non-empty LM stack
- childElements = getNextChildElements(currentChildLM, context, childLC, alignment,
- lmStack, restartPosition, restartAtLM);
- // once encountered, irrelevant for following child LMs
- emptyStack = true;
- // force reset as of the next child
- doReset = true;
- }
-
- if (contentList.isEmpty()) {
- // propagate keep-with-previous up from the first child
- context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- }
-
- // handle non-empty child
- if (childElements != null && !childElements.isEmpty()) {
- if (!contentList.isEmpty()
- && !ElementListUtils.startsWithForcedBreak(childElements)) {
- // there is a block handled by prevLM before the one
- // handled by curLM, and the one handled
- // by the current LM does not begin with a break
- addInBetweenBreak(contentList, context, childLC);
- }
- if (childElements.size() == 1
- && ElementListUtils.startsWithForcedBreak(childElements)) {
- // a descendant of this block has break-before
- if (currentChildLM.isFinished() && !hasNextChildLM()) {
- // if there is no more content, make sure pending
- // marks are cleared
- forcedBreakAfterLast = (BreakElement) childElements.get(0);
- context.clearPendingMarks();
- // break without adding the child elements
- break;
- }
- if (contentList.isEmpty()) {
- // empty fo:block: zero-length box makes sure the IDs and/or markers
- // are registered and borders/padding are painted.
- elements.add(makeAuxiliaryZeroWidthBox());
- }
- // add the forced break
- contentList.addAll(childElements);
- // wrap position and return
- wrapPositionElements(contentList, elements);
- return elements;
- } else {
- // add all accumulated child elements
- contentList.addAll(childElements);
- if (ElementListUtils.endsWithForcedBreak(childElements)) {
- // a descendant of this block has break-after
- if (currentChildLM.isFinished() && !hasNextChildLM()) {
- // if there is no more content, make sure any
- // pending marks are cleared
- forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
- context.clearPendingMarks();
- break;
- }
- //wrap positions and return
- wrapPositionElements(contentList, elements);
- return elements;
- }
- }
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
- }
- currentChildLM = getChildLM();
- }
-
- if (contentList.isEmpty()) {
- if (forcedBreakAfterLast == null) {
- // empty fo:block: zero-length box makes sure the IDs and/or markers
- // are registered.
- elements.add(makeAuxiliaryZeroWidthBox());
- }
- } else {
- // wrap child positions
- wrapPositionElements(contentList, elements);
- }
-
- addLastVisibleMarks(elements, context, alignment);
-
- if (forcedBreakAfterLast == null) {
- addKnuthElementsForBreakAfter(elements, context);
- } else {
- forcedBreakAfterLast.clearPendingMarks();
- elements.add(forcedBreakAfterLast);
- }
-
- context.updateKeepWithNextPending(getKeepWithNext());
- setFinished(true);
- return elements;
- }
-
- /**
- * Creates and initializes a {@link LayoutContext} to pass to the child LM
- * @param context the parent {@link LayoutContext}
- * @return a new child layout context
- */
- protected LayoutContext makeChildLayoutContext(LayoutContext context) {
- LayoutContext childLC = new LayoutContext(0);
- childLC.copyPendingMarksFrom(context);
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setRefIPD(referenceIPD);
- return childLC;
- }
-
- /**
- * Checks if this LM's first "visible marks" (= borders, padding, spaces) have
- * already been processed, and if necessary, adds corresponding elements to
- * the specified list, and updates the given layout context accordingly.
- * @param elements the element list
- * @param context the layout context
- * @param alignment the vertical alignment
- */
- protected void addFirstVisibleMarks(List<ListElement> elements,
- LayoutContext context, int alignment) {
- if (!firstVisibleMarkServed) {
- addKnuthElementsForSpaceBefore(elements, alignment);
- context.updateKeepWithPreviousPending(getKeepWithPrevious());
- }
- addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
- firstVisibleMarkServed = true;
-
- //Spaces, border and padding to be repeated at each break
- addPendingMarks(context);
- }
-
- /**
- * Adds elements the LM's last/closing marks to the specified list, and
- * updates the layout context accordingly.
- * @param elements the element list
- * @param context the layout context
- * @param alignment the vertical alignment
- */
- protected void addLastVisibleMarks(List<ListElement> elements,
- LayoutContext context, int alignment) {
- addKnuthElementsForBorderPaddingAfter(elements, true);
- addKnuthElementsForSpaceAfter(elements, alignment);
-
- // All child content processed. Only break-after can occur now, so...
- context.clearPendingMarks();
- }
-
- /**
- * Check whether there is a break-before condition. If so, and
- * the specified {@code context} allows it, add the necessary elements
- * to the given {@code elements} list.
- * @param context the layout context
- * @param elements the element list
- * @return {@code false} if there is a break-before condition, and it has not been served;
- * {@code true} otherwise
- */
- protected boolean breakBeforeServed(LayoutContext context, List<ListElement> elements) {
- if (!breakBeforeServed) {
- breakBeforeServed = true;
- if (!context.suppressBreakBefore()) {
- if (addKnuthElementsForBreakBefore(elements, context)) {
- return false;
- }
- }
- }
- return breakBeforeServed;
- }
-
- private KnuthBox makeZeroWidthBox() {
- return new KnuthBox(0, new NonLeafPosition(this, null), false);
- }
-
- private KnuthBox makeAuxiliaryZeroWidthBox() {
- return new KnuthBox(0, notifyPos(new Position(this)), true);
- }
-
- private KnuthPenalty makeZeroWidthPenalty(int penaltyValue) {
- return new KnuthPenalty(0, penaltyValue, false, new NonLeafPosition(this, null), false);
- }
-
- private KnuthGlue makeSpaceAdjustmentGlue(int width, Adjustment adjustmentClass,
- boolean isAuxiliary) {
- return new KnuthGlue(width, 0, 0,
- adjustmentClass,
- new NonLeafPosition(this, null),
- isAuxiliary);
- }
-
- /**
- * Gets the next set of child elements for the given childLM.
- * The default implementation basically copies the pending marks to the child layout context,
- * and subsequently calls the appropriate variant of {@code childLM.getNextKnuthElements()},
- * passing it all relevant parameters.
- * @param childLM the current child LM
- * @param context the layout context
- * @param childLC the child layout context
- * @param alignment the vertical alignment
- * @param lmStack the stack of currently active LMs (if any)
- * @param restartPosition the position to restart from (if any)
- * @param restartAtLM the LM to restart from (if any)
- * @return list of elements corresponding to the content generated by childLM
- */
- protected List<ListElement> getNextChildElements(LayoutManager childLM, LayoutContext context,
- LayoutContext childLC, int alignment, Stack<LayoutManager> lmStack,
- Position restartPosition, LayoutManager restartAtLM) {
-
- if (childLM == this.childLMs.get(0)) {
- childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
- //Handled already by the parent (break collapsing, see above)
- }
-
- if (lmStack == null) {
- // route to default implementation, in case childLM does not provide
- // an override similar to this class
- return childLM.getNextKnuthElements(childLC, alignment);
- } else {
- return childLM.getNextKnuthElements(childLC, alignment, lmStack,
- restartPosition, restartAtLM);
- }
- }
-
- /**
- * Adds a break element to the content list between individual child elements.
- * @param contentList the content list
- * @param parentLC the parent layout context
- * @param childLC the currently active child layout context
- */
- protected void addInBetweenBreak(List<ListElement> contentList, LayoutContext parentLC,
- LayoutContext childLC) {
-
- if (mustKeepTogether()
- || parentLC.isKeepWithNextPending()
- || childLC.isKeepWithPreviousPending()) {
-
- Keep keep = getKeepTogether();
-
- //Handle pending keep-with-next
- keep = keep.compare(parentLC.getKeepWithNextPending());
- parentLC.clearKeepWithNextPending();
-
- //Handle pending keep-with-previous from child LM
- keep = keep.compare(childLC.getKeepWithPreviousPending());
- childLC.clearKeepWithPreviousPending();
-
- // add a penalty to forbid or discourage a break between blocks
- contentList.add(new BreakElement(
- new Position(this), keep.getPenalty(),
- keep.getContext(), parentLC));
- return;
- }
-
- ListElement last = ListUtil.getLast(contentList);
- if (last.isGlue()) {
- // the last element in contentList is a glue;
- // it is a feasible breakpoint, there is no need to add
- // a penalty
- log.warn("glue-type break possibility not handled properly, yet");
- //TODO Does this happen? If yes, need to deal with border and padding
- //at the break possibility
- } else if (!ElementListUtils.endsWithNonInfinitePenalty(contentList)) {
-
- // TODO vh: this is hacky
- // The getNextKnuthElements method of TableCellLM must not be called
- // twice, otherwise some settings like indents or borders will be
- // counted several times and lead to a wrong output. Anyway the
- // getNextKnuthElements methods should be called only once eventually
- // (i.e., when multi-threading the code), even when there are forced
- // breaks.
- // If we add a break possibility after a forced break the
- // AreaAdditionUtil.addAreas method will act on a sequence starting
- // with a SpaceResolver.SpaceHandlingBreakPosition element, having no
- // LM associated to it. Thus it will stop early instead of adding
- // areas for following Positions. The above test aims at preventing
- // such a situation from occurring. add a null penalty to allow a break
- // between blocks
-
- // add a null penalty to allow a break between blocks
- contentList.add(new BreakElement(
- new Position(this), 0, Constants.EN_AUTO, parentLC));
- }
- }
-
- /** {@inheritDoc} */
- public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
- assert (lastElement != null && lastElement.getPosition() != null);
- Position innerPosition = lastElement.getPosition().getPosition();
-
- if (innerPosition == null && lastElement.isGlue()) {
- // this adjustment applies to space-before or space-after of this block
- if (((KnuthGlue) lastElement).getAdjustmentClass()
- == Adjustment.SPACE_BEFORE_ADJUSTMENT) {
- // this adjustment applies to space-before
- adjustedSpaceBefore += adj;
- } else {
- // this adjustment applies to space-after
- adjustedSpaceAfter += adj;
- }
- return adj;
- } else if (innerPosition instanceof MappingPosition) {
- // this block has block-progression-unit > 0: the adjustment can concern
- // - the space-before or space-after of this block,
- // - the line number of a descendant of this block
- MappingPosition mappingPos = (MappingPosition)innerPosition;
- if (lastElement.isGlue()) {
- // lastElement is a glue
- ListIterator storedListIterator = storedList.listIterator(
- mappingPos.getFirstIndex());
- int newAdjustment = 0;
- while (storedListIterator.nextIndex() <= mappingPos.getLastIndex()) {
- KnuthElement storedElement = (KnuthElement)storedListIterator.next();
- if (storedElement.isGlue()) {
- newAdjustment += ((BlockLevelLayoutManager)storedElement
- .getLayoutManager()).negotiateBPDAdjustment(
- adj - newAdjustment, storedElement);
- }
- }
- newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
- : -bpUnit * neededUnits(-newAdjustment));
- return newAdjustment;
- } else {
- // lastElement is a penalty: this means that the paragraph
- // has been split between consecutive pages:
- // this may involve a change in the number of lines
- KnuthPenalty storedPenalty = (KnuthPenalty)
- storedList.get(mappingPos.getLastIndex());
- if (storedPenalty.getWidth() > 0) {
- // the original penalty has width > 0
- return ((BlockLevelLayoutManager)storedPenalty.getLayoutManager())
- .negotiateBPDAdjustment(storedPenalty.getWidth(),
- storedPenalty);
- } else {
- // the original penalty has width = 0
- // the adjustment involves only the spaces before and after
- return adj;
- }
- }
- } else if (innerPosition != null && innerPosition.getLM() != this) {
- // this adjustment concerns another LM
- NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
- lastElement.setPosition(innerPosition);
- int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
- .negotiateBPDAdjustment(adj, lastElement);
- lastElement.setPosition(savedPos);
- return returnValue;
- } else {
- // this should never happen
- log.error("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
- return 0;
- }
- }
-
- /** {@inheritDoc} */
- public void discardSpace(KnuthGlue spaceGlue) {
- assert (spaceGlue != null && spaceGlue.getPosition() != null);
- Position innerPosition = spaceGlue.getPosition().getPosition();
-
- if (innerPosition == null || innerPosition.getLM() == this) {
- // if this block has block-progression-unit > 0, innerPosition can be
- // a MappingPosition
- // spaceGlue represents space before or space after of this block
- if (spaceGlue.getAdjustmentClass() == Adjustment.SPACE_BEFORE_ADJUSTMENT) {
- // space-before must be discarded
- adjustedSpaceBefore = 0;
- foSpaceBefore = MinOptMax.ZERO;
- } else {
- // space-after must be discarded
- adjustedSpaceAfter = 0;
- foSpaceAfter = MinOptMax.ZERO;
- //TODO Why are both cases handled in the same way?
- }
- } else {
- // this element was not created by this BlockLM
- NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
- spaceGlue.setPosition(innerPosition);
- ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
- spaceGlue.setPosition(savedPos);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public List getChangedKnuthElements(List oldList, int alignment) {
- ListIterator<KnuthElement> oldListIterator = oldList.listIterator();
- KnuthElement currElement = null;
- KnuthElement prevElement = null;
- List<KnuthElement> returnedList = new LinkedList<KnuthElement>();
- List<KnuthElement> returnList = new LinkedList<KnuthElement>();
- int fromIndex = 0;
-
- // "unwrap" the Positions stored in the elements
- KnuthElement oldElement;
- while (oldListIterator.hasNext()) {
- oldElement = oldListIterator.next();
- assert oldElement.getPosition() != null;
- Position innerPosition = oldElement.getPosition().getPosition();
- if (innerPosition != null) {
- // oldElement was created by a descendant
- oldElement.setPosition(innerPosition);
- } else {
- // oldElement was created by this LM:
- // modify its position in order to recognize it was not created
- // by a child
- oldElement.setPosition(new Position(this));
- }
- }
-
- // create the iterator
- ListIterator<KnuthElement> workListIterator = oldList.listIterator();
- while (workListIterator.hasNext()) {
- currElement = workListIterator.next();
- if (prevElement != null
- && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
- // prevElement is the last element generated by the same LM
- BlockLevelLayoutManager prevLM
- = (BlockLevelLayoutManager)prevElement.getLayoutManager();
- BlockLevelLayoutManager currLM
- = (BlockLevelLayoutManager)currElement.getLayoutManager();
- boolean somethingAdded = false;
- if (prevLM != this) {
- returnedList.addAll(
- prevLM.getChangedKnuthElements(
- oldList.subList(fromIndex, workListIterator.previousIndex()),
- alignment));
- somethingAdded = true;
- } else {
- // do nothing
- }
- fromIndex = workListIterator.previousIndex();
-
- /*
- * TODO: why are KnuthPenalties added here,
- * while in getNextKE they were changed to BreakElements?
- */
- // there is another block after this one
- if (somethingAdded
- && (this.mustKeepTogether()
- || prevLM.mustKeepWithNext()
- || currLM.mustKeepWithPrevious())) {
- // add an infinite penalty to forbid a break between blocks
- returnedList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
- } else if (somethingAdded
- && !ListUtil.getLast(returnedList).isGlue()) {
- // add a null penalty to allow a break between blocks
- returnedList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
- }
- }
- prevElement = currElement;
- }
- if (currElement != null) {
- LayoutManager currLM = currElement.getLayoutManager();
- if (currLM != this) {
- returnedList.addAll(currLM.getChangedKnuthElements(
- oldList.subList(fromIndex, oldList.size()), alignment));
- } else {
- // there are no more elements to add
- // remove the last penalty added to returnedList
- if (!returnedList.isEmpty()) {
- ListUtil.removeLast(returnedList);
- }
- }
- }
-
- // append elements representing space-before
- boolean spaceBeforeIsConditional = true;
- if (fobj instanceof org.apache.fop.fo.flow.Block) {
- spaceBeforeIsConditional = getSpaceBeforeProperty().isDiscard();
- }
- if (adjustedSpaceBefore != 0) {
- if (!spaceBeforeIsConditional) {
- // add elements to prevent the glue to be discarded
- returnList.add(makeZeroWidthBox());
- returnList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
- }
-
- returnList.add(makeSpaceAdjustmentGlue(adjustedSpaceBefore,
- Adjustment.SPACE_BEFORE_ADJUSTMENT,
- false));
- }
-
- // "wrap" the Position stored in each element of returnedList
- // and add elements to returnList
- for (KnuthElement el : returnedList) {
- el.setPosition(new NonLeafPosition(this, el.getPosition()));
- returnList.add(el);
- }
-
- // append elements representing space-after
- boolean spaceAfterIsConditional = true;
- if (fobj instanceof org.apache.fop.fo.flow.Block) {
- spaceAfterIsConditional = getSpaceAfterProperty().isDiscard();
- }
- if (adjustedSpaceAfter != 0) {
- if (!spaceAfterIsConditional) {
- returnList.add(makeZeroWidthPenalty(KnuthPenalty.INFINITE));
- }
-
- returnList.add(makeSpaceAdjustmentGlue(adjustedSpaceAfter,
- Adjustment.SPACE_AFTER_ADJUSTMENT,
- spaceAfterIsConditional));
-
- if (!spaceAfterIsConditional) {
- returnList.add(makeZeroWidthBox());
- }
- }
-
- return returnList;
- }
-
- /**
- * Retrieves and returns the keep-together strength from the parent element.
- * @return the keep-together strength
- */
- protected Keep getParentKeepTogether() {
- Keep keep = Keep.KEEP_AUTO;
- if (getParent() instanceof BlockLevelLayoutManager) {
- keep = ((BlockLevelLayoutManager)getParent()).getKeepTogether();
- } else if (getParent() instanceof InlineLayoutManager) {
- if (((InlineLayoutManager) getParent()).mustKeepTogether()) {
- keep = Keep.KEEP_ALWAYS;
- }
- //TODO Fix me
- //strength = ((InlineLayoutManager) getParent()).getKeepTogetherStrength();
- }
- return keep;
- }
-
- /** {@inheritDoc} */
- public boolean mustKeepTogether() {
- return !getKeepTogether().isAuto();
- }
-
- /** {@inheritDoc} */
- public boolean mustKeepWithPrevious() {
- return !getKeepWithPrevious().isAuto();
- }
-
- /** {@inheritDoc} */
- public boolean mustKeepWithNext() {
- return !getKeepWithNext().isAuto();
- }
-
- /** {@inheritDoc} */
- public Keep getKeepTogether() {
- Keep keep = Keep.getKeep(getKeepTogetherProperty());
- keep = keep.compare(getParentKeepTogether());
- return keep;
- }
-
- /** {@inheritDoc} */
- public Keep getKeepWithPrevious() {
- return Keep.getKeep(getKeepWithPreviousProperty());
- }
-
- /** {@inheritDoc} */
- public Keep getKeepWithNext() {
- return Keep.getKeep(getKeepWithNextProperty());
- }
-
- /**
- * {@inheritDoc}
- * Default implementation throws a {@link IllegalStateException}.
- * Must be implemented by the subclass, if applicable.
- */
- public KeepProperty getKeepTogetherProperty() {
- throw new IllegalStateException();
- }
-
- /**
- * {@inheritDoc}
- * Default implementation throws a {@link IllegalStateException}.
- * Must be implemented by the subclass, if applicable.
- */
- public KeepProperty getKeepWithPreviousProperty() {
- throw new IllegalStateException();
- }
-
- /**
- * {@inheritDoc}
- * Default implementation throws a {@link IllegalStateException}.
- * Must be implemented by the subclass, if applicable.
- */
- public KeepProperty getKeepWithNextProperty() {
- throw new IllegalStateException();
- }
-
- /**
- * Adds the unresolved elements for border and padding to a layout context so break
- * possibilities can be properly constructed.
- * @param context the layout context
- */
- protected void addPendingMarks(LayoutContext context) {
- CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
- if (borderAndPadding != null) {
- if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
- context.addPendingBeforeMark(new BorderElement(
- getAuxiliaryPosition(),
- borderAndPadding.getBorderInfo(
- CommonBorderPaddingBackground.BEFORE).getWidth(),
- RelSide.BEFORE,
- false, false, this));
- }
- if (borderAndPadding.getPaddingBefore(false, this) > 0) {
- context.addPendingBeforeMark(new PaddingElement(
- getAuxiliaryPosition(),
- borderAndPadding.getPaddingLengthProperty(
- CommonBorderPaddingBackground.BEFORE),
- RelSide.BEFORE,
- false, false, this));
- }
- if (borderAndPadding.getBorderAfterWidth(false) > 0) {
- context.addPendingAfterMark(new BorderElement(
- getAuxiliaryPosition(),
- borderAndPadding.getBorderInfo(
- CommonBorderPaddingBackground.AFTER).getWidth(),
- RelSide.AFTER,
- false, false, this));
- }
- if (borderAndPadding.getPaddingAfter(false, this) > 0) {
- context.addPendingAfterMark(new PaddingElement(
- getAuxiliaryPosition(),
- borderAndPadding.getPaddingLengthProperty(
- CommonBorderPaddingBackground.AFTER),
- RelSide.AFTER,
- false, false, this));
- }
- }
- }
-
- /** @return the border, padding and background info structure */
- private CommonBorderPaddingBackground getBorderPaddingBackground() {
- if (fobj instanceof org.apache.fop.fo.flow.Block) {
- return ((org.apache.fop.fo.flow.Block)fobj)
- .getCommonBorderPaddingBackground();
- } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
- return ((org.apache.fop.fo.flow.BlockContainer)fobj)
- .getCommonBorderPaddingBackground();
- } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
- return ((org.apache.fop.fo.flow.ListBlock)fobj)
- .getCommonBorderPaddingBackground();
- } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
- return ((org.apache.fop.fo.flow.ListItem)fobj)
- .getCommonBorderPaddingBackground();
- } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
- return ((org.apache.fop.fo.flow.table.Table)fobj)
- .getCommonBorderPaddingBackground();
- } else {
- return null;
- }
- }
-
- /** @return the space-before property */
- protected SpaceProperty getSpaceBeforeProperty() {
- if (fobj instanceof org.apache.fop.fo.flow.Block) {
- return ((org.apache.fop.fo.flow.Block)fobj)
- .getCommonMarginBlock().spaceBefore;
- } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
- return ((org.apache.fop.fo.flow.BlockContainer)fobj)
- .getCommonMarginBlock().spaceBefore;
- } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
- return ((org.apache.fop.fo.flow.ListBlock)fobj)
- .getCommonMarginBlock().spaceBefore;
- } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
- return ((org.apache.fop.fo.flow.ListItem)fobj)
- .getCommonMarginBlock().spaceBefore;
- } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
- return ((org.apache.fop.fo.flow.table.Table)fobj)
- .getCommonMarginBlock().spaceBefore;
- } else {
- return null;
- }
- }
-
- /** @return the space-after property */
- protected SpaceProperty getSpaceAfterProperty() {
- if (fobj instanceof org.apache.fop.fo.flow.Block) {
- return ((org.apache.fop.fo.flow.Block)fobj)
- .getCommonMarginBlock().spaceAfter;
- } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
- return ((org.apache.fop.fo.flow.BlockContainer)fobj)
- .getCommonMarginBlock().spaceAfter;
- } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
- return ((org.apache.fop.fo.flow.ListBlock)fobj)
- .getCommonMarginBlock().spaceAfter;
- } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
- return ((org.apache.fop.fo.flow.ListItem)fobj)
- .getCommonMarginBlock().spaceAfter;
- } else if (fobj instanceof org.apache.fop.fo.flow.table.Table) {
- return ((org.apache.fop.fo.flow.table.Table)fobj)
- .getCommonMarginBlock().spaceAfter;
- } else {
- return null;
- }
- }
-
- /**
- * Creates Knuth elements for before border padding and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param isFirst true if this is the first time a layout manager instance needs to generate
- * border and padding
- */
- protected void addKnuthElementsForBorderPaddingBefore(List returnList, boolean isFirst) {
- //Border and Padding (before)
- CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
- if (borderAndPadding != null) {
- if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
- returnList.add(new BorderElement(
- getAuxiliaryPosition(),
- borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.BEFORE)
- .getWidth(),
- RelSide.BEFORE, isFirst, false, this));
- }
- if (borderAndPadding.getPaddingBefore(false, this) > 0) {
- returnList.add(new PaddingElement(
- getAuxiliaryPosition(),
- borderAndPadding.getPaddingLengthProperty(
- CommonBorderPaddingBackground.BEFORE),
- RelSide.BEFORE, isFirst, false, this));
- }
- }
- }
-
- /**
- * Creates Knuth elements for after border padding and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param isLast true if this is the last time a layout manager instance needs to generate
- * border and padding
- */
- protected void addKnuthElementsForBorderPaddingAfter(List returnList, boolean isLast) {
- //Border and Padding (after)
- CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
- if (borderAndPadding != null) {
- if (borderAndPadding.getPaddingAfter(false, this) > 0) {
- returnList.add(new PaddingElement(
- getAuxiliaryPosition(),
- borderAndPadding.getPaddingLengthProperty(
- CommonBorderPaddingBackground.AFTER),
- RelSide.AFTER, false, isLast, this));
- }
- if (borderAndPadding.getBorderAfterWidth(false) > 0) {
- returnList.add(new BorderElement(
- getAuxiliaryPosition(),
- borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.AFTER)
- .getWidth(),
- RelSide.AFTER, false, isLast, this));
- }
- }
- }
-
- /**
- * Creates Knuth elements for break-before and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param context the layout context
- * @return true if an element has been added due to a break-before.
- */
- protected boolean addKnuthElementsForBreakBefore(List returnList, LayoutContext context) {
- int breakBefore = getBreakBefore();
- if (breakBefore == EN_PAGE
- || breakBefore == EN_COLUMN
- || breakBefore == EN_EVEN_PAGE
- || breakBefore == EN_ODD_PAGE) {
- // return a penalty element, representing a forced page break
- returnList.add(new BreakElement(getAuxiliaryPosition(),
- 0, -KnuthElement.INFINITE, breakBefore, context));
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Returns the break-before value of the current formatting object.
- * @return the break-before value (Constants.EN_*)
- */
- private int getBreakBefore() {
- int breakBefore = EN_AUTO;
- if (fobj instanceof BreakPropertySet) {
- breakBefore = ((BreakPropertySet)fobj).getBreakBefore();
- }
- if (true /* uncomment to only partially merge: && breakBefore != EN_AUTO*/) {
- LayoutManager lm = getChildLM();
- //It is assumed this is only called when the first LM is active.
- if (lm instanceof BlockStackingLayoutManager) {
- BlockStackingLayoutManager bslm = (BlockStackingLayoutManager)lm;
- breakBefore = BreakUtil.compareBreakClasses(
- breakBefore, bslm.getBreakBefore());
- }
- }
- return breakBefore;
- }
-
- /**
- * Creates Knuth elements for break-after and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param context the layout context
- * @return true if an element has been added due to a break-after.
- */
- protected boolean addKnuthElementsForBreakAfter(List returnList, LayoutContext context) {
- int breakAfter = -1;
- if (fobj instanceof BreakPropertySet) {
- breakAfter = ((BreakPropertySet)fobj).getBreakAfter();
- }
- if (breakAfter == EN_PAGE
- || breakAfter == EN_COLUMN
- || breakAfter == EN_EVEN_PAGE
- || breakAfter == EN_ODD_PAGE) {
- // add a penalty element, representing a forced page break
- returnList.add(new BreakElement(getAuxiliaryPosition(),
- 0, -KnuthElement.INFINITE, breakAfter, context));
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Creates Knuth elements for space-before and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param alignment vertical alignment
- */
- protected void addKnuthElementsForSpaceBefore(List returnList, int alignment) {
- SpaceProperty spaceBefore = getSpaceBeforeProperty();
- // append elements representing space-before
- if (spaceBefore != null
- && !(spaceBefore.getMinimum(this).getLength().getValue(this) == 0
- && spaceBefore.getMaximum(this).getLength().getValue(this) == 0)) {
- returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceBefore,
- RelSide.BEFORE,
- true, false, this));
- }
- }
-
- /**
- * Creates Knuth elements for space-after and adds them to the return list.
- * @param returnList return list to add the additional elements to
- * @param alignment vertical alignment
- */
- protected void addKnuthElementsForSpaceAfter(List returnList, int alignment) {
- SpaceProperty spaceAfter = getSpaceAfterProperty();
- // append elements representing space-after
- if (spaceAfter != null
- && !(spaceAfter.getMinimum(this).getLength().getValue(this) == 0
- && spaceAfter.getMaximum(this).getLength().getValue(this) == 0)) {
- returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceAfter,
- RelSide.AFTER,
- false, true, this));
- }
- }
-
- /** A mapping position. */
- protected static class MappingPosition extends Position {
-
- private int firstIndex;
- private int lastIndex;
-
- /**
- * Construct mapping position.
- * @param lm layout manager
- * @param first position
- * @param last position
- */
- public MappingPosition(LayoutManager lm, int first, int last) {
- super(lm);
- firstIndex = first;
- lastIndex = last;
- }
-
- /** @return first index */
- public int getFirstIndex() {
- return firstIndex;
- }
-
- /** @return last index */
- public int getLastIndex() {
- return lastIndex;
- }
- }
-
- /**
- * "wrap" the Position inside each element moving the elements from
- * SourceList to targetList
- * @param sourceList source list
- * @param targetList target list receiving the wrapped position elements
- */
- protected void wrapPositionElements(List sourceList, List targetList) {
- wrapPositionElements(sourceList, targetList, false);
- }
-
- /**
- * "wrap" the Position inside each element moving the elements from
- * SourceList to targetList
- * @param sourceList source list
- * @param targetList target list receiving the wrapped position elements
- * @param force if true, every Position is wrapped regardless of its LM of origin
- */
- protected void wrapPositionElements(List sourceList, List targetList, boolean force) {
-
- ListIterator listIter = sourceList.listIterator();
- Object tempElement;
- while (listIter.hasNext()) {
- tempElement = listIter.next();
- if (tempElement instanceof ListElement) {
- wrapPositionElement(
- (ListElement) tempElement,
- targetList,
- force);
- } else if (tempElement instanceof List) {
- wrapPositionElements(
- (List) tempElement,
- targetList,
- force);
- }
- }
- }
-
- /**
- * "wrap" the Position inside the given element and add it to the target list.
- * @param el the list element
- * @param targetList target list receiving the wrapped position elements
- * @param force if true, every Position is wrapped regardless of its LM of origin
- */
- protected void wrapPositionElement(ListElement el, List targetList, boolean force) {
- if (force || el.getLayoutManager() != this) {
- el.setPosition(notifyPos(new NonLeafPosition(this, el.getPosition())));
- }
- targetList.add(el);
- }
-
-
- /** @return the sum of start-indent and end-indent */
- protected int getIPIndents() {
- return startIndent + endIndent;
- }
-
- /**
- * Returns the IPD of the content area
- * @return the IPD of the content area
- */
- @Override
- public int getContentAreaIPD() {
- return contentAreaIPD;
- }
-
- /**
- * Sets the IPD of the content area
- * @param contentAreaIPD the IPD of the content area
- */
- protected void setContentAreaIPD(int contentAreaIPD) {
- this.contentAreaIPD = contentAreaIPD;
- }
-
- /**
- * Returns the BPD of the content area
- * @return the BPD of the content area
- */
- @Override
- public int getContentAreaBPD() {
- return -1;
- }
-
- /** {@inheritDoc} */
- @Override
- public void reset() {
- super.reset();
- breakBeforeServed = false;
- firstVisibleMarkServed = false;
- // TODO startIndent, endIndent
- }
-
- }
|