Index: src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java (working copy) @@ -415,18 +415,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - return KEEP_AUTO; + public Keep getKeepTogether() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; } } Index: src/java/org/apache/fop/layoutmgr/KeepUtil.java =================================================================== --- src/java/org/apache/fop/layoutmgr/KeepUtil.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/KeepUtil.java (working copy) @@ -1,109 +0,0 @@ -/* - * 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 org.apache.fop.fo.Constants; -import org.apache.fop.fo.properties.KeepProperty; -import org.apache.fop.fo.properties.Property; - -/** - * Utility class for working with keeps. - */ -public class KeepUtil { - - /** - * Converts a keep property into an integer value. - *

- * Note: The conversion restricts the effectively available integer range by two values. - * Integer.MIN_VALUE is used to represent the value "auto" and - * Integer.MAX_VALUE is used to represebt the value "always". - * @param keep the keep property - * @return the keep value as an integer - */ - public static int getKeepStrength(Property keep) { - if (keep.isAuto()) { - return BlockLevelLayoutManager.KEEP_AUTO; - } else if (keep.getEnum() == Constants.EN_ALWAYS) { - return BlockLevelLayoutManager.KEEP_ALWAYS; - } else { - return keep.getNumber().intValue(); - } - } - - /** - * Returns the combined block-level keep strength from a keep property. - *

- * Note: This is a temporary method to be used until it is possible to differentiate between - * page and column keeps! - * @param keep the keep property - * @return the combined keep strength - */ - public static int getCombinedBlockLevelKeepStrength(KeepProperty keep) { - return Math.max( - getKeepStrength(keep.getWithinPage()), - getKeepStrength(keep.getWithinColumn())); - } - - /** - * Indicates whether a keep strength indicates a keep constraint. - * @param strength the keep strength - * @return true if the keep is not "auto" - */ - public static boolean hasKeep(int strength) { - return strength > BlockLevelLayoutManager.KEEP_AUTO; - } - - /** - * Returns the penalty value to be used for a certain keep strength. - *

- * @param keepStrength the keep strength - * @return the penalty value - */ - public static int getPenaltyForKeep(int keepStrength) { - if (keepStrength == BlockLevelLayoutManager.KEEP_AUTO) { - return 0; - } - int penalty = KnuthElement.INFINITE; - if (keepStrength < BlockLevelLayoutManager.KEEP_ALWAYS) { - penalty--; - } - return penalty; - } - - /** - * Returns a string representation of a keep strength value. - * @param keepStrength the keep strength - * @return the string representation - */ - public static String keepStrengthToString(int keepStrength) { - if (keepStrength == BlockLevelLayoutManager.KEEP_AUTO) { - return "auto"; - } else if (keepStrength == BlockLevelLayoutManager.KEEP_ALWAYS) { - return "always"; - } else { - return Integer.toString(keepStrength); - } - } - -} Index: src/java/org/apache/fop/layoutmgr/KnuthPenalty.java =================================================================== --- src/java/org/apache/fop/layoutmgr/KnuthPenalty.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/KnuthPenalty.java (working copy) @@ -45,7 +45,7 @@ public static final int FLAGGED_PENALTY = 50; private int penalty; - private boolean bFlagged; + private boolean isFlagged; private int breakClass = -1; /** @@ -55,12 +55,12 @@ * @param p the penalty value of this penalty * @param f is this penalty flagged? * @param pos the Position stored in this penalty - * @param bAux is this penalty auxiliary? + * @param isAuxiliary is this penalty auxiliary? */ - public KnuthPenalty(int w, int p, boolean f, Position pos, boolean bAux) { - super(w, pos, bAux); + public KnuthPenalty(int w, int p, boolean f, Position pos, boolean isAuxiliary) { + super(w, pos, isAuxiliary); penalty = p; - bFlagged = f; + isFlagged = f; } /** @@ -69,18 +69,16 @@ * @param w the width of this penalty * @param p the penalty value of this penalty * @param f is this penalty flagged? - * @param iBreakClass the break class of this penalty (one of + * @param breakClass the break class of this penalty (one of * {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE}, * {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE}) * @param pos the Position stored in this penalty - * @param bAux is this penalty auxiliary? + * @param isAuxiliary is this penalty auxiliary? */ public KnuthPenalty(int w, int p, boolean f, - int iBreakClass, Position pos, boolean bAux) { - super(w, pos, bAux); - penalty = p; - bFlagged = f; - breakClass = iBreakClass; + int breakClass, Position pos, boolean isAuxiliary) { + this(w, p, f, pos, isAuxiliary); + this.breakClass = breakClass; } /** {@inheritDoc} */ @@ -105,14 +103,19 @@ /** @return true is this penalty is a flagged one. */ public boolean isFlagged() { - return bFlagged; + return isFlagged; } /** {@inheritDoc} */ public boolean isForcedBreak() { - return penalty == -KnuthElement.INFINITE; + return (penalty == -KnuthElement.INFINITE); } + /** {@inheritDoc} */ + public int getType() { + return PENALTY_TYPE; + } + /** * @return the break class of this penalty (EN_AUTO, EN_COLUMN, EN_PAGE, EN_EVEN_PAGE, * EN_ODD_PAGE) @@ -121,14 +124,6 @@ return breakClass; } - /** - * Sets the break class for this penalty. - * @param cl the break class (EN_AUTO, EN_COLUMN, EN_PAGE, EN_EVEN_PAGE, EN_ODD_PAGE) - */ - public void setBreakClass(int cl) { - this.breakClass = cl; - } - /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(64); @@ -137,22 +132,22 @@ } sb.append("penalty"); sb.append(" p="); - if (getP() < 0) { + if (this.penalty < 0) { sb.append("-"); } - if (Math.abs(getP()) == INFINITE) { + if (Math.abs(this.penalty) == INFINITE) { sb.append("INFINITE"); } else { - sb.append(getP()); + sb.append(this.penalty); } - if (isFlagged()) { + if (this.isFlagged) { sb.append(" [flagged]"); } sb.append(" w="); sb.append(getW()); if (isForcedBreak()) { sb.append(" (forced break"); - switch (getBreakClass()) { + switch (this.breakClass) { case Constants.EN_PAGE: sb.append(", page"); break; @@ -168,8 +163,23 @@ default: } sb.append(")"); + } else { + sb.append(" (keep context: "); + switch (this.breakClass) { + case Constants.EN_PAGE: + sb.append(" page)"); + break; + case Constants.EN_COLUMN: + sb.append(" column)"); + break; + case Constants.EN_LINE: + sb.append(" line)"); + break; + default: + sb.append(" auto)"); + break; + } } return sb.toString(); } - } Index: src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (working copy) @@ -36,6 +36,7 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.Block; import org.apache.fop.fo.properties.CommonHyphenation; +import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; @@ -46,7 +47,7 @@ import org.apache.fop.layoutmgr.BreakingAlgorithm; import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.InlineKnuthSequence; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBlockBox; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; @@ -362,7 +363,7 @@ int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast; indent += (textAlign == Constants.EN_CENTER) ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0; - indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0; + indent += (bestActiveNode.line == 1 && indentFirstPart && isFirstInBlock) ? textIndent : 0; double ratio = (textAlign == Constants.EN_JUSTIFY || difference < 0 && -difference <= bestActiveNode.availableShrink) ? bestActiveNode.adjustRatio : 0; @@ -1054,12 +1055,12 @@ for (int p = 0; p < knuthParagraphs.size(); p++) { // penalty between paragraphs if (p > 0) { - int strength = getKeepTogetherStrength(); - int penalty = KeepUtil.getPenaltyForKeep(strength); - if (penalty < KnuthElement.INFINITE) { + Keep keep = getKeepTogether(); + int penalty = keep.getPenalty(); + //if (penalty < KnuthElement.INFINITE) { returnList.add(new BreakElement( - new Position(this), penalty, context)); - } + new Position(this), penalty, keep.getContext(), context)); + //} } LineLayoutPossibilities llPoss; @@ -1098,12 +1099,12 @@ && i >= fobj.getOrphans() && i <= llPoss.getChosenLineCount() - fobj.getWidows()) { // penalty allowing a page break between lines - int strength = getKeepTogetherStrength(); - int penalty = KeepUtil.getPenaltyForKeep(strength); - if (penalty < KnuthElement.INFINITE) { + Keep keep = getKeepTogether(); + int penalty = keep.getPenalty(); + //if (penalty < KnuthElement.INFINITE) { returnList.add(new BreakElement( - returnPosition, penalty, context)); - } + returnPosition, penalty, keep.getContext(), context)); + //} } int endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); @@ -1283,28 +1284,43 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - return ((BlockLevelLayoutManager) getParent()).getKeepTogetherStrength(); + public KeepProperty getKeepTogetherProperty() { + return ((BlockLevelLayoutManager) getParent()).getKeepTogetherProperty(); } /** {@inheritDoc} */ + public KeepProperty getKeepWithPreviousProperty() { + return ((BlockLevelLayoutManager) getParent()).getKeepWithPreviousProperty(); + } + + /** {@inheritDoc} */ + public KeepProperty getKeepWithNextProperty() { + return ((BlockLevelLayoutManager) getParent()).getKeepWithNextProperty(); + } + + /** {@inheritDoc} */ + public Keep getKeepTogether() { + return ((BlockLevelLayoutManager) getParent()).getKeepTogether(); + } + + /** {@inheritDoc} */ public boolean mustKeepWithPrevious() { - return getKeepWithPreviousStrength() > KEEP_AUTO; + return !getKeepWithPrevious().isAuto(); } /** {@inheritDoc} */ public boolean mustKeepWithNext() { - return getKeepWithNextStrength() > KEEP_AUTO; + return !getKeepWithNext().isAuto(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ Index: src/java/org/apache/fop/layoutmgr/AbstractBreaker.java =================================================================== --- src/java/org/apache/fop/layoutmgr/AbstractBreaker.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/AbstractBreaker.java (working copy) @@ -399,7 +399,13 @@ ListElement lastBreakElement = effectiveList.getElement(endElementIndex); if (lastBreakElement.isPenalty()) { KnuthPenalty pen = (KnuthPenalty)lastBreakElement; - lastBreakClass = pen.getBreakClass(); + // TODO Handle keep.within-column differently so that break class is + // automatically set to the right value + if (pen.getP() >= KnuthPenalty.INFINITE - 1) { + lastBreakClass = Constants.EN_COLUMN; + } else { + lastBreakClass = pen.getBreakClass(); + } } else { lastBreakClass = Constants.EN_COLUMN; } @@ -562,7 +568,7 @@ nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn); Position breakPosition = null; - if (((KnuthElement) ListUtil.getLast(returnedList)).isForcedBreak()) { + if (ElementListUtils.endsWithForcedBreak(returnedList)) { KnuthPenalty breakPenalty = (KnuthPenalty) ListUtil .removeLast(returnedList); breakPosition = breakPenalty.getPosition(); Index: src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java (working copy) @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr; +import org.apache.fop.fo.properties.KeepProperty; + /** * The interface for LayoutManagers which generate block areas */ @@ -35,11 +37,6 @@ /** Adjustment class: adjustment for line height */ int LINE_HEIGHT_ADJUSTMENT = 3; - /** The integer value for "auto" keep strength */ - int KEEP_AUTO = Integer.MIN_VALUE; - /** The integer value for "always" keep strength */ - int KEEP_ALWAYS = Integer.MAX_VALUE; - int negotiateBPDAdjustment(int adj, KnuthElement lastElement); void discardSpace(KnuthGlue spaceGlue); @@ -48,7 +45,7 @@ * Returns the keep-together strength for this element. * @return the keep-together strength */ - int getKeepTogetherStrength(); + Keep getKeepTogether(); /** * @return true if this element must be kept together @@ -59,7 +56,7 @@ * Returns the keep-with-previous strength for this element. * @return the keep-with-previous strength */ - int getKeepWithPreviousStrength(); + Keep getKeepWithPrevious(); /** * @return true if this element must be kept with the previous element. @@ -70,11 +67,37 @@ * Returns the keep-with-next strength for this element. * @return the keep-with-next strength */ - int getKeepWithNextStrength(); + Keep getKeepWithNext(); /** * @return true if this element must be kept with the next element. */ boolean mustKeepWithNext(); + /** + * Returns the keep-together property specified on the FObj. + * (optional operation) + * @return the keep-together property + * @throws UnsupportedOperationException if the method is not + * supported by this LM + */ + KeepProperty getKeepTogetherProperty(); + + /** + * Returns the keep-with-previous property specified on the FObj. + * (optional operation) + * @return the keep-together property + * @throws UnsupportedOperationException if the method is not + * supported by this LM + */ + KeepProperty getKeepWithPreviousProperty(); + + /** + * Returns the keep-with-next property specified on the FObj. + * (optional operation) + * @return the keep-together property + * @throws UnsupportedOperationException if the method is not + * supported by this LM + */ + KeepProperty getKeepWithNextProperty(); } Index: src/java/org/apache/fop/layoutmgr/LayoutContext.java =================================================================== --- src/java/org/apache/fop/layoutmgr/LayoutContext.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/LayoutContext.java (working copy) @@ -145,8 +145,8 @@ private int breakBefore; private int breakAfter; - private int pendingKeepWithNext = BlockLevelLayoutManager.KEEP_AUTO; - private int pendingKeepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO; + private Keep pendingKeepWithNext = Keep.KEEP_AUTO; + private Keep pendingKeepWithPrevious = Keep.KEEP_AUTO; private int disableColumnBalancing; @@ -237,7 +237,7 @@ * Returns the strength of a keep-with-next currently pending. * @return the keep-with-next strength */ - public int getKeepWithNextPending() { + public Keep getKeepWithNextPending() { return this.pendingKeepWithNext; } @@ -245,7 +245,7 @@ * Returns the strength of a keep-with-previous currently pending. * @return the keep-with-previous strength */ - public int getKeepWithPreviousPending() { + public Keep getKeepWithPreviousPending() { return this.pendingKeepWithPrevious; } @@ -253,14 +253,14 @@ * Clears any pending keep-with-next strength. */ public void clearKeepWithNextPending() { - this.pendingKeepWithNext = BlockLevelLayoutManager.KEEP_AUTO; + this.pendingKeepWithNext = Keep.KEEP_AUTO; } /** * Clears any pending keep-with-previous strength. */ public void clearKeepWithPreviousPending() { - this.pendingKeepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO; + this.pendingKeepWithPrevious = Keep.KEEP_AUTO; } /** @@ -273,18 +273,18 @@ /** * Updates the currently pending keep-with-next strength. - * @param strength the new strength to consider + * @param keep the new strength to consider */ - public void updateKeepWithNextPending(int strength) { - this.pendingKeepWithNext = Math.max(this.pendingKeepWithNext, strength); + public void updateKeepWithNextPending(Keep keep) { + this.pendingKeepWithNext = this.pendingKeepWithNext.compare(keep); } /** * Updates the currently pending keep-with-previous strength. - * @param strength the new strength to consider + * @param keep the new strength to consider */ - public void updateKeepWithPreviousPending(int strength) { - this.pendingKeepWithPrevious = Math.max(this.pendingKeepWithPrevious, strength); + public void updateKeepWithPreviousPending(Keep keep) { + this.pendingKeepWithPrevious = this.pendingKeepWithPrevious.compare(keep); } /** @@ -292,7 +292,7 @@ * @return true if a keep-with-next constraint is pending */ public boolean isKeepWithNextPending() { - return getKeepWithNextPending() != BlockLevelLayoutManager.KEEP_AUTO; + return !getKeepWithNextPending().isAuto(); } /** @@ -300,7 +300,7 @@ * @return true if a keep-with-previous constraint is pending */ public boolean isKeepWithPreviousPending() { - return getKeepWithPreviousPending() != BlockLevelLayoutManager.KEEP_AUTO; + return !getKeepWithPreviousPending().isAuto(); } public void setLeadingSpace(SpaceSpecifier space) { @@ -677,9 +677,8 @@ + "\nStarts New Area: \t" + startsNewArea() + "\nIs Last Area: \t" + isLastArea() + "\nTry Hyphenate: \t" + tryHyphenate() - + "\nKeeps: \t[keep-with-next=" + KeepUtil.keepStrengthToString(getKeepWithNextPending()) - + "][keep-with-previous=" - + KeepUtil.keepStrengthToString(getKeepWithPreviousPending()) + "] pending" + + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending() + + "][keep-with-previous=" + getKeepWithPreviousPending() + "] pending" + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "][" + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]"; } Index: src/java/org/apache/fop/layoutmgr/Keep.java =================================================================== --- src/java/org/apache/fop/layoutmgr/Keep.java (revision 0) +++ src/java/org/apache/fop/layoutmgr/Keep.java (revision 0) @@ -0,0 +1,156 @@ +/* + * 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 org.apache.fop.fo.Constants; +import org.apache.fop.fo.properties.KeepProperty; +import org.apache.fop.fo.properties.Property; + +/** + * Object representing a keep constraint, corresponding + * to the XSL-FO keep properties. + */ +public class Keep { + + /** The integer value for "auto" keep strength. */ + private static final int STRENGTH_AUTO = Integer.MIN_VALUE; + + /** The integer value for "always" keep strength. */ + private static final int STRENGTH_ALWAYS = Integer.MAX_VALUE; + + public static final Keep KEEP_AUTO = new Keep(STRENGTH_AUTO, Constants.EN_AUTO); + + public static final Keep KEEP_ALWAYS = new Keep(STRENGTH_ALWAYS, Constants.EN_LINE); + + private int strength = STRENGTH_AUTO; + + private int context; + + private Keep(int strength, int context) { + this.strength = strength; + this.context = context; + } + + private static int getKeepStrength(Property keep) { + if (keep.isAuto()) { + return STRENGTH_AUTO; + } else if (keep.getEnum() == Constants.EN_ALWAYS) { + return STRENGTH_ALWAYS; + } else { + return keep.getNumber().intValue(); + } + } + + /** + * Obtain a Keep instance corresponding to the given {@link KeepProperty} + * + * @param keepProperty the {@link KeepProperty} + * @return a new instance corresponding to the given property + */ + public static Keep getKeep(KeepProperty keepProperty) { + Keep keep = new Keep(STRENGTH_AUTO, Constants.EN_AUTO); + keep.update(keepProperty.getWithinPage(), Constants.EN_PAGE); + keep.update(keepProperty.getWithinColumn(), Constants.EN_COLUMN); + keep.update(keepProperty.getWithinLine(), Constants.EN_LINE); + return keep; + } + + private void update(Property keep, int context) { + if (!keep.isAuto()) { + this.strength = getKeepStrength(keep); + this.context = context; + } + } + + /** @return {@code true} if the keep property was specified as "auto" */ + public boolean isAuto() { + return (this.strength == STRENGTH_AUTO); + } + + /** + * Returns the context of this keep. + * + * @return one of {@link Constants#EN_LINE}, {@link Constants#EN_COLUMN} or + * {@link Constants#EN_PAGE} + */ + public int getContext() { + return this.context; + } + + /** + * Return the penalty value corresponding to the strength of this Keep. + * Note: integer keep-values will all result in the same penalty value. + * @return the penalty value corresponding to the strength of this Keep */ + public int getPenalty() { + if (strength == STRENGTH_AUTO) { + return 0; + } else if (strength == STRENGTH_ALWAYS) { + return KnuthElement.INFINITE; + } else { + return KnuthElement.INFINITE - 1; + } + } + + private static int getKeepContextPriority(int context) { + switch (context) { + case Constants.EN_LINE: return 0; + case Constants.EN_COLUMN: return 1; + case Constants.EN_PAGE: return 2; + case Constants.EN_AUTO: return 3; + default: throw new IllegalArgumentException(); + } + } + + /** + * Compare this Keep instance to another one, and return the + * stronger one if the context is the same + * + * @param other the instance to compare to + * @return the winning Keep instance + */ + public Keep compare(Keep other) { + + /* check strength "always" first, regardless of priority */ + if (this.strength == STRENGTH_ALWAYS + && this.strength > other.strength) { + return this; + } else if (other.strength == STRENGTH_ALWAYS + && other.strength > this.strength) { + return other; + } + + int pThis = getKeepContextPriority(this.context); + int pOther = getKeepContextPriority(other.context); + + /* equal priority: strongest wins */ + if (pThis == pOther) { + return (strength >= other.strength) ? this : other; + } + + /* different priority: lowest priority wins */ + return (pThis < pOther) ? this : other; + } + + /** {@inheritDoc} */ + public String toString() { + return (strength == STRENGTH_AUTO) ? "auto" + : (strength == STRENGTH_ALWAYS) ? "always" + : Integer.toString(strength); + } + +} Property changes on: src/java/org/apache/fop/layoutmgr/Keep.java ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java (working copy) @@ -37,6 +37,7 @@ 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; import org.apache.fop.util.ListUtil; @@ -261,7 +262,7 @@ if (!firstVisibleMarkServed) { addKnuthElementsForSpaceBefore(returnList, alignment); - context.updateKeepWithPreviousPending(getKeepWithPreviousStrength()); + context.updateKeepWithPreviousPending(getKeepWithPrevious()); } addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); @@ -323,8 +324,7 @@ //Avoid NoSuchElementException below (happens with empty blocks) continue; } - if (((ListElement) ListUtil.getLast(returnedList)) - .isForcedBreak()) { + if (ElementListUtils.endsWithForcedBreak(returnedList)) { // a descendant of this block has break-after if (curLM.isFinished()) { // there is no other content in this block; @@ -391,7 +391,7 @@ context.clearPendingMarks(); addKnuthElementsForBreakAfter(returnList, context); - context.updateKeepWithNextPending(getKeepWithNextStrength()); + context.updateKeepWithNextPending(getKeepWithNext()); setFinished(true); return returnList; @@ -1011,23 +1011,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KeepUtil.getCombinedBlockLevelKeepStrength( - getBlockContainerFO().getKeepTogether()); - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + public KeepProperty getKeepTogetherProperty() { + return getBlockContainerFO().getKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength( - getBlockContainerFO().getKeepWithNext()); + public KeepProperty getKeepWithPreviousProperty() { + return getBlockContainerFO().getKeepWithPrevious(); } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength( - getBlockContainerFO().getKeepWithPrevious()); + public KeepProperty getKeepWithNextProperty() { + return getBlockContainerFO().getKeepWithNext(); } /** Index: src/java/org/apache/fop/layoutmgr/BreakElement.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BreakElement.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BreakElement.java (working copy) @@ -41,9 +41,13 @@ * @param context the layout context which contains the pending conditional elements */ public BreakElement(Position position, int penaltyValue, LayoutContext context) { - this(position, 0, penaltyValue, -1, context); + this(position, penaltyValue, -1, context); } + public BreakElement(Position position, int penaltyValue, int breakClass, LayoutContext context) { + this(position, 0, penaltyValue, breakClass, context); + } + /** * Constructor for hard breaks. * @@ -168,6 +172,17 @@ sb.append("; w:"); sb.append(penaltyWidth); sb.append("]"); + switch (getBreakClass()) { + case Constants.EN_PAGE: + sb.append(" (page context)"); + break; + case Constants.EN_COLUMN: + sb.append(" (column context)"); + break; + case Constants.EN_LINE: + sb.append(" (line context)"); + break; + } return sb.toString(); } Index: src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java (working copy) @@ -63,10 +63,6 @@ /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { - // set layout dimensions - int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); - int flowBPD = getCurrentPV().getBodyRegion().getBPD(); - // currently active LM LayoutManager curLM; List returnedList; @@ -84,12 +80,10 @@ int disableColumnBalancing = EN_FALSE; if (curLM instanceof BlockLayoutManager) { span = ((BlockLayoutManager)curLM).getBlockFO().getSpan(); - disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO() - .getDisableColumnBalancing(); + disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO().getDisableColumnBalancing(); } else if (curLM instanceof BlockContainerLayoutManager) { span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); - disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO() - .getDisableColumnBalancing(); + disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO().getDisableColumnBalancing(); } int currentSpan = context.getCurrentSpan(); @@ -113,6 +107,7 @@ // get elements from curLM returnedList = curLM.getNextKnuthElements(childLC, alignment); + //int contentHeight = ElementListUtils.calcContentLength(returnedList); //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size()); if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) { context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); @@ -154,7 +149,7 @@ context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); childLC.clearKeepWithNextPending(); - context.updateKeepWithNextPending(getKeepWithNextStrength()); + context.updateKeepWithNextPending(getKeepWithNext()); } SpaceResolver.resolveElementList(returnList); @@ -203,18 +198,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - return KEEP_AUTO; + public Keep getKeepTogether() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ Index: src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java (working copy) @@ -92,18 +92,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - return getParentKeepTogetherStrength(); + public Keep getKeepTogether() { + return getParentKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; } } Index: src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (working copy) @@ -210,21 +210,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - KeepProperty keep = getBlockFO().getKeepTogether(); - int strength = KeepUtil.getCombinedBlockLevelKeepStrength(keep); - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + public KeepProperty getKeepTogetherProperty() { + return getBlockFO().getKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithNext()); + public KeepProperty getKeepWithPreviousProperty() { + return getBlockFO().getKeepWithPrevious(); } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithPrevious()); + public KeepProperty getKeepWithNextProperty() { + return getBlockFO().getKeepWithNext(); } /** {@inheritDoc} */ Index: src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java =================================================================== --- src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (working copy) @@ -20,18 +20,21 @@ package org.apache.fop.layoutmgr; import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition; import org.apache.fop.traits.MinOptMax; +import org.apache.fop.util.ListUtil; -class PageBreakingAlgorithm extends BreakingAlgorithm { +public class PageBreakingAlgorithm extends BreakingAlgorithm { /** the logger for the class */ private static Log log = LogFactory.getLog(PageBreakingAlgorithm.class); @@ -47,9 +50,9 @@ * List<List<KnuthElement>>, it contains the sequences of KnuthElement * representing the footnotes bodies. */ - private ArrayList footnotesList = null; + private List footnotesList = null; /** Cumulated bpd of unhandled footnotes. */ - private ArrayList lengthList = null; + private List lengthList = null; /** Length of all the footnotes which will be put on the current page. */ private int totalFootnotesLength = 0; /** @@ -58,6 +61,7 @@ * footnotes from its preceding pages. */ private int insertedFootnotesLength = 0; + /** True if footnote citations have been met since the beginning of the page sequence. */ private boolean footnotesPending = false; /** @@ -178,6 +182,7 @@ } } + /** {@inheritDoc} */ protected void initialize() { super.initialize(); insertedFootnotesLength = 0; @@ -185,6 +190,72 @@ footnoteElementIndex = -1; } + private int currentKeepContext = Constants.EN_AUTO; + private KnuthNode lastBeforeKeepContextSwitch; + + /** + * {@inheritDoc} + * Overridden to defer a part to the next page, if it + * must be kept within one page, but is too large to fit in + * the last N-1 columns. + */ + protected KnuthNode recoverFromTooLong(KnuthNode lastTooLong) { + + // return super.recoverFromTooLong(lastTooLong); + if (log.isDebugEnabled()) { + log.debug("Recovering from too long: " + lastTooLong); + } + + if (lastBeforeKeepContextSwitch == null + || currentKeepContext == Constants.EN_AUTO) { + return super.recoverFromTooLong(lastTooLong); + } + + KnuthNode node = lastBeforeKeepContextSwitch; + lastBeforeKeepContextSwitch = null; + // content would overflow, insert empty page/column(s) and try again + while (!pageProvider.endPage(node.line + 1)) { + node = createNode( + node.position, + node.line + 1, 1, + 0, 0, 0, + 0, 0, 0, + 0, 0, node); + } + return node; + } + + /** + * {@inheritDoc} + * Overridden to return the last page-break node, instead of the node with the highest + * position index (which may actually be a column-break node) + */ + protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) { + + //if either node is null, return the other one + if (node1 == null || node2 == null) { + return (node2 == null) ? node1 : node2; + } + + //if the PageProvider has been properly initialized, + //check whether either one of the nodes does not start a page + //while the other one does + //if so, return the one that corresponds to a page-break + if (pageProvider != null) { + if (!pageProvider.endPage(node1.line) + && pageProvider.endPage(node2.line)) { + return node2; + } else if (!pageProvider.endPage(node2.line) + && pageProvider.endPage(node1.line)) { + return node1; + } + } + + //all other cases: use superclass implementation + return super.compareNodes(node1, node2); + } + + /** {@inheritDoc} */ protected KnuthNode createNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, double adjustRatio, int availableShrink, int availableStretch, @@ -196,6 +267,7 @@ difference, totalDemerits, previous); } + /** {@inheritDoc} */ protected KnuthNode createNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink) { return new KnuthPageNode(position, line, fitness, @@ -209,6 +281,7 @@ } /** + * {@inheritDoc} * Page-breaking specific handling of the given box. Currently it adds the footnotes * cited in the given box to the list of to-be-handled footnotes. * @param box a block-level element possibly containing foonotes citations @@ -222,9 +295,31 @@ firstNewFootnoteIndex = footnotesList.size() - 1; } } + super.handleBox(box); } /** + * {@inheritDoc} + * Overridden to consider penalties with value {@link KnuthElement#INFINITE} + * as legal break-points, if the current keep-context allows this + * (a keep-*.within-page="always" constraint still permits column-breaks) + */ + protected void handlePenaltyAt(KnuthPenalty penalty, int position, + int allowedBreaks) { + super.handlePenaltyAt(penalty, position, allowedBreaks); + /* if the penalty had value INFINITE, default implementation + * will not have considered it a legal break, do so now + */ + if (penalty.getP() == KnuthPenalty.INFINITE) { + int breakClass = penalty.getBreakClass(); + if (breakClass == Constants.EN_PAGE + || breakClass == Constants.EN_COLUMN) { + considerLegalBreak(penalty, position); + } + } + } + + /** * Handles the footnotes cited inside a block-level box. Updates footnotesList and the * value of totalFootnotesLength with the lengths of the given footnotes. * @param elementLists list of KnuthElement sequences corresponding to the footnotes @@ -244,9 +339,9 @@ } // compute the total length of the footnotes - ListIterator elementListsIterator = elementLists.listIterator(); - while (elementListsIterator.hasNext()) { - LinkedList noteList = (LinkedList) elementListsIterator.next(); + for (Iterator elementListsIterator = elementLists.iterator(); + elementListsIterator.hasNext();) { + final List noteList = (List) elementListsIterator.next(); //Space resolution (Note: this does not respect possible stacking constraints //between footnotes!) @@ -254,21 +349,23 @@ int noteLength = 0; footnotesList.add(noteList); - ListIterator noteListIterator = noteList.listIterator(); - while (noteListIterator.hasNext()) { - KnuthElement element = (KnuthElement) noteListIterator.next(); + for (Iterator noteListIterator = noteList.iterator(); + noteListIterator.hasNext();) { + final KnuthElement element = (KnuthElement) noteListIterator.next(); if (element.isBox() || element.isGlue()) { noteLength += element.getW(); } } - int prevLength = (lengthList.size() == 0 + int prevLength = (lengthList == null || lengthList.isEmpty()) ? 0 - : ((Integer) lengthList.get(lengthList.size() - 1)).intValue()); + : ((Integer) ListUtil.getLast(lengthList)).intValue(); + //TODO: replace with Integer.valueOf() once we switch to Java 5 lengthList.add(new Integer(prevLength + noteLength)); totalFootnotesLength += noteLength; } } + /** {@inheritDoc} */ protected int restartFrom(KnuthNode restartingNode, int currentIndex) { int returnValue = super.restartFrom(restartingNode, currentIndex); newFootnotes = false; @@ -276,10 +373,10 @@ // remove from footnotesList the note lists that will be met // after the restarting point for (int j = currentIndex; j >= restartingNode.position; j--) { - KnuthElement resettedElement = getElement(j); - if (resettedElement instanceof KnuthBlockBox - && ((KnuthBlockBox) resettedElement).hasAnchors()) { - resetFootnotes(((KnuthBlockBox) resettedElement).getElementLists()); + final KnuthElement resetElement = getElement(j); + if (resetElement instanceof KnuthBlockBox + && ((KnuthBlockBox) resetElement).hasAnchors()) { + resetFootnotes(((KnuthBlockBox) resetElement).getElementLists()); } } } @@ -288,12 +385,12 @@ private void resetFootnotes(List elementLists) { for (int i = 0; i < elementLists.size(); i++) { - /*LinkedList removedList = (LinkedList)*/footnotesList.remove(footnotesList.size() - 1); - lengthList.remove(lengthList.size() - 1); + /*LinkedList removedList = (LinkedList)*/ListUtil.removeLast(footnotesList); + ListUtil.removeLast(lengthList); // update totalFootnotesLength - if (lengthList.size() > 0) { - totalFootnotesLength = ((Integer) lengthList.get(lengthList.size() - 1)).intValue(); + if (!lengthList.isEmpty()) { + totalFootnotesLength = ((Integer) ListUtil.getLast(lengthList)).intValue(); } else { totalFootnotesLength = 0; } @@ -304,16 +401,67 @@ } } + /** {@inheritDoc} */ protected void considerLegalBreak(KnuthElement element, int elementIdx) { + if (element.isPenalty()) { + int breakClass = ((KnuthPenalty) element).getBreakClass(); + switch (breakClass) { + case Constants.EN_COLUMN: + case Constants.EN_PAGE: + if (this.currentKeepContext != breakClass) { + this.lastBeforeKeepContextSwitch = getLastTooShort(); + } + this.currentKeepContext = breakClass; + break; + case Constants.EN_AUTO: + this.currentKeepContext = breakClass; + break; + default: + //nop + } + } super.considerLegalBreak(element, elementIdx); newFootnotes = false; } + /** {@inheritDoc} */ + protected boolean elementCanEndLine(KnuthElement element, int line) { + if (!(element.isPenalty()) || pageProvider == null) { + return true; + } else { + KnuthPenalty p = (KnuthPenalty) element; + if (p.getP() <= 0) { + return true; + } else { + int context = p.getBreakClass(); + switch (context) { + case Constants.EN_LINE: + case Constants.EN_COLUMN: + return p.getP() < KnuthPenalty.INFINITE; + case Constants.EN_PAGE: + return p.getP() < KnuthPenalty.INFINITE + || !pageProvider.endPage(line - 1); + case Constants.EN_AUTO: + log.warn("keep is not auto but context is"); + return true; + default: + if (p.getP() < KnuthPenalty.INFINITE) { + log.warn("Non recognized keep context:" + context); + return true; + } else { + return false; + } + } + } + } + } + + /** {@inheritDoc} */ protected int computeDifference(KnuthNode activeNode, KnuthElement element, int elementIndex) { KnuthPageNode pageNode = (KnuthPageNode) activeNode; int actualWidth = totalWidth - pageNode.totalWidth; - int footnoteSplit; + int footnoteSplit = 0; boolean canDeferOldFootnotes; if (element.isPenalty()) { actualWidth += element.getW(); @@ -332,7 +480,7 @@ insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; footnoteListIndex = footnotesList.size() - 1; footnoteElementIndex - = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; + = getFootnoteList(footnoteListIndex).size() - 1; } else if (((canDeferOldFootnotes = checkCanDeferOldFootnotes(pageNode, elementIndex)) || newFootnotes) @@ -358,7 +506,7 @@ insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; footnoteListIndex = footnotesList.size() - 1; footnoteElementIndex - = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; + = getFootnoteList(footnoteListIndex).size() - 1; } } else { // all footnotes have already been placed on previous pages @@ -375,7 +523,8 @@ } } - /** Checks whether footnotes from preceding pages may be deferred to the page after + /** + * Checks whether footnotes from preceding pages may be deferred to the page after * the given element. * @param node active node for the preceding page break * @param contentElementIndex index of the Knuth element considered for the @@ -448,7 +597,7 @@ return ((newFootnotes && firstNewFootnoteIndex != 0 && (listIndex < firstNewFootnoteIndex - 1 - || elementIndex < ((LinkedList) footnotesList.get(listIndex)).size() - 1)) + || elementIndex < getFootnoteList(listIndex).size() - 1)) || length < totalFootnotesLength); } @@ -457,6 +606,7 @@ * @param activeNode currently considered previous page break * @param availableLength available space for footnotes * @param canDeferOldFootnotes + * @return ... */ private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength, boolean canDeferOldFootnotes) { @@ -473,6 +623,7 @@ * @param prevLength total length of footnotes inserted so far * @param availableLength available space for footnotes on this page * @param canDeferOldFootnotes + * @return ... */ private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength, int availableLength, boolean canDeferOldFootnotes) { @@ -491,7 +642,7 @@ // already placed in a page: advance to the next element int listIndex = prevListIndex; int elementIndex = prevElementIndex; - if (elementIndex == ((LinkedList) footnotesList.get(listIndex)).size() - 1) { + if (elementIndex == getFootnoteList(listIndex).size() - 1) { listIndex++; elementIndex = 0; } else { @@ -524,8 +675,7 @@ } // try adding a split of the next note - noteListIterator = ((LinkedList) footnotesList.get(listIndex)) - .listIterator(elementIndex); + noteListIterator = getFootnoteList(listIndex).listIterator(elementIndex); int prevSplitLength = 0; int prevIndex = -1; @@ -539,7 +689,7 @@ prevIndex = index; } // get a sub-sequence from the note element list - boolean bPrevIsBox = false; + boolean boxPreceding = false; while (noteListIterator.hasNext()) { // as this method is called only if it is not possible to insert // all footnotes, and we have already tried (and failed) to insert @@ -549,31 +699,33 @@ if (element.isBox()) { // element is a box splitLength += element.getW(); - bPrevIsBox = true; + boxPreceding = true; } else if (element.isGlue()) { // element is a glue - if (bPrevIsBox) { + if (boxPreceding) { // end of the sub-sequence index = noteListIterator.previousIndex(); break; } - bPrevIsBox = false; + boxPreceding = false; splitLength += element.getW(); } else { // element is a penalty - if (element.getP() < KnuthElement.INFINITE) { + //if (element.getP() < KnuthElement.INFINITE) { // end of the sub-sequence index = noteListIterator.previousIndex(); break; - } + //} } } } + // if prevSplitLength is 0, this means that the available length isn't enough // to insert even the smallest split of the last footnote, so we cannot end a // page here // if prevSplitLength is > 0 we can insert some footnote content in this page // and insert the remaining in the following one + //TODO: check this conditional, as the first one is always false...? if (!somethingAdded) { // there was not enough space to add a piece of the first new footnote // this is not a good break @@ -583,12 +735,13 @@ footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1; footnoteElementIndex = (prevIndex != -1) ? prevIndex - : ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; + : getFootnoteList(footnoteListIndex).size() - 1; } return prevSplitLength; } } + /** {@inheritDoc} */ protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { // compute the adjustment ratio if (difference > 0) { @@ -618,6 +771,7 @@ } } + /** {@inheritDoc} */ protected double computeDemerits(KnuthNode activeNode, KnuthElement element, int fitnessClass, double r) { double demerits = 0; @@ -625,7 +779,9 @@ double f = Math.abs(r); f = 1 + 100 * f * f * f; if (element.isPenalty() && element.getP() >= 0) { - f += element.getP(); + if (element.getP() < KnuthPenalty.INFINITE) { + f += element.getP(); + } demerits = f * f; } else if (element.isPenalty() && !element.isForcedBreak()) { double penalty = element.getP(); @@ -654,7 +810,7 @@ } if (footnoteListIndex < footnotesList.size()) { if (footnoteElementIndex - < ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1) { + < getFootnoteList(footnoteListIndex).size() - 1) { // add demerits for the footnote split between pages demerits += splitFootnoteDemerits; } @@ -680,27 +836,28 @@ } private void createFootnotePages(KnuthPageNode lastNode) { + insertedFootnotesLength = lastNode.totalFootnotes; footnoteListIndex = lastNode.footnoteListIndex; footnoteElementIndex = lastNode.footnoteElementIndex; int availableBPD = getLineWidth(lastNode.line); int split = 0; + int tmpLength = -1; KnuthPageNode prevNode = lastNode; // create pages containing the remaining footnote bodies while (insertedFootnotesLength < totalFootnotesLength) { + tmpLength = ((Integer) lengthList.get(footnoteListIndex)).intValue(); // try adding some more content - if (((Integer) lengthList.get(footnoteListIndex)).intValue() - insertedFootnotesLength - <= availableBPD) { + if ((tmpLength - insertedFootnotesLength) <= availableBPD) { // add a whole footnote - availableBPD -= ((Integer) lengthList.get(footnoteListIndex)).intValue() - - insertedFootnotesLength; - insertedFootnotesLength = ((Integer)lengthList.get(footnoteListIndex)).intValue(); + availableBPD -= tmpLength - insertedFootnotesLength; + insertedFootnotesLength = tmpLength; footnoteElementIndex - = ((LinkedList)footnotesList.get(footnoteListIndex)).size() - 1; + = getFootnoteList(footnoteListIndex).size() - 1; } else if ((split = getFootnoteSplit(footnoteListIndex, footnoteElementIndex, - insertedFootnotesLength, availableBPD, true)) - > 0) { + insertedFootnotesLength, availableBPD, true)) + > 0) { // add a piece of a footnote availableBPD -= split; insertedFootnotesLength += split; @@ -732,12 +889,19 @@ } /** - * @return a list of PageBreakPosition elements + * @return a list of {@link PageBreakPosition} elements + * corresponding to the computed page- and column-breaks */ public LinkedList getPageBreaks() { return pageBreaks; } + /** + * Insert the given {@link PageBreakPosition} as the first + * element in the list of page-breaks + * + * @param pageBreak the position to insert + */ public void insertPageBreakAsFirst(PageBreakPosition pageBreak) { if (pageBreaks == null) { pageBreaks = new LinkedList(); @@ -759,9 +923,11 @@ } } + /** {@inheritDoc} */ public void updateData1(int total, double demerits) { } + /** {@inheritDoc} */ public void updateData2(KnuthNode bestActiveNode, KnuthSequence sequence, int total) { @@ -807,7 +973,7 @@ int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex; int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex; if (footnotesList != null - && firstElementIndex == ((LinkedList) footnotesList.get(firstListIndex)).size() - 1) { + && firstElementIndex == getFootnoteList(firstListIndex).size() - 1) { // advance to the next list firstListIndex++; firstElementIndex = 0; @@ -829,6 +995,7 @@ ratio, difference)); } + /** {@inheritDoc} */ protected int filterActiveNodes() { // leave only the active node with fewest total demerits KnuthNode bestActiveNode = null; @@ -848,11 +1015,17 @@ } } } - return bestActiveNode.line; + return (bestActiveNode == null) ? -1 : bestActiveNode.line; } - public LinkedList getFootnoteList(int index) { - return (LinkedList) footnotesList.get(index); + /** + * Obtain the element-list corresponding to the footnote at the given index. + * + * @param index the index in the list of footnotes + * @return the element-list + */ + protected final List getFootnoteList(int index) { + return (List) footnotesList.get(index); } /** @return the associated top-level formatting object. */ Index: src/java/org/apache/fop/layoutmgr/PageProvider.java =================================================================== --- src/java/org/apache/fop/layoutmgr/PageProvider.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/PageProvider.java (working copy) @@ -146,7 +146,50 @@ return this.lastReportedBPD; } + // Wish there were a more elegant way to do this in Java + private int[] getColIndexAndColCount(int index) { + int columnCount = 0; + int colIndex = startColumnOfCurrentElementList + index; + int pageIndex = -1; + do { + colIndex -= columnCount; + pageIndex++; + Page page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); + columnCount = page.getPageViewport().getCurrentSpan().getColumnCount(); + } while (colIndex >= columnCount); + return new int[] {colIndex, columnCount}; + } + /** + * Checks if a break at the passed index would start a new page + * @param index the index of the element before the break + * @return {@code true} if the break starts a new page + */ + boolean startPage(int index) { + return getColIndexAndColCount(index)[0] == 0; + } + + /** + * Checks if a break at the passed index would end a page + * @param index the index of the element before the break + * @return {@code true} if the break ends a page + */ + boolean endPage(int index) { + int[] colIndexAndColCount = getColIndexAndColCount(index); + return colIndexAndColCount[0] == colIndexAndColCount[1] - 1; + } + + /** + * Obtain the applicable column-count for the element at the + * passed index + * @param index the index of the element + * @return the number of columns + */ + int getColumnCount(int index) { + return getColIndexAndColCount(index)[1]; + } + + /** * Returns the part index (0 0) { if (end[0] + 1 == elementLists[0].size()) { - keepWithNextActive = Math.max(keepWithNextActive, keepWithNextPendingOnLabel); + keepWithNextActive = keepWithNextActive.compare(keepWithNextPendingOnLabel); } if (end[1] + 1 == elementLists[1].size()) { - keepWithNextActive = Math.max(keepWithNextActive, keepWithNextPendingOnBody); + keepWithNextActive = keepWithNextActive.compare(keepWithNextPendingOnBody); } // compute penalty height and box height @@ -339,14 +339,13 @@ } if (addedBoxHeight < totalHeight) { - int strength = BlockLevelLayoutManager.KEEP_AUTO; - strength = Math.max(strength, keepWithNextActive); - strength = Math.max(strength, getKeepTogetherStrength()); + Keep keep = keepWithNextActive.compare(getKeepTogether()); int p = stepPenalty; if (p > -KnuthElement.INFINITE) { - p = Math.max(p, KeepUtil.getPenaltyForKeep(strength)); + p = Math.max(p, keep.getPenalty()); } - returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context)); + returnList.add(new BreakElement(stepPosition, penaltyHeight, p, keep.getContext(), + context)); } } @@ -644,21 +643,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KeepUtil.getCombinedBlockLevelKeepStrength( - getListItemFO().getKeepTogether()); - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + public KeepProperty getKeepTogetherProperty() { + return getListItemFO().getKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getListItemFO().getKeepWithNext()); + public KeepProperty getKeepWithPreviousProperty() { + return getListItemFO().getKeepWithPrevious(); } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getListItemFO().getKeepWithPrevious()); + public KeepProperty getKeepWithNextProperty() { + return getListItemFO().getKeepWithNext(); } /** {@inheritDoc} */ Index: src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java (working copy) @@ -28,8 +28,9 @@ import org.apache.fop.fo.flow.AbstractListItemPart; import org.apache.fop.fo.flow.ListItemBody; import org.apache.fop.fo.flow.ListItemLabel; +import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; @@ -221,20 +222,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KeepUtil.getCombinedBlockLevelKeepStrength(getPartFO().getKeepTogether()); - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + public KeepProperty getKeepTogetherProperty() { + return getPartFO().getKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; } } Index: src/java/org/apache/fop/layoutmgr/table/ActiveCell.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/ActiveCell.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/ActiveCell.java (working copy) @@ -32,8 +32,8 @@ import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.ElementListUtils; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBlockBox; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; @@ -75,7 +75,7 @@ /** True if the next CellPart that will be created will be the last one for this cell. */ private boolean lastCellPart; - private int keepWithNextStrength; + private Keep keepWithNext; private int spanIndex = 0; @@ -218,7 +218,7 @@ includedLength = -1; // Avoid troubles with cells having content of zero length totalLength = previousRowsLength + ElementListUtils.calcContentLength(elementList); endRowIndex = rowIndex + pgu.getCell().getNumberRowsSpanned() - 1; - keepWithNextStrength = BlockLevelLayoutManager.KEEP_AUTO; + keepWithNext = Keep.KEEP_AUTO; remainingLength = totalLength - previousRowsLength; afterNextStep = new Step(previousRowsLength); @@ -314,7 +314,11 @@ KnuthElement el = (KnuthElement) knuthIter.next(); if (el.isPenalty()) { prevIsBox = false; - if (el.getP() < KnuthElement.INFINITE) { + if (el.getP() < KnuthElement.INFINITE + || ((KnuthPenalty) el).getBreakClass() == Constants.EN_PAGE) { + // TODO too much is being done in that test, only to handle + // keep.within-column properly. + // First legal break point breakFound = true; KnuthPenalty p = (KnuthPenalty) el; @@ -533,7 +537,7 @@ */ CellPart createCellPart() { if (nextStep.end + 1 == elementList.size()) { - keepWithNextStrength = pgu.getKeepWithNextStrength(); + keepWithNext = pgu.getKeepWithNext(); // TODO if keep-with-next is set on the row, must every cell of the row // contribute some content from children blocks? // see http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200802.mbox/ @@ -576,8 +580,8 @@ } } - int getKeepWithNextStrength() { - return keepWithNextStrength; + Keep getKeepWithNext() { + return keepWithNext; } int getPenaltyValue() { Index: src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java (working copy) @@ -35,10 +35,9 @@ import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TablePart; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ElementListUtils; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; @@ -213,13 +212,13 @@ context.clearKeepsPending(); context.setBreakBefore(Constants.EN_AUTO); context.setBreakAfter(Constants.EN_AUTO); - int keepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO; + Keep keepWithPrevious = Keep.KEEP_AUTO; int breakBefore = Constants.EN_AUTO; if (rowGroup != null) { RowGroupLayoutManager rowGroupLM = new RowGroupLayoutManager(getTableLM(), rowGroup, stepper); List nextRowGroupElems = rowGroupLM.getNextKnuthElements(context, alignment, bodyType); - keepWithPrevious = Math.max(keepWithPrevious, context.getKeepWithPreviousPending()); + keepWithPrevious = keepWithPrevious.compare(context.getKeepWithPreviousPending()); breakBefore = context.getBreakBefore(); int breakBetween = context.getBreakAfter(); returnList.addAll(nextRowGroupElems); @@ -228,7 +227,7 @@ //Note previous pending keep-with-next and clear the strength //(as the layout context is reused) - int keepWithNextPending = context.getKeepWithNextPending(); + Keep keepWithNextPending = context.getKeepWithNextPending(); context.clearKeepWithNextPending(); //Get elements for next row group @@ -246,17 +245,17 @@ */ //Determine keep constraints - int penaltyStrength = BlockLevelLayoutManager.KEEP_AUTO; - penaltyStrength = Math.max(penaltyStrength, keepWithNextPending); - penaltyStrength = Math.max(penaltyStrength, context.getKeepWithPreviousPending()); + Keep keep = keepWithNextPending.compare(context.getKeepWithPreviousPending()); context.clearKeepWithPreviousPending(); - penaltyStrength = Math.max(penaltyStrength, getTableLM().getKeepTogetherStrength()); - int penaltyValue = KeepUtil.getPenaltyForKeep(penaltyStrength); + keep = keep.compare(getTableLM().getKeepTogether()); + int penaltyValue = keep.getPenalty(); + int breakClass = keep.getContext(); breakBetween = BreakUtil.compareBreakClasses(breakBetween, context.getBreakBefore()); if (breakBetween != Constants.EN_AUTO) { penaltyValue = -KnuthElement.INFINITE; + breakClass = breakBetween; } BreakElement breakElement; ListIterator elemIter = returnList.listIterator(returnList.size()); @@ -267,7 +266,7 @@ breakElement = (BreakElement) elem; } breakElement.setPenaltyValue(penaltyValue); - breakElement.setBreakClass(breakBetween); + breakElement.setBreakClass(breakClass); returnList.addAll(nextRowGroupElems); breakBetween = context.getBreakAfter(); } Index: src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java (working copy) @@ -60,8 +60,8 @@ LinkedList returnList = new LinkedList(); createElementsForRowGroup(context, alignment, bodyType, returnList); - context.updateKeepWithPreviousPending(rowGroup[0].getKeepWithPreviousStrength()); - context.updateKeepWithNextPending(rowGroup[rowGroup.length - 1].getKeepWithNextStrength()); + context.updateKeepWithPreviousPending(rowGroup[0].getKeepWithPrevious()); + context.updateKeepWithNextPending(rowGroup[rowGroup.length - 1].getKeepWithNext()); int breakBefore = Constants.EN_AUTO; TableRow firstRow = rowGroup[0].getTableRow(); Index: src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java (working copy) @@ -35,11 +35,11 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.layoutmgr.BlockLevelEventProducer; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ConditionalElementListener; -import org.apache.fop.layoutmgr.KeepUtil; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; import org.apache.fop.layoutmgr.LayoutContext; @@ -256,10 +256,10 @@ log.debug(contentKnuthElements); wrapPositionElements(contentKnuthElements, returnList); - context.updateKeepWithPreviousPending(getKeepWithPreviousStrength()); + context.updateKeepWithPreviousPending(getKeepWithPrevious()); context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); - context.updateKeepWithNextPending(getKeepWithNextStrength()); + context.updateKeepWithNextPending(getKeepWithNext()); context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); if (getTable().isSeparateBorderModel()) { @@ -448,20 +448,18 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepTogether()); - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + public KeepProperty getKeepTogetherProperty() { + return getTable().getKeepTogether(); } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepWithNext()); + public KeepProperty getKeepWithPreviousProperty() { + return getTable().getKeepWithPrevious(); } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepWithPrevious()); + public KeepProperty getKeepWithNextProperty() { + return getTable().getKeepWithNext(); } // --------- Property Resolution related functions --------- // Index: src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java (working copy) @@ -24,6 +24,7 @@ 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.Trait; @@ -31,16 +32,17 @@ import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; -import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.ElementListUtils; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; @@ -153,11 +155,12 @@ log.debug("child LM signals pending keep with next"); } if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { - primaryGridUnit.setKeepWithPreviousStrength(childLC.getKeepWithPreviousPending()); + primaryGridUnit.setKeepWithPrevious(childLC.getKeepWithPreviousPending()); childLC.clearKeepWithPreviousPending(); } - if (prevLM != null) { + if (prevLM != null + && !ElementListUtils.endsWithForcedBreak(contentList)) { // there is a block handled by prevLM // before the one handled by curLM addInBetweenBreak(contentList, context, childLC); @@ -174,7 +177,7 @@ } prevLM = curLM; } - primaryGridUnit.setKeepWithNextStrength(context.getKeepWithNextPending()); + primaryGridUnit.setKeepWithNext(context.getKeepWithNextPending()); returnedList = new LinkedList(); if (!contentList.isEmpty()) { @@ -195,7 +198,7 @@ } final KnuthElement lastItem = (KnuthElement) ListUtil .getLast(returnList); - if (((KnuthElement) lastItem).isForcedBreak()) { + if (lastItem.isForcedBreak()) { KnuthPenalty p = (KnuthPenalty) lastItem; primaryGridUnit.setBreakAfter(p.getBreakClass()); p.setP(0); @@ -556,26 +559,23 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KEEP_AUTO; + public Keep getKeepTogether() { + Keep keep = Keep.KEEP_AUTO; if (primaryGridUnit.getRow() != null) { - strength = Math.max(strength, KeepUtil.getKeepStrength( - primaryGridUnit.getRow().getKeepTogether().getWithinPage())); - strength = Math.max(strength, KeepUtil.getKeepStrength( - primaryGridUnit.getRow().getKeepTogether().getWithinColumn())); + keep = Keep.getKeep(primaryGridUnit.getRow().getKeepTogether()); } - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; + keep = keep.compare(getParentKeepTogether()); + return keep; } /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-next!) + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-next!) } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-previous!) + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-previous!) } // --------- Property Resolution related functions --------- // Index: src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java (working copy) @@ -23,6 +23,7 @@ import org.apache.fop.area.Block; import org.apache.fop.fo.flow.table.TableAndCaption; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; @@ -201,32 +202,21 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; /* TODO Complete me! - int strength = KeepUtil.getCombinedBlockLevelKeepStrength( - getTableAndCaptionFO().getKeepTogether()); - */ - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; - } - - /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; - /* TODO Complete me! return KeepUtil.getCombinedBlockLevelKeepStrength( getTableAndCaptionFO().getKeepWithNext()); */ } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; /* TODO Complete me! return KeepUtil.getCombinedBlockLevelKeepStrength( getTableAndCaptionFO().getKeepWithPrevious()); */ } -} \ No newline at end of file +} Index: src/java/org/apache/fop/layoutmgr/table/TableStepper.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableStepper.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableStepper.java (working copy) @@ -30,12 +30,10 @@ import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BreakElement; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBlockBox; import org.apache.fop.layoutmgr.KnuthBox; -import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; import org.apache.fop.layoutmgr.KnuthPenalty; import org.apache.fop.layoutmgr.LayoutContext; @@ -241,40 +239,38 @@ } } - int strength = BlockLevelLayoutManager.KEEP_AUTO; + Keep keep = Keep.KEEP_AUTO; int stepPenalty = 0; for (Iterator iter = activeCells.iterator(); iter.hasNext();) { ActiveCell activeCell = (ActiveCell) iter.next(); - strength = Math.max(strength, activeCell.getKeepWithNextStrength()); + keep = keep.compare(activeCell.getKeepWithNext()); stepPenalty = Math.max(stepPenalty, activeCell.getPenaltyValue()); } if (!rowFinished) { - strength = Math.max(strength, rowGroup[activeRowIndex].getKeepTogetherStrength()); + keep = keep.compare(rowGroup[activeRowIndex].getKeepTogether()); //The above call doesn't take the penalty from the table into account, so... - strength = Math.max(strength, getTableLM().getKeepTogetherStrength()); + keep = keep.compare(getTableLM().getKeepTogether()); } else if (activeRowIndex < rowGroup.length - 1) { - strength = Math.max(strength, - rowGroup[activeRowIndex].getKeepWithNextStrength()); - strength = Math.max(strength, - rowGroup[activeRowIndex + 1].getKeepWithPreviousStrength()); + keep = keep.compare(rowGroup[activeRowIndex].getKeepWithNext()); + keep = keep.compare(rowGroup[activeRowIndex + 1].getKeepWithPrevious()); nextBreakClass = BreakUtil.compareBreakClasses(nextBreakClass, rowGroup[activeRowIndex].getBreakAfter()); nextBreakClass = BreakUtil.compareBreakClasses(nextBreakClass, rowGroup[activeRowIndex + 1].getBreakBefore()); } - int p = KeepUtil.getPenaltyForKeep(strength); + int p = keep.getPenalty(); if (rowHeightSmallerThanFirstStep) { rowHeightSmallerThanFirstStep = false; p = KnuthPenalty.INFINITE; } - if (p > -KnuthElement.INFINITE) { - p = Math.max(p, stepPenalty); - } + p = Math.max(p, stepPenalty); + int breakClass = keep.getContext(); if (nextBreakClass != Constants.EN_AUTO) { log.trace("Forced break encountered"); p = -KnuthPenalty.INFINITE; //Overrides any keeps (see 4.8 in XSL 1.0) + breakClass = nextBreakClass; } - returnList.add(new BreakElement(penaltyPos, effPenaltyLen, p, nextBreakClass, context)); + returnList.add(new BreakElement(penaltyPos, effPenaltyLen, p, breakClass, context)); if (penaltyOrGlueLen < 0) { returnList.add(new KnuthGlue(-penaltyOrGlueLen, 0, 0, new Position(null), true)); } Index: src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java (working copy) @@ -23,6 +23,7 @@ import org.apache.fop.area.Block; import org.apache.fop.fo.flow.table.TableCaption; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; @@ -197,30 +198,17 @@ } /** {@inheritDoc} */ - public int getKeepTogetherStrength() { - int strength = KEEP_AUTO; + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; /* TODO Complete me! - strength = Math.max(strength, KeepUtil.getKeepStrength( - getTableCaptionFO().getKeepTogether().getWithinPage())); - strength = Math.max(strength, KeepUtil.getKeepStrength( - getTableCaptionFO().getKeepTogether().getWithinColumn())); - */ - strength = Math.max(strength, getParentKeepTogetherStrength()); - return strength; - } - - /** {@inheritDoc} */ - public int getKeepWithNextStrength() { - return KEEP_AUTO; - /* TODO Complete me! return KeepUtil.getCombinedBlockLevelKeepStrength( getTableCaptionFO().getKeepWithNext()); */ } /** {@inheritDoc} */ - public int getKeepWithPreviousStrength() { - return KEEP_AUTO; + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; /* TODO Complete me! return KeepUtil.getCombinedBlockLevelKeepStrength( getTableCaptionFO().getKeepWithPrevious()); Index: src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java (working copy) @@ -34,6 +34,7 @@ import org.apache.fop.fo.properties.BreakPropertySet; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.SpaceProperty; +import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.layoutmgr.inline.LineLayoutManager; import org.apache.fop.traits.MinOptMax; @@ -259,7 +260,7 @@ updateContentAreaIPDwithOverconstrainedAdjust(); - List returnedList = null; + List returnedList; List contentList = new LinkedList(); List returnList = new LinkedList(); @@ -274,7 +275,7 @@ if (!firstVisibleMarkServed) { addKnuthElementsForSpaceBefore(returnList, alignment); - context.updateKeepWithPreviousPending(getKeepWithPreviousStrength()); + context.updateKeepWithPreviousPending(getKeepWithPrevious()); } addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); @@ -316,7 +317,7 @@ } if (returnedList != null && returnedList.size() == 1 - && ((ListElement) returnedList.get(0)).isForcedBreak()) { + && ElementListUtils.startsWithForcedBreak(returnedList)) { if (curLM.isFinished() && !hasNextChildLM()) { // a descendant of this block has break-before @@ -342,7 +343,6 @@ // "wrap" the Position inside each element // moving the elements from contentList to returnList - returnedList = new LinkedList(); wrapPositionElements(contentList, returnList); return returnList; @@ -375,7 +375,6 @@ } /* end of extension */ - returnedList = new LinkedList(); wrapPositionElements(contentList, returnList); return returnList; @@ -394,7 +393,6 @@ } /* end of extension */ - returnedList = new LinkedList(); if (!contentList.isEmpty()) { wrapPositionElements(contentList, returnList); } else if (forcedBreakAfterLast == null) { @@ -417,7 +415,7 @@ returnList.add(forcedBreakAfterLast); } - context.updateKeepWithNextPending(getKeepWithNextStrength()); + context.updateKeepWithNextPending(getKeepWithNext()); setFinished(true); @@ -426,31 +424,32 @@ /** * Adds a break element to the content list between individual child elements. - * @param contentList the content list to populate - * @param context the current layout context + * @param contentList + * @param parentLC * @param childLC the currently active child layout context */ - protected void addInBetweenBreak(List contentList, LayoutContext context, - LayoutContext childLC) { + protected void addInBetweenBreak(List contentList, LayoutContext parentLC, + LayoutContext childLC) { + if (mustKeepTogether() - || context.isKeepWithNextPending() + || parentLC.isKeepWithNextPending() || childLC.isKeepWithPreviousPending()) { - int strength = getKeepTogetherStrength(); + Keep keep = getKeepTogether(); //Handle pending keep-with-next - strength = Math.max(strength, context.getKeepWithNextPending()); - context.clearKeepWithNextPending(); + keep = keep.compare(parentLC.getKeepWithNextPending()); + parentLC.clearKeepWithNextPending(); //Handle pending keep-with-previous from child LM - strength = Math.max(strength, childLC.getKeepWithPreviousPending()); + keep = keep.compare(childLC.getKeepWithPreviousPending()); childLC.clearKeepWithPreviousPending(); - int penalty = KeepUtil.getPenaltyForKeep(strength); + int penalty = keep.getPenalty(); // add a penalty to forbid or discourage a break between blocks contentList.add(new BreakElement( - new Position(this), penalty, context)); + new Position(this), penalty, keep.getContext(), parentLC)); return; } @@ -481,7 +480,7 @@ // add a null penalty to allow a break between blocks contentList.add(new BreakElement( - new Position(this), 0, context)); + new Position(this), 0, parentLC)); } } @@ -817,36 +816,80 @@ * Retrieves and returns the keep-together strength from the parent element. * @return the keep-together strength */ - protected int getParentKeepTogetherStrength() { - int strength = KEEP_AUTO; + protected Keep getParentKeepTogether() { + Keep keep = Keep.KEEP_AUTO; if (getParent() instanceof BlockLevelLayoutManager) { - strength = ((BlockLevelLayoutManager)getParent()).getKeepTogetherStrength(); + keep = ((BlockLevelLayoutManager)getParent()).getKeepTogether(); } else if (getParent() instanceof InlineLayoutManager) { if (((InlineLayoutManager) getParent()).mustKeepTogether()) { - strength = KEEP_ALWAYS; + keep = Keep.KEEP_ALWAYS; } //TODO Fix me //strength = ((InlineLayoutManager) getParent()).getKeepTogetherStrength(); } - return strength; + return keep; } /** {@inheritDoc} */ public boolean mustKeepTogether() { - return getKeepTogetherStrength() > KEEP_AUTO; + return !getKeepTogether().isAuto(); } /** {@inheritDoc} */ public boolean mustKeepWithPrevious() { - return getKeepWithPreviousStrength() > KEEP_AUTO; + return !getKeepWithPrevious().isAuto(); } /** {@inheritDoc} */ public boolean mustKeepWithNext() { - return getKeepWithNextStrength() > KEEP_AUTO; + 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 {@code UnsupportedOperationException} + * Must be implemented by the subclass, if applicable. + */ + public KeepProperty getKeepTogetherProperty() { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * Default implementation throws {@code UnsupportedOperationException} + * Must be implemented by the subclass, if applicable. + */ + public KeepProperty getKeepWithPreviousProperty() { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * Default implementation throws {@code UnsupportedOperationException} + * Must be implemented by the subclass, if applicable. + */ + public KeepProperty getKeepWithNextProperty() { + throw new UnsupportedOperationException(); + } + + /** * Adds the unresolved elements for border and padding to a layout context so break * possibilities can be properly constructed. * @param context the layout context Index: src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (working copy) @@ -22,12 +22,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.fo.FONode; +import org.apache.fop.fo.Constants; /** * The set of nodes is sorted into lines indexed into activeLines. * The nodes in each line are linked together in a single linked list by the - * KnuthNode.next field. The activeLines array contains a link to the head of + * {@link KnuthNode#next} field. The activeLines array contains a link to the head of * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'. *

* The set of active nodes can be traversed by @@ -57,13 +57,20 @@ /** wrap-option = "no-wrap". */ public static final int ONLY_FORCED_BREAKS = 2; + static class FitnessClasses { + static final int VERY_TIGHT = 0; + static final int TIGHT = 1; + static final int LOOSE = 2; + static final int VERY_LOOSE = 3; + + static final String[] NAMES = { "VERY TIGHT", "TIGHT", "LOOSE", "VERY LOOSE" }; + } + // parameters of Knuth's algorithm: - /** Penalty value for flagged penalties. */ - private int flaggedPenalty = 50; /** Demerit for consecutive lines ending at flagged penalties. */ - protected int repeatedFlaggedDemerit = 50; + protected int repeatedFlaggedDemerit = KnuthPenalty.FLAGGED_PENALTY; /** Demerit for consecutive lines belonging to incompatible fitness classes . */ - protected int incompatibleFitnessDemerit = 50; + protected int incompatibleFitnessDemerit = KnuthPenalty.FLAGGED_PENALTY; /** Maximum number of consecutive lines ending with a flagged penalty. * Only a value >= 1 is a significant limit. */ @@ -110,7 +117,7 @@ /** Alignment of the paragraph's last line. */ protected int alignmentLast; /** Used to handle the text-indent property (indent the first line of a paragraph). */ - protected boolean bFirst; + protected boolean indentFirstPart; /** * The set of active nodes in ascending line order. For each line l, activeLines[2l] contains a @@ -151,30 +158,35 @@ protected BestRecords best; - /** {@inheritDoc} */ private boolean partOverflowRecoveryActivated = true; private KnuthNode lastRecovered; /** * Create a new instance. - * @param align alignment of the paragraph/page. One of EN_START, EN_JUSTIFY, etc. For - * pages EN_BEFORE, EN_AFTER are mapped to the corresponding inline properties - * (EN_START, EN_END) + * + * @param align alignment of the paragraph/page. One of {@link Constants#EN_START}, + * {@link Constants#EN_JUSTIFY}, {@link Constants#EN_CENTER}, + * {@link Constants#EN_END}. + * For pages, {@link Constants#EN_BEFORE} and {@link Constants#EN_AFTER} + * are mapped to the corresponding inline properties, + * {@link Constants#EN_START} and {@link Constants#EN_END}. * @param alignLast alignment of the paragraph's last line - * @param first for the text-indent property (indent the first line of a paragraph) - * @param partOverflowRecovery true if too long elements should be moved to the next line/part - * @param maxFlagCount maximum allowed number of consecutive lines ending at a flagged penalty - * item + * @param first for the text-indent property ({@code true} if the first line + * of a paragraph should be indented) + * @param partOverflowRecovery {@code true} if too long elements should be moved to + * the next line/part + * @param maxFlagCount maximum allowed number of consecutive lines ending at a flagged penalty + * item */ public BreakingAlgorithm(int align, int alignLast, boolean first, boolean partOverflowRecovery, int maxFlagCount) { - alignment = align; - alignmentLast = alignLast; - bFirst = first; + this.alignment = align; + this.alignmentLast = alignLast; + this.indentFirstPart = first; this.partOverflowRecoveryActivated = partOverflowRecovery; this.best = new BestRecords(); - maxFlaggedPenaltiesCount = maxFlagCount; + this.maxFlaggedPenaltiesCount = maxFlagCount; } @@ -249,7 +261,8 @@ return ""; + + " dem:" + totalDemerits + + " fitness:" + FitnessClasses.NAMES[fitness] + ">"; } } @@ -386,7 +399,6 @@ * one of the optimal breakpoints * @param sequence the corresponding paragraph * @param total the number of lines into which the paragraph will be broken - * @see #calculateBreakPoints(KnuthNode, KnuthSequence, int) */ public abstract void updateData2(KnuthNode bestActiveNode, KnuthSequence sequence, @@ -404,13 +416,18 @@ return findBreakingPoints(par, 0, threshold, force, allowedBreaks); } - /** Finds an optimal set of breakpoints for the given paragraph. - * @param par the paragraph to break - * @param startIndex index of the Knuth element at which the breaking must start - * @param threshold upper bound of the adjustment ratio - * @param force true if a set of breakpoints must be found even if there are no - * feasible ones - * @param allowedBreaks one of ONLY_FORCED_BREAKS, NO_FLAGGED_PENALTIES, ALL_BREAKS + /** + * Finds an optimal set of breakpoints for the given paragraph. + * + * @param par the paragraph to break + * @param startIndex index of the Knuth element at which the breaking must start + * @param threshold upper bound of the adjustment ratio + * @param force {@code true} if a set of breakpoints must be found, even + * if there are no feasible ones + * @param allowedBreaks the type(s) of breaks allowed. One of {@link #ONLY_FORCED_BREAKS}, + * {@link #NO_FLAGGED_PENALTIES} or {@link #ALL_BREAKS}. + * + * @return the number of effective breaks */ public int findBreakingPoints(KnuthSequence par, int startIndex, double threshold, boolean force, @@ -418,142 +435,67 @@ this.par = par; this.threshold = threshold; this.force = force; - //this.lineWidth = lineWidth; + + // initialize the algorithm initialize(); - activeLines = new KnuthNode[20]; - - // reset lastTooShort and lastTooLong, as they could be not null - // because of previous calls to findBreakingPoints - lastTooShort = lastTooLong = null; - // reset startLine and endLine - startLine = endLine = 0; - // current element in the paragraph - KnuthElement thisElement = null; // previous element in the paragraph is a KnuthBox? boolean previousIsBox = false; - // index of the first KnuthBox in the sequence - int firstBoxIndex = startIndex; - if (alignment != org.apache.fop.fo.Constants.EN_CENTER) { - while (par.size() > firstBoxIndex - && !((KnuthElement) par.get(firstBoxIndex)).isBox()) { - firstBoxIndex++; - } + // index of the first KnuthBox in the sequence, in case of non-centered + // alignment. For centered alignment, we need to take into account preceding + // penalties+glues used for the filler spaces + if (alignment != Constants.EN_CENTER) { + startIndex = par.getFirstBoxIndex(); } + startIndex = (startIndex == -1) ? 0 : startIndex; // create an active node representing the starting point - activeLines = new KnuthNode[20]; - addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); + addNode(0, createNode(startIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); + KnuthNode lastForced = getNode(0); + if (log.isTraceEnabled()) { log.trace("Looping over " + (par.size() - startIndex) + " elements"); + log.trace(par); } - KnuthNode lastForced = getNode(0); + // main loop + int elementIndex = startIndex; + while (!isEndOfParagraph(elementIndex)) { - // main loop - for (int i = startIndex; i < par.size(); i++) { - thisElement = getElement(i); - if (thisElement.isBox()) { - // a KnuthBox object is not a legal line break - totalWidth += thisElement.getW(); - previousIsBox = true; - handleBox((KnuthBox) thisElement); - } else if (thisElement.isGlue()) { - // a KnuthGlue object is a legal line break - // only if the previous object is a KnuthBox - // consider these glues according to the value of allowedBreaks - if (previousIsBox - && !(allowedBreaks == ONLY_FORCED_BREAKS)) { - considerLegalBreak(thisElement, i); - } - totalWidth += thisElement.getW(); - totalStretch += thisElement.getY(); - totalShrink += thisElement.getZ(); - previousIsBox = false; - } else { - // a KnuthPenalty is a legal line break - // only if its penalty is not infinite; - // consider all penalties, non-flagged penalties or non-forcing penalties - // according to the value of allowedBreaks - if (((KnuthPenalty) thisElement).getP() < KnuthElement.INFINITE - && (!(allowedBreaks == NO_FLAGGED_PENALTIES) - || !(((KnuthPenalty) thisElement).isFlagged())) - && (!(allowedBreaks == ONLY_FORCED_BREAKS) - || ((KnuthPenalty) thisElement).getP() == -KnuthElement.INFINITE)) { - considerLegalBreak(thisElement, i); - } - previousIsBox = false; - } + previousIsBox = handleElementAt( + elementIndex, previousIsBox, allowedBreaks).isBox(); + if (activeNodeCount == 0) { + if (!force) { log.debug("Could not find a set of breaking points " + threshold); return 0; } + // lastDeactivated was a "good" break, while lastTooShort and lastTooLong // were "bad" breaks since the beginning; // if it is not the node we just restarted from, lastDeactivated can // replace either lastTooShort or lastTooLong if (lastDeactivated != null && lastDeactivated != lastForced) { - if (lastDeactivated.adjustRatio > 0) { - lastTooShort = lastDeactivated; - } else { - lastTooLong = lastDeactivated; - } + replaceLastDeactivated(); } - if (lastTooShort == null || lastForced.position == lastTooShort.position) { - if (isPartOverflowRecoveryActivated()) { - if (this.lastRecovered == null) { - this.lastRecovered = lastTooLong; - if (log.isDebugEnabled()) { - log.debug("Recovery point: " + lastRecovered); - } - } - // content would overflow, insert empty line/page and try again - KnuthNode node = createNode( - lastTooLong.previous.position, lastTooLong.previous.line + 1, 1, - 0, 0, 0, - 0, 0, 0, - 0, 0, lastTooLong.previous); - lastForced = node; - node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1; - if (log.isDebugEnabled()) { - log.debug("first part doesn't fit into line, recovering: " - + node.fitRecoveryCounter); - } - if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) { - while (lastForced.fitRecoveryCounter > 0) { - lastForced = lastForced.previous; - lastDeactivated = lastForced.previous; - startLine--; - endLine--; - } - lastForced = this.lastRecovered; - this.lastRecovered = null; - startLine = lastForced.line; - endLine = lastForced.line; - log.debug("rolled back..."); - } - } else { - lastForced = lastTooLong; - } + + if (lastTooShort == null + || lastForced.position == lastTooShort.position) { + lastForced = recoverFromOverflow(); } else { lastForced = lastTooShort; this.lastRecovered = null; } - - if (log.isDebugEnabled()) { - log.debug("Restarting at node " + lastForced); - } - i = restartFrom(lastForced, i); + elementIndex = restartFrom(lastForced, elementIndex); + } else { + elementIndex++; } } + finish(); - if (log.isTraceEnabled()) { - log.trace("Main loop completed " + activeNodeCount); - log.trace("Active nodes=" + toString("")); - } // there is at least one set of breaking points // select one or more active nodes, removing the others from the list @@ -571,42 +513,33 @@ return line; } - /** - * This method tries to find the context FO for a position in a KnuthSequence. - * @param seq the KnuthSequence to inspect - * @param position the index of the position in the KnuthSequence - * @return the requested context FO note or null, if no context node could be determined - */ - private FONode findContextFO(KnuthSequence seq, int position) { - ListElement el = seq.getElement(position); - while (el.getLayoutManager() == null && position < seq.size() - 1) { - position++; - el = seq.getElement(position); + protected KnuthNode recoverFromTooLong(KnuthNode lastTooLong) { + if (log.isDebugEnabled()) { + log.debug("Recovering from too long: " + lastTooLong); } - Position pos = (el != null ? el.getPosition() : null); - LayoutManager lm = (pos != null ? pos.getLM() : null); - while (pos instanceof NonLeafPosition) { - pos = ((NonLeafPosition)pos).getPosition(); - if (pos != null && pos.getLM() != null) { - lm = pos.getLM(); - } - } - if (lm != null) { - return lm.getFObj(); - } else { - return null; - } + + // content would overflow, insert empty line/page and try again + return createNode( + lastTooLong.previous.position, lastTooLong.previous.line + 1, 1, + 0, 0, 0, + 0, 0, 0, + 0, 0, lastTooLong.previous); } - /** Resets the algorithm's variables. */ + /** Initializes the algorithm's variables. */ protected void initialize() { this.totalWidth = 0; this.totalStretch = 0; this.totalShrink = 0; + this.lastTooShort = this.lastTooLong = null; + this.startLine = this.endLine = 0; + this.activeLines = new KnuthNode[20]; } - /** Creates a new active node for a feasible breakpoint at the given position. Only + /** + * Creates a new active node for a feasible breakpoint at the given position. Only * called in forced mode. + * * @param position index of the element in the Knuth sequence * @param line number of the line ending at the breakpoint * @param fitness fitness class of the line ending at the breakpoint. One of 0, 1, 2, 3. @@ -621,6 +554,7 @@ * @param difference difference between target and actual line width * @param totalDemerits minimum total demerits up to the breakpoint * @param previous active node for the preceding breakpoint + * @return a new node */ protected KnuthNode createNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, @@ -646,11 +580,179 @@ best.getNode(fitness)); } - /** Empty method, hook for subclasses. */ + /** + * Return the last node that yielded a too short line. + * @return the node corresponding to the last too short line + */ + protected final KnuthNode getLastTooShort() { + return this.lastTooShort; + } + + /** + * @param elementIndex the index to check + * + * @return {@code true} if the given element index points to + * the end of the current paragraph, or beyond + */ + protected final boolean isEndOfParagraph(int elementIndex) { + return (elementIndex - par.size() >= 0); + } + + /** + * Generic handler for a {@link KnuthElement} at the given {@code position}, + * taking into account whether the preceding element was a box, and which + * type(s) of breaks are allowed. + * Non-overridable. This method simply serves to route the call to one of the + * more specific handlers ({@link #handleBox(KnuthBox)}, + * {@link #handleGlueAt(KnuthGlue,int,boolean,int)} or + * {@link #handlePenaltyAt(KnuthPenalty,int,int)}. The specialized handlers + * can be overridden by subclasses to add to or modify the default behavior + * for the different types of elements. + * + * @param position the position index of the element in the list + * @param previousIsBox {@code true} if the previous element is a box + * @param allowedBreaks the type(s) of breaks allowed; should be one + * of {@link #ALL_BREAKS}, {@link #NO_FLAGGED_PENALTIES} + * or {@link #ONLY_FORCED_BREAKS} + * @return the handled element + */ + protected final KnuthElement handleElementAt(int position, + boolean previousIsBox, + int allowedBreaks) { + KnuthElement element = getElement(position); + if (element.isBox()) { + handleBox((KnuthBox) element); + } else if (element.isGlue()) { + handleGlueAt((KnuthGlue) element, position, previousIsBox, allowedBreaks); + } else if (element.isPenalty()){ + handlePenaltyAt((KnuthPenalty) element, position, allowedBreaks); + } else { + throw new IllegalArgumentException( + "Unknown KnuthElement type: expecting KnuthBox, KnuthGlue or KnuthPenalty"); + } + return element; + } + + /** + * Handle a {@link KnuthBox}. + * + * @param box the {@link KnuthBox} to handle + */ protected void handleBox(KnuthBox box) { + // a KnuthBox object is not a legal line break, + // just add the width to the total + totalWidth += box.getW(); } + /** + * Handle a {@link KnuthGlue} at the given position, + * taking into account the additional parameters. + * + * @param glue the {@link KnuthGlue} to handle + * @param position the position of the glue in the list + * @param previousIsBox {@code true} if the preceding element is a box + * @param allowedBreaks the type of breaks that are allowed + */ + protected void handleGlueAt(KnuthGlue glue, int position, + boolean previousIsBox, int allowedBreaks) { + // a KnuthGlue object is a legal line break + // only if the previous object is a KnuthBox + // consider these glues according to the value of allowedBreaks + if (previousIsBox + && !(allowedBreaks == ONLY_FORCED_BREAKS)) { + considerLegalBreak(glue, position); + } + totalWidth += glue.getW(); + totalStretch += glue.getY(); + totalShrink += glue.getZ(); + } + + /** + * Handle a {@link KnuthPenalty} at the given position, + * taking into account the type of breaks allowed. + * + * @param penalty the {@link KnuthPenalty} to handle + * @param position the position of the penalty in the list + * @param allowedBreaks the type of breaks that are allowed + */ + protected void handlePenaltyAt(KnuthPenalty penalty, int position, + int allowedBreaks) { + // a KnuthPenalty is a legal line break + // only if its penalty is not infinite; + // consider all penalties, non-flagged penalties or non-forcing penalties + // according to the value of allowedBreaks + if (((penalty.getP() < KnuthElement.INFINITE) + && (!(allowedBreaks == NO_FLAGGED_PENALTIES) || !penalty.isFlagged()) + && (!(allowedBreaks == ONLY_FORCED_BREAKS) + || penalty.isForcedBreak()))) { + considerLegalBreak(penalty, position); + } + } + + /** + * Replace the last too-long or too-short node by the last deactivated + * node, if applicable. + */ + protected final void replaceLastDeactivated() { + if (lastDeactivated.adjustRatio > 0) { + //last deactivated was too short + lastTooShort = lastDeactivated; + } else { + //last deactivated was too long or exactly the right width + lastTooLong = lastDeactivated; + } + } + + /** + * Recover from an overflow condition. + * + * @return the new {@code lastForced} node + */ + protected KnuthNode recoverFromOverflow() { + KnuthNode lastForced; + if (isPartOverflowRecoveryActivated()) { + if (lastRecovered == null) { + lastRecovered = lastTooLong; + if (log.isDebugEnabled()) { + log.debug("Recovery point: " + lastRecovered); + } + } + KnuthNode node = recoverFromTooLong(lastTooLong); + lastForced = node; + node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1; + if (log.isDebugEnabled()) { + log.debug("first part doesn't fit into line, recovering: " + + node.fitRecoveryCounter); + } + if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) { + while (lastForced.fitRecoveryCounter > 0 + && lastForced.previous != null) { + lastForced = lastForced.previous; + lastDeactivated = lastForced.previous; + } + lastForced = lastRecovered; + lastRecovered = null; + startLine = lastForced.line; + endLine = lastForced.line; + log.debug("rolled back..."); + } + } else { + lastForced = lastTooLong; + } + return lastForced; + } + + /** + * Restart from the given node at the given index. + * + * @param restartingNode the {@link KnuthNode} to restart from + * @param currentIndex the current position index + * @return the index of the restart point + */ protected int restartFrom(KnuthNode restartingNode, int currentIndex) { + if (log.isDebugEnabled()) { + log.debug("Restarting at node " + restartingNode); + } restartingNode.totalDemerits = 0; addNode(restartingNode.line, restartingNode); startLine = restartingNode.line; @@ -672,7 +774,8 @@ return restartingIndex; } - /** Determines if the given breakpoint is a feasible breakpoint. That is, if a decent + /** + * Determines if the given breakpoint is a feasible breakpoint. That is, if a decent * line may be built between one of the currently active nodes and this breakpoint. * @param element the paragraph's element to consider * @param elementIdx the element's index inside the paragraph @@ -689,6 +792,9 @@ lastDeactivated = null; lastTooLong = null; for (int line = startLine; line < endLine; line++) { + if (!elementCanEndLine(element, line + 1)) { + continue; + } for (KnuthNode node = getNode(line); node != null; node = node.next) { if (node.position == elementIdx) { continue; @@ -697,6 +803,7 @@ double r = computeAdjustmentRatio(node, difference); int availableShrink = totalShrink - node.totalShrink; int availableStretch = totalStretch - node.totalStretch; + if (log.isTraceEnabled()) { log.trace("\tr=" + r + " difference=" + difference); log.trace("\tline=" + line); @@ -704,22 +811,17 @@ // The line would be too long. if (r < -1 || element.isForcedBreak()) { - // Deactivate node. - if (log.isTraceEnabled()) { - log.trace("Removing " + node); - } - removeNode(line, node); - lastDeactivated = compareNodes(lastDeactivated, node); + deactivateNode(node, line); } + int fitnessClass = computeFitness(r); + double demerits = computeDemerits(node, element, fitnessClass, r); // The line is within the available shrink and the threshold. if (r >= -1 && r <= threshold) { - int fitnessClass = computeFitness(r); - double demerits = computeDemerits(node, element, fitnessClass, r); if (log.isTraceEnabled()) { log.trace("\tDemerits=" + demerits); - log.trace("\tFitness class=" + fitnessClass); + log.trace("\tFitness class=" + FitnessClasses.NAMES[fitnessClass]); } if (demerits < best.getDemerits(fitnessClass)) { @@ -733,8 +835,6 @@ // The line is way too short, but we are in forcing mode, so a node is // calculated and stored in lastValidNode. if (force && (r <= -1 || r > threshold)) { - int fitnessClass = computeFitness(r); - double demerits = computeDemerits(node, element, fitnessClass, r); int newWidth = totalWidth; int newStretch = totalStretch; int newShrink = totalShrink; @@ -759,6 +859,7 @@ } if (r <= -1) { + log.debug("Considering tooLong, demerits=" + demerits); if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { lastTooLong = createNode(elementIdx, line + 1, fitnessClass, newWidth, newStretch, newShrink, @@ -792,6 +893,37 @@ } /** + * Deactivate the given node + * + * @param node the node + * @param line the line number + */ + protected void deactivateNode(KnuthNode node, int line) { + // Deactivate node... + if (log.isTraceEnabled()) { + log.trace("Removing " + node); + } + removeNode(line, node); + // ... and remember it, if it was a good candidate + lastDeactivated = compareNodes(lastDeactivated, node); + } + + /** + * Check if the given {@link KnuthElement} at the given line number + * can end a "line" (= a "column" or "page" in page-breaking context). + * + * @param element the {@link KnuthElement} to check. + * @param line the "line" number + * @return {@code true} if the given element can terminate a "line" + */ + protected boolean elementCanEndLine(KnuthElement element, int line) { + // Default implementation: the element can end a line if either it is not + // a penalty, or it is a penalty with value less than KnuthElement.INFINITE. + return !(element.isPenalty()) + || element.getP() < KnuthPenalty.INFINITE; + } + + /** * Adds new active nodes for breaks at the given element. * @param line number of the previous line; this element will end line number (line+1) * @param elementIdx the element's index @@ -832,7 +964,7 @@ // by line number and position; if (log.isTraceEnabled()) { log.trace("\tInsert new break in list of " + activeNodeCount - + " from fitness class " + i); + + " from fitness class " + FitnessClasses.NAMES[i]); } KnuthNode newNode = createNode(elementIdx, line + 1, i, newWidth, newStretch, newShrink); @@ -846,8 +978,9 @@ * Return the difference between the natural width of a line that would be made * between the given active node and the given element, and the available width of the * real line. - * @param activeNode node for the previous breakpoint - * @param element currently considered breakpoint + * @param activeNode node for the previous breakpoint + * @param element currently considered breakpoint + * @param elementIndex index of the element that is considered as a breakpoint * @return The difference in width. Positive numbers mean extra space in the line, * negative number that the line overflows. */ @@ -862,7 +995,7 @@ } /** - * Return the adjust ration needed to make up for the difference. A ration of + * Return the adjustment ratio needed to make up for the difference. A ratio of *

- * @param activeNode - * @param difference - * @return The ration. + * @param activeNode the currently active node + * @param difference the difference between content-length and available width + * @return The adjustment ratio. */ protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { // compute the adjustment ratio @@ -901,18 +1034,18 @@ * very tight or very loose). * See the section on "More Bells and Whistles" in Knuth's * "Breaking Paragraphs Into Lines". - * @param r + * @param adjustRatio the adjustment ratio * @return the fitness class */ - private int computeFitness(double r) { - if (r < -0.5) { - return 0; - } else if (r <= 0.5) { - return 1; - } else if (r <= 1) { - return 2; + private int computeFitness(double adjustRatio) { + if (adjustRatio < -0.5) { + return FitnessClasses.VERY_TIGHT; + } else if (adjustRatio <= 0.5) { + return FitnessClasses.TIGHT; + } else if (adjustRatio <= 1) { + return FitnessClasses.LOOSE; } else { - return 3; + return FitnessClasses.VERY_LOOSE; } } @@ -933,12 +1066,14 @@ // compute demerits double f = Math.abs(r); f = 1 + 100 * f * f * f; - if (element.isPenalty() && element.getP() >= 0) { - f += element.getP(); - demerits = f * f; - } else if (element.isPenalty() && !element.isForcedBreak()) { + if (element.isPenalty()) { double penalty = element.getP(); - demerits = f * f - penalty * penalty; + if (penalty >= 0) { + f += penalty; + demerits = f * f; + } else if (!element.isForcedBreak()) { + demerits = f * f - penalty * penalty; + } } else { demerits = f * f; } @@ -982,7 +1117,15 @@ return demerits; } + /** + * Hook for subclasses to trigger special behavior after ending the + * main loop in {@link #findBreakingPoints(KnuthSequence,int,double,boolean,int)} + */ protected void finish() { + if (log.isTraceEnabled()) { + log.trace("Main loop completed " + activeNodeCount); + log.trace("Active nodes=" + toString("")); + } } /** @@ -1106,10 +1249,10 @@ sb.append("[\n"); for (int i = startLine; i < endLine; i++) { for (KnuthNode node = getNode(i); node != null; node = node.next) { - sb.append(prepend + "\t" + node + ",\n"); + sb.append(prepend).append('\t').append(node).append(",\n"); } } - sb.append(prepend + "]"); + sb.append(prepend).append("]"); return sb.toString(); } Index: src/java/org/apache/fop/layoutmgr/KnuthSequence.java =================================================================== --- src/java/org/apache/fop/layoutmgr/KnuthSequence.java (revision 786985) +++ src/java/org/apache/fop/layoutmgr/KnuthSequence.java (working copy) @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr; +import org.apache.fop.util.ListUtil; + import java.util.ArrayList; import java.util.List; import java.util.ListIterator; @@ -26,10 +28,10 @@ /** * Represents a list of Knuth elements. */ -/** - * - */ public abstract class KnuthSequence extends ArrayList { + + private int firstBoxIndex = -1; + /** * Creates a new and empty list. */ @@ -132,11 +134,9 @@ * @return the last element of this sequence. */ public ListElement getLast() { - int idx = size(); - if (idx == 0) { - return null; - } - return (ListElement) get(idx - 1); + return (isEmpty() + ? null + : (ListElement) ListUtil.getLast(this)); } /** @@ -144,25 +144,63 @@ * @return the removed element. */ public ListElement removeLast() { - int idx = size(); - if (idx == 0) { - return null; - } - return (ListElement) remove(idx - 1); + return (isEmpty() + ? null + : (ListElement) ListUtil.removeLast(this)); } /** - * @param index The index of the element to be returned + * @param index the index of the element to be returned * @return the element at index index. */ public ListElement getElement(int index) { - return (ListElement) get(index); + return ((index >= size() || index < 0) + ? null + : (ListElement) get(index)); } + /** @return the position index of the first box in this sequence */ + protected int getFirstBoxIndex() { + if (isEmpty()) { + firstBoxIndex = -1; + } else if (firstBoxIndex == -1){ + //not yet initialized, or dirty + firstBoxIndex = getFirstBoxIndex(0); + } + return firstBoxIndex; + } + /** + * @param fromIndex the index to start from + * @return the position index of the first box, after the given position + */ + protected int getFirstBoxIndex(int fromIndex) { + if (isEmpty() || fromIndex >= size() || fromIndex < 0) { + return -1; + } else { + ListElement element = null; + int posIndex = fromIndex; + int lastIndex = size() - 1; + while (posIndex < lastIndex + && !(element = getElement(posIndex)).isBox()) { + posIndex++; + } + if (element != null && element.isBox()) { + return posIndex; + } + } + return -1; + } + + /** * Is this an inline or a block sequence? * @return true if this is an inline sequence */ public abstract boolean isInlineSequence(); + /** {@inheritDoc} */ + public String toString() { + return ""; + } + } Index: src/java/org/apache/fop/fo/flow/table/EffRow.java =================================================================== --- src/java/org/apache/fop/fo/flow/table/EffRow.java (revision 786985) +++ src/java/org/apache/fop/fo/flow/table/EffRow.java (working copy) @@ -23,8 +23,7 @@ import java.util.List; import org.apache.fop.fo.Constants; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; -import org.apache.fop.layoutmgr.KeepUtil; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.table.TableRowIterator; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.BreakUtil; @@ -170,20 +169,19 @@ * * @return the strength of the keep-with-previous constraint */ - public int getKeepWithPreviousStrength() { - int strength = BlockLevelLayoutManager.KEEP_AUTO; + public Keep getKeepWithPrevious() { + Keep keep = Keep.KEEP_AUTO; TableRow row = getTableRow(); if (row != null) { - strength = Math.max(strength, - KeepUtil.getCombinedBlockLevelKeepStrength(row.getKeepWithPrevious())); + keep = Keep.getKeep(row.getKeepWithPrevious()); } for (Iterator iter = gridUnits.iterator(); iter.hasNext();) { GridUnit gu = (GridUnit) iter.next(); if (gu.isPrimary()) { - strength = Math.max(strength, gu.getPrimary().getKeepWithPreviousStrength()); + keep = keep.compare(gu.getPrimary().getKeepWithPrevious()); } } - return strength; + return keep; } /** @@ -192,20 +190,19 @@ * * @return the strength of the keep-with-next constraint */ - public int getKeepWithNextStrength() { - int strength = BlockLevelLayoutManager.KEEP_AUTO; + public Keep getKeepWithNext() { + Keep keep = Keep.KEEP_AUTO; TableRow row = getTableRow(); if (row != null) { - strength = Math.max(strength, - KeepUtil.getCombinedBlockLevelKeepStrength(row.getKeepWithNext())); + keep = Keep.getKeep(row.getKeepWithNext()); } for (Iterator iter = gridUnits.iterator(); iter.hasNext();) { GridUnit gu = (GridUnit) iter.next(); if (!gu.isEmpty() && gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()) { - strength = Math.max(strength, gu.getPrimary().getKeepWithNextStrength()); + keep = keep.compare(gu.getPrimary().getKeepWithNext()); } } - return strength; + return keep; } /** @@ -213,16 +210,13 @@ * not take the parent table's keeps into account! * @return the keep-together strength */ - public int getKeepTogetherStrength() { + public Keep getKeepTogether() { TableRow row = getTableRow(); - int strength = BlockLevelLayoutManager.KEEP_AUTO; + Keep keep = Keep.KEEP_AUTO; if (row != null) { - strength = Math.max(strength, KeepUtil.getKeepStrength( - row.getKeepTogether().getWithinPage())); - strength = Math.max(strength, KeepUtil.getKeepStrength( - row.getKeepTogether().getWithinColumn())); + keep = Keep.getKeep(row.getKeepTogether()); } - return strength; + return keep; } /** Index: src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java =================================================================== --- src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java (revision 786985) +++ src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java (working copy) @@ -19,14 +19,13 @@ package org.apache.fop.fo.flow.table; -import java.util.LinkedList; import java.util.List; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.ElementListUtils; +import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.table.TableCellLayoutManager; /** @@ -54,8 +53,8 @@ private boolean isSeparateBorderModel; private int halfBorderSeparationBPD; - private int keepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO; - private int keepWithNext = BlockLevelLayoutManager.KEEP_AUTO; + private Keep keepWithPrevious = Keep.KEEP_AUTO; + private Keep keepWithNext = Keep.KEEP_AUTO; private int breakBefore = Constants.EN_AUTO; private int breakAfter = Constants.EN_AUTO; @@ -334,16 +333,16 @@ * * @return the keep-with-previous strength */ - public int getKeepWithPreviousStrength() { + public Keep getKeepWithPrevious() { return keepWithPrevious; } /** * Don't use, reserved for TableCellLM. TODO - * @param strength the keep strength + * @param keep the keep strength */ - public void setKeepWithPreviousStrength(int strength) { - this.keepWithPrevious = strength; + public void setKeepWithPrevious(Keep keep) { + this.keepWithPrevious = keep; } /** @@ -352,16 +351,16 @@ * * @return the keep-with-next strength */ - public int getKeepWithNextStrength() { + public Keep getKeepWithNext() { return keepWithNext; } /** * Don't use, reserved for TableCellLM. TODO - * @param strength the keep strength + * @param keep the keep strength */ - public void setKeepWithNextStrength(int strength) { - this.keepWithNext = strength; + public void setKeepWithNext(Keep keep) { + this.keepWithNext = keep; } /** Index: test/layoutengine/standard-testcases/block_keep_within-column.xml =================================================================== --- test/layoutengine/standard-testcases/block_keep_within-column.xml (revision 0) +++ test/layoutengine/standard-testcases/block_keep_within-column.xml (revision 0) @@ -0,0 +1,140 @@ + + + + + +

+ This test checks whether keeps within-column are respected. +

+
+ + + + + + + + + + + + + [BOB-1] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-1] + + + [BOB-2] foo bar foo bar foo bar foo bar foo [EOB-2] + + + + + + [BOB-3] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + + [BOB-3a] foo bar foo bar foo bar foo bar foo [EOB-3a] + + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-3] + + + + + + [BOB-4] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + + [BOB-4a] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-4a] + + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-4] + + + + + + + [BOB-5a] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-5a] + + + [BOB-5b] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-5b] + + + [BOB-5c] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-5c] + + + + + + + + [BOB-6a] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-6a] + + + [BOB-6b] foo bar foo bar foo bar foo bar foo bar [EOB-6b] + + + [BOB-6c] foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar [EOB-6c] + + + + + + + + + + +
+ Index: test/layoutengine/standard-testcases/inline_block_nested_6.xml =================================================================== --- test/layoutengine/standard-testcases/inline_block_nested_6.xml (revision 786985) +++ test/layoutengine/standard-testcases/inline_block_nested_6.xml (working copy) @@ -52,16 +52,15 @@ 5 - 6 + 5 - 6 + 3 - 5 - 3 + 14 Index: test/layoutengine/standard-testcases/table-row_keep-together.xml =================================================================== --- test/layoutengine/standard-testcases/table-row_keep-together.xml (revision 786985) +++ test/layoutengine/standard-testcases/table-row_keep-together.xml (working copy) @@ -64,10 +64,10 @@ - - - 3 + + + 5 Index: test/layoutengine/standard-testcases/table_keep-together.xml =================================================================== --- test/layoutengine/standard-testcases/table_keep-together.xml (revision 786985) +++ test/layoutengine/standard-testcases/table_keep-together.xml (working copy) @@ -101,9 +101,11 @@ - + + + 3