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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <ul>
-     *   <li>"auto": returns 0</li>
-     *   <li>"always": returns KnuthElement.INFINITE</li>
-     *   <li>otherwise: returns KnuthElement.INFINITE - 1</li>
-     * </ul>
-     * @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 <a href="http://www.w3.org/TR/xsl/#d0e26492">keep properties</a>.
+ */
+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.
+     * <em>Note: integer keep-values will all result in the same penalty value.</em>
+     * @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&lt;List&lt;KnuthElement&gt;&gt;, 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<x<partCount) which denotes the first part on the last page
      * generated by the current element list.
      * @param partCount Number of parts determined by the breaking algorithm
@@ -272,4 +315,4 @@
         return page;
     }
 
-}
\ No newline at end of file
+}
Index: src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java	(revision 786985)
+++ src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java	(working copy)
@@ -29,10 +29,10 @@
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.fo.flow.ListBlock;
+import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
 import org.apache.fop.layoutmgr.ConditionalElementListener;
 import org.apache.fop.layoutmgr.ElementListUtils;
-import org.apache.fop.layoutmgr.KeepUtil;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.LayoutManager;
 import org.apache.fop.layoutmgr.NonLeafPosition;
@@ -279,21 +279,18 @@
     }
 
     /** {@inheritDoc} */
-    public int getKeepTogetherStrength() {
-        int strength = KeepUtil.getCombinedBlockLevelKeepStrength(
-                getListBlockFO().getKeepTogether());
-        strength = Math.max(strength, getParentKeepTogetherStrength());
-        return strength;
+    public KeepProperty getKeepTogetherProperty() {
+        return getListBlockFO().getKeepTogether();
     }
 
     /** {@inheritDoc} */
-    public int getKeepWithNextStrength() {
-        return KeepUtil.getCombinedBlockLevelKeepStrength(getListBlockFO().getKeepWithNext());
+    public KeepProperty getKeepWithPreviousProperty() {
+        return getListBlockFO().getKeepWithPrevious();
     }
 
     /** {@inheritDoc} */
-    public int getKeepWithPreviousStrength() {
-        return KeepUtil.getCombinedBlockLevelKeepStrength(getListBlockFO().getKeepWithPrevious());
+    public KeepProperty getKeepWithNextProperty() {
+        return getListBlockFO().getKeepWithNext();
     }
 
     /** {@inheritDoc} */
Index: src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java	(revision 786985)
+++ src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java	(working copy)
@@ -32,13 +32,13 @@
 import org.apache.fop.fo.flow.ListItem;
 import org.apache.fop.fo.flow.ListItemBody;
 import org.apache.fop.fo.flow.ListItemLabel;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
+import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
 import org.apache.fop.layoutmgr.BreakElement;
 import org.apache.fop.layoutmgr.ConditionalElementListener;
 import org.apache.fop.layoutmgr.ElementListObserver;
 import org.apache.fop.layoutmgr.ElementListUtils;
-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;
@@ -83,8 +83,8 @@
     private MinOptMax effSpaceBefore;
     private MinOptMax effSpaceAfter;
 
-    private int keepWithNextPendingOnLabel;
-    private int keepWithNextPendingOnBody;
+    private Keep keepWithNextPendingOnLabel;
+    private Keep keepWithNextPendingOnBody;
 
     private int listItemHeight;
 
@@ -254,8 +254,8 @@
 
         context.updateKeepWithNextPending(this.keepWithNextPendingOnLabel);
         context.updateKeepWithNextPending(this.keepWithNextPendingOnBody);
-        context.updateKeepWithNextPending(getKeepWithNextStrength());
-        context.updateKeepWithPreviousPending(getKeepWithPreviousStrength());
+        context.updateKeepWithNextPending(getKeepWithNext());
+        context.updateKeepWithPreviousPending(getKeepWithPrevious());
 
         setFinished(true);
         resetSpaces();
@@ -276,16 +276,16 @@
         int totalHeight = Math.max(fullHeights[0], fullHeights[1]);
         int step;
         int addedBoxHeight = 0;
-        int keepWithNextActive = BlockLevelLayoutManager.KEEP_AUTO;
+        Keep keepWithNextActive = Keep.KEEP_AUTO;
 
         LinkedList returnList = new LinkedList();
         while ((step = getNextStep(elementLists, start, end, partialHeights)) > 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'.
  * <p>
  * 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 "<KnuthNode at " + position + " "
                     + totalWidth + "+" + totalStretch + "-" + totalShrink
                     + " line:" + line + " prev:" + (previous != null ? previous.position : -1)
-                    + " dem:" + totalDemerits + ">";
+                    + " 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
      * <ul>
      *    <li>0 means that the break has the exact right width</li>
      *    <li>&gt;= -1 &amp;&amp; &lt; 0  means that the break is wider than the line,
@@ -871,9 +1004,9 @@
      *        but within the maximum values of the glues.</li>
      *    <li>&gt; 1 means that the break is too small to make up for the glues.</li>
      * </ul>
-     * @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 "<KnuthSequence " + super.toString() + ">";
+    }
+
 }
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks whether keeps within-column are respected.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="page" page-width="400pt" page-height="70pt">
+          <fo:region-body column-count="5" />
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="page" font-size="10pt">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block break-before="page">
+            <!-- simple test: keep the second block together within
+                 one column, breaking the preceding block early 
+                 if necessary -->
+            <fo:block id="block-1">
+              [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]
+            </fo:block>
+            <fo:block id="block-2" keep-together.within-column="always">
+              [BOB-2] foo bar foo bar foo bar foo bar foo [EOB-2]
+            </fo:block>
+          </fo:block>
+          <fo:block break-before="page">
+            <!-- same as the first, but now a nested block
+                 with a higher integer value, and some content
+                 following -->
+            <fo:block id="block-3" keep-together.within-column="5">
+              [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
+              <fo:block font-weight="bold" id="block-3a" keep-together.within-column="always">
+                [BOB-3a] foo bar foo bar foo bar foo bar foo [EOB-3a]
+              </fo:block>
+              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]
+            </fo:block>
+          </fo:block>
+          <fo:block break-before="page">
+            <!-- nested block must be kept together within the same
+                 page, while the outer block may be broken, if necessary -->
+            <fo:block font-style="italic" id="block-4" keep-together.within-column="5">
+              [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
+              <fo:block id="block-4a" keep-together.within-page="always">
+                [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]
+              </fo:block>
+              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]
+            </fo:block>
+          </fo:block>
+          <fo:block break-before="page">
+            <!-- test keep-with-next in conjunction with keep-together
+                 respecting the default value for widows/orphans -->
+            <fo:block id="block-5">
+              <fo:block id="block-5a">
+              [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]
+              </fo:block>
+              <fo:block id="block-5b" keep-with-next.within-column="always">
+              [BOB-5b] foo bar foo bar foo bar foo bar foo bar
+              foo bar foo bar foo bar foo bar foo bar [EOB-5b]
+              </fo:block>
+              <fo:block id="block-5c" keep-together.within-column="always">
+              [BOB-5c] foo bar foo bar foo bar foo bar foo bar
+              foo bar foo bar foo bar foo bar foo bar [EOB-5c]
+              </fo:block>
+            </fo:block>
+          </fo:block>
+          <fo:block break-before="page">
+            <!-- test keep-together in conjunction with keep-with-previous -->
+            <fo:block id="block-6">
+              <fo:block id="block-6a">
+              [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]
+              </fo:block>
+              <fo:block id="block-6b" keep-together.within-column="always">
+              [BOB-6b] foo bar foo bar foo bar foo bar foo bar [EOB-6b]
+              </fo:block>
+              <fo:block id="block-6c" keep-with-previous.within-column="always">
+              [BOB-6c] foo bar foo bar foo bar foo bar foo bar
+              foo bar foo bar foo bar foo bar foo bar [EOB-6c]
+              </fo:block>
+            </fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="10" xpath="count(//page)" />
+  </checks>
+</testcase>
+
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 @@
       <skip>5</skip>
       <!-- penalty between blocks b11 and b12, set by InlineLM in b1 -->
       <penalty w="0" p="0"/>
-      <skip>6</skip>
+      <skip>5</skip>
       <!-- penalty between blocks b21 and b22, set by InlineLM in b2 -->
       <!-- keep-together.within-page="always" -->
       <penalty w="0" p="1000"/>
-      <skip>6</skip>
+      <skip>3</skip>
       <!-- penalty between blocks b31 and b32, set by InlineLM in b3 -->
       <!-- keep-with-next.within-page="always" -->
       <penalty w="0" p="1000"/>
-      <skip>5</skip>
-      <skip>3</skip>
+      <skip>14</skip>
     </element-list>
   </checks>
 </testcase>			          
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 @@
     <element-list category="breaker" index="0">
       <box w="14400"/>
       <penalty w="0" p="0"/>
-      <box w="28800"/>
-      <penalty w="0" p="0"/>
       <box w="14400"/>
-      <skip>3</skip>
+      <penalty w="0" p="INF"/>
+      <box w="14400"/>
+      <skip>5</skip>
     </element-list>
   </checks>
 </testcase>
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 @@
     <element-list category="breaker" index="0">
       <box w="14400"/>
       <penalty w="0" p="0"/>
-      <box w="28800"/>
+      <box w="14400"/>
       <penalty w="0" p="INF"/>
       <box w="14400"/>
+      <penalty w="0" p="INF"/>
+      <box w="14400"/>
       <penalty w="0" p="0"/>
       <box w="14400"/>
       <skip>3</skip>
