]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla#53924: Support for retrieve-table-markers, submitted by Luis Bernardo.
authorMehdi Houshmand <mehdi@apache.org>
Fri, 28 Sep 2012 15:22:49 +0000 (15:22 +0000)
committerMehdi Houshmand <mehdi@apache.org>
Fri, 28 Sep 2012 15:22:49 +0000 (15:22 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1391502 13f79535-47bb-0310-9956-ffa450edef68

41 files changed:
src/java/org/apache/fop/area/PageViewport.java
src/java/org/apache/fop/fo/Constants.java
src/java/org/apache/fop/fo/FONode.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/FOValidationEventProducer.java
src/java/org/apache/fop/fo/FOValidationEventProducer.xml
src/java/org/apache/fop/fo/FObj.java
src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java
src/java/org/apache/fop/fo/flow/Markers.java [new file with mode: 0644]
src/java/org/apache/fop/fo/flow/RetrieveMarker.java
src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java
src/java/org/apache/fop/fo/flow/table/TableCell.java
src/java/org/apache/fop/fo/flow/table/TablePart.java
src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
src/java/org/apache/fop/layoutmgr/LocalBreaker.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/RowPainter.java
src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
status.xml
test/java/org/apache/fop/fo/flow/MarkersTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java
test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java [new file with mode: 0644]
test/layoutengine/standard-testcases/markers_10.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml [new file with mode: 0644]

index f38964ebc48a206fc77fb791badcd34b2cecc0f0..d1250238ab7ca94c1ab5e686ab1f6307481c0627 100644 (file)
@@ -34,6 +34,10 @@ import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.AbstractRetrieveMarker;
+import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.Markers;
 import org.apache.fop.fo.pagination.SimplePageMaster;
 import org.apache.fop.traits.WritingModeTraitsGetter;
 
@@ -81,13 +85,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable {
 
     private Map<String, List<PageViewport>> pendingResolved = null;
 
-    // hashmap of markers for this page
-    // start and end are added by the fo that contains the markers
-    private Map<String, Marker> markerFirstStart = null;
-    private Map<String, Marker> markerLastStart = null;
-    private Map<String, Marker> markerFirstAny = null;
-    private Map<String, Marker> markerLastEnd = null;
-    private Map<String, Marker> markerLastAny = null;
+    private Markers pageMarkers;
 
     /**
      * logging instance
@@ -352,115 +350,23 @@ public class PageViewport extends AreaTreeObject implements Resolvable {
     }
 
     /**
-     * Add the markers for this page.
-     * Only the required markers are kept.
-     * For "first-starting-within-page" it adds the markers
-     * that are starting only if the marker class name is not
-     * already added.
-     * For "first-including-carryover" it adds any starting marker
-     * if the marker class name is not already added.
-     * For "last-starting-within-page" it adds all marks that
-     * are starting, replacing earlier markers.
-     * For "last-ending-within-page" it adds all markers that
-     * are ending, replacing earlier markers.
-     *
-     * Should this logic be placed in the Page layout manager.
+     * Register the markers for this page.
      *
      * @param marks the map of markers to add
      * @param starting if the area being added is starting or ending
      * @param isfirst if the area being added has is-first trait
      * @param islast if the area being added has is-last trait
      */
-    public void addMarkers(Map<String, Marker> marks, boolean starting,
-            boolean isfirst, boolean islast) {
-
-        if (marks == null) {
-            return;
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("--" + marks.keySet() + ": "
-                    + (starting ? "starting" : "ending")
-                    + (isfirst ? ", first" : "")
-                    + (islast ? ", last" : ""));
-        }
-
-        // at the start of the area, register is-first and any areas
-        if (starting) {
-            if (isfirst) {
-                if (markerFirstStart == null) {
-                    markerFirstStart = new HashMap<String, Marker>();
-                }
-                if (markerFirstAny == null) {
-                    markerFirstAny = new HashMap<String, Marker>();
-                }
-                // first on page: only put in new values, leave current
-                for (String key : marks.keySet()) {
-                    if (!markerFirstStart.containsKey(key)) {
-                        markerFirstStart.put(key, marks.get(key));
-                        if (log.isTraceEnabled()) {
-                            log.trace("page " + pageNumberString + ": "
-                                    + "Adding marker " + key + " to FirstStart");
-                        }
-                    }
-                    if (!markerFirstAny.containsKey(key)) {
-                        markerFirstAny.put(key, marks.get(key));
-                        if (log.isTraceEnabled()) {
-                            log.trace("page " + pageNumberString + ": "
-                                    + "Adding marker " + key + " to FirstAny");
-                        }
-                    }
-                }
-                if (markerLastStart == null) {
-                    markerLastStart = new HashMap<String, Marker>();
-                }
-                // last on page: replace all
-                markerLastStart.putAll(marks);
-                if (log.isTraceEnabled()) {
-                    log.trace("page " + pageNumberString + ": "
-                            + "Adding all markers to LastStart");
-                }
-            } else {
-                if (markerFirstAny == null) {
-                    markerFirstAny = new HashMap<String, Marker>();
-                }
-                // first on page: only put in new values, leave current
-                for (String key : marks.keySet()) {
-                    if (!markerFirstAny.containsKey(key)) {
-                        markerFirstAny.put(key, marks.get(key));
-                        if (log.isTraceEnabled()) {
-                            log.trace("page " + pageNumberString + ": "
-                                    + "Adding marker " + key + " to FirstAny");
-                        }
-                    }
-                }
-            }
-        } else {
-            // at the end of the area, register is-last and any areas
-            if (islast) {
-                if (markerLastEnd == null) {
-                    markerLastEnd = new HashMap<String, Marker>();
-                }
-                // last on page: replace all
-                markerLastEnd.putAll(marks);
-                if (log.isTraceEnabled()) {
-                    log.trace("page " + pageNumberString + ": "
-                            + "Adding all markers to LastEnd");
-                }
-            }
-            if (markerLastAny == null) {
-                markerLastAny = new HashMap<String, Marker>();
-            }
-            // last on page: replace all
-            markerLastAny.putAll(marks);
-            if (log.isTraceEnabled()) {
-                log.trace("page " + pageNumberString + ": "
-                        + "Adding all markers to LastAny");
-            }
+    public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) {
+        if (pageMarkers == null) {
+            pageMarkers = new Markers();
         }
+        pageMarkers.register(marks, starting, isfirst, islast);
     }
 
+
     /**
-     * Get a marker from this page.
+     * Resolve a marker from this page.
      * This will retrieve a marker with the class name
      * and position.
      *
@@ -468,64 +374,17 @@ public class PageViewport extends AreaTreeObject implements Resolvable {
      * @param pos the position to retrieve
      * @return Object the marker found or null
      */
-    public Marker getMarker(String name, int pos) {
-        Marker mark = null;
-        String posName = null;
-        switch (pos) {
-            case EN_FSWP:
-                if (markerFirstStart != null) {
-                    mark = markerFirstStart.get(name);
-                    posName = "FSWP";
-                }
-                if (mark == null && markerFirstAny != null) {
-                    mark = markerFirstAny.get(name);
-                    posName = "FirstAny after " + posName;
-                }
-            break;
-            case EN_FIC:
-                if (markerFirstAny != null) {
-                    mark = markerFirstAny.get(name);
-                    posName = "FIC";
-                }
-            break;
-            case EN_LSWP:
-                if (markerLastStart != null) {
-                    mark = markerLastStart.get(name);
-                    posName = "LSWP";
-                }
-                if (mark == null && markerLastAny != null) {
-                    mark = markerLastAny.get(name);
-                    posName = "LastAny after " + posName;
-                }
-            break;
-            case EN_LEWP:
-                if (markerLastEnd != null) {
-                    mark = markerLastEnd.get(name);
-                    posName = "LEWP";
-                }
-                if (mark == null && markerLastAny != null) {
-                    mark = markerLastAny.get(name);
-                    posName = "LastAny after " + posName;
-                }
-            break;
-            default:
-                assert false;
-        }
-        if (log.isTraceEnabled()) {
-            log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name
-                    + " at position " + posName);
+    public Marker resolveMarker(AbstractRetrieveMarker rm) {
+        if (pageMarkers == null) {
+            return null;
         }
-        return mark;
+        return pageMarkers.resolve(rm);
     }
 
     /** Dumps the current marker data to the logger. */
     public void dumpMarkers() {
-        if (log.isTraceEnabled()) {
-            log.trace("FirstAny: " + this.markerFirstAny);
-            log.trace("FirstStart: " + this.markerFirstStart);
-            log.trace("LastAny: " + this.markerLastAny);
-            log.trace("LastEnd: " + this.markerLastEnd);
-            log.trace("LastStart: " + this.markerLastStart);
+        if (pageMarkers != null) {
+            pageMarkers.dump();
         }
     }
 
index 6688042e263b04d1dc108695f463dc7fc3238025..1f098fe0553f530aab589d2bbae0e115d9fdc6bb 100644 (file)
@@ -1227,6 +1227,8 @@ public interface Constants {
     int EN_BT = 204; // bottom to top
     /** Enumeration constant */
     int EN_TB_LR = 205; // for top-to-bottom, left-to-right writing mode
+    /** Enumeration constant -- for fo:retrieve-table-marker */
+    int EN_FIRST_INCLUDING_CARRYOVER = 206;
     /** Number of enumeration constants defined */
-    int ENUM_COUNT = 205;
+    int ENUM_COUNT = 206;
 }
index 8b55e6d301ab01799b6e5c2b2f808e30640cb8ff..707dae91edf2e2333889667a8842a410fdcff409 100644 (file)
@@ -562,6 +562,19 @@ public abstract class FONode implements Cloneable {
         getFOValidationEventProducer().invalidChild(this, parentName, qn, ruleViolated, loc);
     }
 
+    /**
+     * Helper function to return "not supported child" exceptions. Note that the child is valid, just not
+     * supported yet by FOP.
+     * 
+     * @param loc org.xml.sax.Locator object of the error (*not* parent node)
+     * @param nsURI namespace URI of incoming invalid node
+     * @param lName local name (i.e., no prefix) of incoming node
+     * @throws ValidationException the validation error provoked by the method call
+     */
+    protected void notSupportedChildError(Locator loc, String nsURI, String lName) throws ValidationException {
+        getFOValidationEventProducer().notSupportedChild(this, getName(), new QName(nsURI, lName), loc);
+    }
+
     /**
      * Helper function to throw an error caused by missing mandatory child elements.
      * (e.g., <code>fo:layout-master-set</code> not having any <code>fo:page-master</code>
index a92b71ef982e07b2abd424a79c57267662548602..6f77efc93d2a9429cd62373458fea134c5978a1d 100644 (file)
@@ -2178,7 +2178,8 @@ public final class FOPropertyMapping implements Constants {
         m  = new EnumProperty.Maker(PR_RETRIEVE_POSITION_WITHIN_TABLE);
         m.setInherited(false);
         m.addEnum("first-starting", getEnumProperty(EN_FIRST_STARTING, "FIRST_STARTING"));
-        m.addEnum("first-including-carryover", getEnumProperty(EN_FIC, "FIC"));
+        m.addEnum("first-including-carryover",
+                getEnumProperty(EN_FIRST_INCLUDING_CARRYOVER, "FIRST_INCLUDING_CARRYOVER"));
         m.addEnum("last-starting", getEnumProperty(EN_LAST_STARTING, "LAST_STARTING"));
         m.addEnum("last-ending", getEnumProperty(EN_LAST_ENDING, "LAST_ENDING"));
         m.setDefault("first-starting");
@@ -2189,7 +2190,7 @@ public final class FOPropertyMapping implements Constants {
         m.setInherited(false);
         m.addEnum("table", getEnumProperty(EN_TABLE, "TABLE"));
         m.addEnum("table-fragment", getEnumProperty(EN_TABLE_FRAGMENT, "TABLE_FRAGMENT"));
-        m.addEnum("page", getEnumProperty(EN_DOCUMENT, "PAGE"));
+        m.addEnum("page", getEnumProperty(EN_PAGE, "PAGE"));
         m.setDefault("table");
         addPropertyMaker("retrieve-boundary-within-table", m);
     }
index ff005b1b4a7953c6ff1e8c4d0417ae49ebdd775a..ee6b078b52a27092425cd40327d8e8d57f33fb38 100644 (file)
@@ -89,6 +89,18 @@ public interface FOValidationEventProducer extends EventProducer {
     void invalidChild(Object source, String elementName, QName offendingNode, String ruleViolated,
             Locator loc) throws ValidationException;
 
+    /**
+     * A valid but not yet supported child was encountered.
+     * 
+     * @param source the event source
+     * @param elementName the name of the context node
+     * @param offendingNode the offending node
+     * @param loc the location of the error or null
+     * @throws ValidationException the validation error provoked by the method call
+     */
+    void notSupportedChild(Object source, String elementName, QName offendingNode, Locator loc)
+            throws ValidationException;
+
     /**
      * A required child element is missing.
      * @param source the event source
index 509f7c1d3d46250707e2bf89c509f8b30baf90fa..a8b2fffb1fa2fc4e84b69edd82da614f02c30cf8 100644 (file)
@@ -17,6 +17,7 @@
   <message key="tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message>
   <message key="nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message>
   <message key="invalidChild">"{offendingNode}" is not a valid child of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message>
+  <message key="notSupportedChild">"{offendingNode}" as a child of "{elementName}" is not supported yet!{{locator}}</message>
   <message key="missingChildElement">"{elementName}" is missing child elements.[ Required content model: {contentModel}]{{locator}}</message>
   <message key="missingProperty">Element "{elementName}" is missing required property "{propertyName}"!{{locator}}</message>
   <message key="idNotUnique">Property ID "{id}" (found on "{elementName}") previously used; ID values must be unique within a document!{severity,equals,EventSeverity:FATAL,, Any reference to it will be considered a reference to the first occurrence in the document.}{{locator}}</message>
index 241a442ab2f59b45c5ae51a0c03835ed1458416d..0e7e55aa9d1dfb14b9415da3eb4903ec68abb4b1 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.fo;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
@@ -35,6 +36,7 @@ import org.apache.xmlgraphics.util.QName;
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.table.TableCell;
 import org.apache.fop.fo.properties.PropertyMaker;
 
 /**
@@ -65,7 +67,7 @@ public abstract class FObj extends FONode implements Constants {
     private boolean isOutOfLineFODescendant = false;
 
     /** Markers added to this element. */
-    private Map markers = null;
+    private Map<String, Marker> markers;
 
     private int bidiLevel = -1;
 
@@ -356,7 +358,7 @@ public abstract class FObj extends FONode implements Constants {
             }
         }
         if (markers == null) {
-            markers = new java.util.HashMap();
+            markers = new HashMap<String, Marker>();
         }
         if (!markers.containsKey(mcname)) {
             markers.put(mcname, marker);
@@ -376,7 +378,7 @@ public abstract class FObj extends FONode implements Constants {
     /**
      * @return the collection of Markers attached to this object
      */
-    public Map getMarkers() {
+    public Map<String, Marker> getMarkers() {
         return markers;
     }
 
@@ -522,6 +524,11 @@ public abstract class FObj extends FONode implements Constants {
         int found = 1;
         FONode temp = getParent();
         while (temp != null) {
+            if (temp instanceof TableCell && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) {
+                // note that if the retrieve-table-marker is not in a table-header/footer an exception is
+                // thrown, so no need to reset this flag in that case
+                ((TableCell) temp).flagAsHavingRetrieveTableMarker();
+            }
             if (temp.getNameId() == ancestorID) {
                 return found;
             }
index 62c8215040637fda6a887ee205ac567250c7439c..636bc04df7d0c48b9048f04667ec54c95a5f09b2 100644 (file)
@@ -46,6 +46,11 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {
 
     private String retrieveClassName;
 
+    private int position;
+    private String positionLabel;
+    private int boundary;
+    private String boundaryLabel;
+
     /**
      * Create a new AbstractRetrieveMarker instance that
      * is a child of the given {@link FONode}
@@ -206,4 +211,43 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {
         return this.retrieveClassName;
     }
 
+    protected void setBoundaryLabel(String label) {
+        this.boundaryLabel = label;
+    }
+
+    protected void setPositionLabel(String label) {
+        this.positionLabel = label;
+    }
+
+    public String getBoundaryLabel() {
+        return this.boundaryLabel;
+    }
+
+    public String getPositionLabel() {
+        return this.positionLabel;
+    }
+
+    protected void setPosition(int position) {
+        this.position = position;
+    }
+
+    protected void setBoundary(int boundary) {
+        this.boundary = boundary;
+    }
+
+    public int getPosition() {
+        return this.position;
+    }
+
+    public int getBoundary() {
+        return this.boundary;
+    }
+
+    public abstract String getLocalName();
+
+    public abstract int getNameId();
+
+    public void changePositionTo(int position) {
+        this.position = position;
+    }
 }
diff --git a/src/java/org/apache/fop/fo/flow/Markers.java b/src/java/org/apache/fop/fo/flow/Markers.java
new file mode 100644 (file)
index 0000000..24da818
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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.fo.flow;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.Constants;
+
+/**
+ * A class to register and resolve markers.
+ */
+public final class Markers {
+
+    // IsAny means either IsFirst or IsLast
+    private Map<String, Marker> firstQualifyingIsFirst;
+    private Map<String, Marker> firstQualifyingIsAny;
+    private Map<String, Marker> lastQualifyingIsFirst;
+    private Map<String, Marker> lastQualifyingIsLast;
+    private Map<String, Marker> lastQualifyingIsAny;
+
+    private static Log log = LogFactory.getLog(Markers.class);
+
+    /**
+     * Registers a marker with the position traits set.
+     * Only the required markers are kept.
+     * For "first-starting-within-page" it adds the markers
+     * that are starting only if the marker class name is not
+     * already added.
+     * For "first-including-carryover" it adds any starting marker
+     * if the marker class name is not already added.
+     * For "last-starting-within-page" it adds all marks that
+     * are starting, replacing earlier markers.
+     * For "last-ending-within-page" it adds all markers that
+     * are ending, replacing earlier markers.
+     *
+     * @param marks a map of markers to register
+     * @param starting whether the registration happens at the start (true) or end (false) the the area 
+     * @param isfirst whether it is the first area of the parent LM
+     * @param islast whether it is the last area of the parent LM
+     */
+    public void register(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) {
+        // TODO: find way to put the page number in the log tracing
+
+        if (marks == null) {
+            return;
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("--" + marks.keySet() + ": " + (starting ? "starting" : "ending")
+                    + (isfirst ? ", first" : "") + (islast ? ", last" : ""));
+        }
+
+        if (starting) {
+            // at the start of the area, register is-first and any areas
+            if (firstQualifyingIsAny == null) {
+                firstQualifyingIsAny = new HashMap<String, Marker>();
+            }
+            if (isfirst) {
+                if (firstQualifyingIsFirst == null) {
+                    firstQualifyingIsFirst = new HashMap<String, Marker>();
+                }
+                // first on scope: only put in new values, leave current
+                for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) {
+                    String key = iter.next();
+                    if (!firstQualifyingIsFirst.containsKey(key)) {
+                        firstQualifyingIsFirst.put(key, marks.get(key));
+                        if (log.isTraceEnabled()) {
+                            log.trace("Adding marker " + key + " to firstQualifyingIsFirst");
+                        }
+                    }
+                    if (!firstQualifyingIsAny.containsKey(key)) {
+                        firstQualifyingIsAny.put(key, marks.get(key));
+                        if (log.isTraceEnabled()) {
+                            log.trace("Adding marker " + key + " to firstQualifyingIsAny");
+                        }
+                    }
+                }
+                if (lastQualifyingIsFirst == null) {
+                    lastQualifyingIsFirst = new HashMap<String, Marker>();
+                }
+                // last on scope: replace all
+                lastQualifyingIsFirst.putAll(marks);
+                if (log.isTraceEnabled()) {
+                    log.trace("Adding all markers to LastStart");
+                }
+            } else {
+                // first on scope: only put in new values, leave current
+                for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) {
+                    String key = iter.next();
+                    if (!firstQualifyingIsAny.containsKey(key)) {
+                        firstQualifyingIsAny.put(key, marks.get(key));
+                        if (log.isTraceEnabled()) {
+                            log.trace("Adding marker " + key + " to firstQualifyingIsAny");
+                        }
+                    }
+                }
+            }
+        } else {
+            // at the end of the area, register is-last and any areas
+            if (islast) {
+                if (lastQualifyingIsLast == null) {
+                    lastQualifyingIsLast = new HashMap<String, Marker>();
+                }
+                // last on page: replace all
+                lastQualifyingIsLast.putAll(marks);
+                if (log.isTraceEnabled()) {
+                    log.trace("Adding all markers to lastQualifyingIsLast");
+                }
+            }
+            if (lastQualifyingIsAny == null) {
+                lastQualifyingIsAny = new HashMap<String, Marker>();
+            }
+            // last on page: replace all
+            lastQualifyingIsAny.putAll(marks);
+            if (log.isTraceEnabled()) {
+                log.trace("Adding all markers to lastQualifyingIsAny");
+            }
+        }
+    }
+
+    /**
+     * Retrieves the best candidate marker for the given position.
+     * @param name the key used to register the marker
+     * @param pos the retrieval scope position
+     * @return a Marker instance
+     */
+    public Marker resolve(AbstractRetrieveMarker arm) {
+        Marker mark = null;
+        int pos = arm.getPosition();
+        String name = arm.getRetrieveClassName();
+        String posName = arm.getPositionLabel();
+        String localName = arm.getLocalName();
+        switch (pos) {
+        case Constants.EN_FSWP: // retrieve-marker
+        case Constants.EN_FIRST_STARTING: // retrieve-table-marker
+            if (firstQualifyingIsFirst != null) {
+                mark = firstQualifyingIsFirst.get(name);
+            }
+            if (mark == null && firstQualifyingIsAny != null) {
+                mark = firstQualifyingIsAny.get(name);
+                posName = "FirstAny after " + posName;
+            }
+            break;
+        case Constants.EN_FIC: // retrieve-marker
+        case Constants.EN_FIRST_INCLUDING_CARRYOVER: // retrieve-table-marker
+            if (firstQualifyingIsAny != null) {
+                mark = firstQualifyingIsAny.get(name);
+            }
+            break;
+        case Constants.EN_LSWP: // retrieve-marker
+        case Constants.EN_LAST_STARTING: // retrieve-table-marker
+            if (lastQualifyingIsFirst != null) {
+                mark = lastQualifyingIsFirst.get(name);
+            }
+            if (mark == null && lastQualifyingIsAny != null) {
+                mark = lastQualifyingIsAny.get(name);
+                posName = "LastAny after " + posName;
+            }
+            break;
+        case Constants.EN_LEWP: // retrieve-marker
+        case Constants.EN_LAST_ENDING: // retrieve-table-marker
+            if (lastQualifyingIsLast != null) {
+                mark = lastQualifyingIsLast.get(name);
+            }
+            if (mark == null && lastQualifyingIsAny != null) {
+                mark = lastQualifyingIsAny.get(name);
+                posName = "LastAny after " + posName;
+            }
+            break;
+        default:
+            throw new RuntimeException("Invalid position attribute in " + localName + ".");
+        }
+        if (log.isTraceEnabled()) {
+            // TODO: find way to put the page number here
+            log.trace(localName + ": name[" + name + "]; position [" + posName + "]");
+        }
+        return mark;
+    }
+
+    /** Dumps the current marker data to the logger. */
+    public void dump() {
+        if (log.isTraceEnabled()) {
+            log.trace("FirstAny: " + this.firstQualifyingIsAny);
+            log.trace("FirstStart: " + this.firstQualifyingIsFirst);
+            log.trace("LastAny: " + this.lastQualifyingIsAny);
+            log.trace("LastEnd: " + this.lastQualifyingIsLast);
+            log.trace("LastStart: " + this.lastQualifyingIsFirst);
+        }
+    }
+
+}
index 5fc70c7f2e30c264a4fa294bd4668735ea0139ff..b001a41ee43dcbefe48842f620499a32f82c60bc 100644 (file)
@@ -34,11 +34,6 @@ import org.apache.fop.fo.PropertyList;
  */
 public class RetrieveMarker extends AbstractRetrieveMarker {
 
-    // The value of properties relevant for fo:retrieve-marker.
-    private int retrievePosition;
-    private int retrieveBoundary;
-    // End of property values
-
     /**
      * Create a new RetrieveMarker instance that is a
      * child of the given {@link FONode}.
@@ -70,8 +65,10 @@ public class RetrieveMarker extends AbstractRetrieveMarker {
     /** {@inheritDoc} */
     public void bind(PropertyList pList) throws FOPException {
         super.bind(pList);
-        this.retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum();
-        this.retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum();
+        setPosition(pList.get(PR_RETRIEVE_POSITION).getEnum());
+        setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION).getObject());
+        setBoundary(pList.get(PR_RETRIEVE_BOUNDARY).getEnum());
+        setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY).getObject());
     }
 
     /**
@@ -84,19 +81,19 @@ public class RetrieveMarker extends AbstractRetrieveMarker {
      *              {@link org.apache.fop.fo.Constants#EN_LEWP}.
      */
     public int getRetrievePosition() {
-        return this.retrievePosition;
+        return getPosition();
     }
 
     /**
      * Return the value for the <code>retrieve-boundary</code>
      * property
-     * @return  the value for retrieve-boundary-within-table; one of
+     * @return  the value for retrieve-boundary; one of
      *              {@link org.apache.fop.fo.Constants#EN_PAGE},
      *              {@link org.apache.fop.fo.Constants#EN_PAGE_SEQUENCE},
      *              {@link org.apache.fop.fo.Constants#EN_DOCUMENT}.
      */
     public int getRetrieveBoundary() {
-        return this.retrieveBoundary;
+        return getBoundary();
     }
 
     /** {@inheritDoc} */
index 3090cb702bfd1d99129cb881cb9468ffc3c750a4..efacba8640e84cbcf4aef72c6722a3d78e9e4ef7 100644 (file)
@@ -32,11 +32,6 @@ import org.apache.fop.fo.PropertyList;
  */
 public class RetrieveTableMarker extends AbstractRetrieveMarker {
 
-    // The value of properties relevant for fo:retrieve-table-marker.
-    private int retrievePositionWithinTable;
-    private int retrieveBoundaryWithinTable;
-    // end property values
-
     /**
      * Create a new RetrieveTableMarker instance that is
      * a child of the given {@link FONode}.
@@ -67,10 +62,10 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
     /** {@inheritDoc} */
     public void bind(PropertyList pList) throws FOPException {
         super.bind(pList);
-        this.retrievePositionWithinTable
-                = pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum();
-        this.retrieveBoundaryWithinTable
-                = pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum();
+        setPosition(pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum());
+        setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getObject());
+        setBoundary(pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum());
+        setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getObject());
     }
 
     /**
@@ -83,7 +78,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
      *              {@link org.apache.fop.fo.Constants#EN_LAST_ENDING}.
      */
     public int getRetrievePositionWithinTable() {
-        return this.retrievePositionWithinTable;
+        return getPosition();
     }
 
     /**
@@ -95,7 +90,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
      *              {@link org.apache.fop.fo.Constants#EN_PAGE}.
      */
     public int getRetrieveBoundaryWithinTable() {
-        return this.retrieveBoundaryWithinTable;
+        return getBoundary();
     }
 
     /** {@inheritDoc} */
@@ -110,4 +105,12 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
     public int getNameId() {
         return FO_RETRIEVE_TABLE_MARKER;
     }
+
+    /** {@inheritDoc} */
+    public void clearChildNodes() {
+        super.clearChildNodes();
+        this.currentTextNode = null;
+        this.lastFOTextProcessed = null;
+    }
+
 }
index c4f9c2aa62fbcfeb7d68acd4efbc1a1156f9064a..f198f3aadf52b5c303c368ef9085b8bace4b1166 100644 (file)
@@ -62,6 +62,8 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder {
     /** used for FO validation */
     private boolean blockItemFound = false;
 
+    private boolean hasRetrieveTableMarker;
+
     /**
      * Create a TableCell instance with the given {@link FONode}
      * as parent.
@@ -247,4 +249,11 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder {
         return FO_TABLE_CELL;
     }
 
+    public void flagAsHavingRetrieveTableMarker() {
+        hasRetrieveTableMarker = true;
+    }
+
+    public boolean hasRetrieveTableMarker() {
+        return hasRetrieveTableMarker;
+    }
 }
index 3ab92cc9477039f2e741ea587b31e2e39f2f069b..032340681d6aaef3da7969413e157973fa586d19 100644 (file)
@@ -169,6 +169,8 @@ public abstract class TablePart extends TableCellContainer {
                             getUserAgent().getEventBroadcaster());
                     eventProducer.noMixRowsAndCells(this, getName(), getLocator());
                 }
+            } else if (localName.equals("retrieve-table-marker")) {
+                notSupportedChildError(loc, nsURI, localName);
             } else {
                 invalidChildError(loc, nsURI, localName);
             }
index 8c213d7d5d832c29d5ba236c2d7dde9fd52f8883..5d7cc0b64b4c30e98a89360c172b7f4ba5bc2463 100644 (file)
@@ -273,4 +273,11 @@ public abstract class AbstractBaseLayoutManager
         throw new UnsupportedOperationException("Not implemented");
     }
 
+    public void preserveChildrenAtEndOfLayout() {
+
+    }
+
+    public void recreateChildrenLMs() {
+
+    }
 }
index 0089f228fd4470505b400c6b5bc0e21336364470..3d64c436c55bfa0a800e77a6d20dba886224e2b9 100644 (file)
@@ -37,6 +37,7 @@ import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.flow.RetrieveMarker;
+import org.apache.fop.layoutmgr.table.TableLayoutManager;
 
 /**
  * The base class for most LayoutManagers.
@@ -67,6 +68,8 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
     private int lastGeneratedPosition = -1;
     private int smallestPosNumberChecked = Integer.MAX_VALUE;
 
+    private boolean preserveChildrenAtEndOfLayout;
+
     /**
      * Abstract layout manager.
      */
@@ -370,19 +373,20 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
     }
 
     /**
-     * Registers the FO's markers on the current PageViewport
+     * Registers the FO's markers on the current PageViewport, and if applicable on the parent TableLM.
      *
      * @param isStarting    boolean indicating whether the markers qualify as 'starting'
      * @param isFirst   boolean indicating whether the markers qualify as 'first'
      * @param isLast    boolean indicating whether the markers qualify as 'last'
      */
-    protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) {
+    protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) {
         if (this.markers != null) {
-            getCurrentPV().addMarkers(
+            getCurrentPV().registerMarkers(
                     this.markers,
                     isStarting,
                     isFirst,
                     isLast);
+            possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
         }
     }
 
@@ -419,11 +423,12 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
 
             notifyEndOfLayout();
 
-            /* References to the child LMs are no longer needed
-             */
-            childLMs = null;
-            curChildLM = null;
-            childLMiter = null;
+            if (!preserveChildrenAtEndOfLayout) {
+                // References to the child LMs are no longer needed
+                childLMs = null;
+                curChildLM = null;
+                childLMiter = null;
+            }
 
             /* markers that qualify have been transferred to the page
              */
@@ -438,13 +443,21 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
                         || lm instanceof PageSequenceLayoutManager)) {
                 lm = lm.getParent();
             }
-            if (lm instanceof FlowLayoutManager) {
+            if (lm instanceof FlowLayoutManager && !preserveChildrenAtEndOfLayout) {
                 fobj.clearChildNodes();
                 fobjIter = null;
             }
         }
     }
 
+    /*
+     * Preserves the children LMs at the end of layout. This is necessary if the layout is expected to be
+     * repeated, as when using retrieve-table-markers.
+     */
+    public void preserveChildrenAtEndOfLayout() {
+        preserveChildrenAtEndOfLayout = true;
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
@@ -467,4 +480,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
         lastGeneratedPosition = -1;
     }
 
+    public void recreateChildrenLMs() {
+        childLMs = new ArrayList();
+        isFinished = false;
+        if (fobj == null) {
+            return;
+        }
+        fobjIter = fobj.getChildNodes();
+        int position = 0;
+        while (createNextChildLMs(position++)) {
+            //
+        }
+        childLMiter = new LMiter(this);
+        for (LMiter iter = new LMiter(this); iter.hasNext();) {
+            AbstractBaseLayoutManager alm = (AbstractBaseLayoutManager) iter.next();
+            alm.initialize();
+            alm.recreateChildrenLMs();
+            alm.preserveChildrenAtEndOfLayout();
+        }
+        curChildLM = getChildLM();
+    }
+
+    protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting,
+            boolean isFirst, boolean isLast) {
+        LayoutManager lm = this.parentLayoutManager;
+        if (lm instanceof FlowLayoutManager || lm instanceof PageSequenceLayoutManager
+                || !(lm instanceof AbstractLayoutManager)) {
+            return;
+        }
+        ((AbstractLayoutManager) lm).possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
+    }
 }
index f36cde1585376df22fcad3e04698293eccecf6ed..edfb389ed02f4a18fa32d3b83c4474254fce5d1f 100644 (file)
@@ -226,15 +226,14 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
     public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
         AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
         String name = rm.getRetrieveClassName();
-        int pos = rm.getRetrievePosition();
         int boundary = rm.getRetrieveBoundary();
 
         // get marker from the current markers on area tree
-        Marker mark = (Marker)getCurrentPV().getMarker(name, pos);
+        Marker mark = getCurrentPV().resolveMarker(rm);
         if (mark == null && boundary != EN_PAGE) {
             // go back over pages until mark found
             // if document boundary then keep going
-            boolean doc = boundary == EN_DOCUMENT;
+            boolean doc = (boundary == EN_DOCUMENT);
             int seq = areaTreeModel.getPageSequenceCount();
             int page = areaTreeModel.getPageCount(seq) - 1;
             while (page < 0 && doc && seq > 1) {
@@ -243,7 +242,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
             }
             while (page >= 0) {
                 PageViewport pv = areaTreeModel.getPage(seq, page);
-                mark = (Marker)pv.getMarker(name, Constants.EN_LEWP);
+                int originalPosition = rm.getPosition();
+                rm.changePositionTo(Constants.EN_LEWP);
+                mark = (Marker) pv.resolveMarker(rm);
+                // this is probably not necessary since the RM will not be used again, but to be safe...
+                rm.changePositionTo(originalPosition);
                 if (mark != null) {
                     break;
                 }
index c80982cce2eb3e4c131f9965f44bd2b99a2f5f7a..0ed6cb69b4c0273d6bf28c1a9e577a61c4c65abc 100644 (file)
@@ -87,7 +87,7 @@ public final class AreaAdditionUtil {
         }
 
         if (bslm != null) {
-            bslm.addMarkersToPage(
+            bslm.registerMarkers(
                     true,
                     bslm.isFirst(firstPos),
                     bslm.isLast(lastPos));
@@ -114,11 +114,10 @@ public final class AreaAdditionUtil {
         }
 
         if (bslm != null) {
-            bslm.addMarkersToPage(
+            bslm.registerMarkers(
                     false,
                     bslm.isFirst(firstPos),
                     bslm.isLast(lastPos));
-            bslm.checkEndOfLayout(lastPos);
         }
 
 
index 920589657781d0f59a8a5ed37e2411cd718f2e10..d20215151b5732595acbb00f17a4b50b1cc60dd1 100644 (file)
@@ -806,7 +806,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
 
         addId();
 
-        addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(true, isFirst(firstPos), isLast(lastPos));
 
         if (bcpos == null) {
             // the Positions in positionList were inside the elements
@@ -826,7 +826,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
             bcpos.getBreaker().addContainedAreas(layoutContext);
         }
 
-        addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(false, isFirst(firstPos), isLast(lastPos));
 
         TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
                 effSpaceBefore, effSpaceAfter);
index a99b45620bf2d502e9e60d97f88edd23e42e2f3d..f62d7f946550cb33d783b5b92b87c8dbbfffbf94 100644 (file)
@@ -312,7 +312,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
 
         addId();
 
-        addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(true, isFirst(firstPos), isLast(lastPos));
 
         // the Positions in positionList were inside the elements
         // created by the LineLM
@@ -327,7 +327,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
             childLM.addAreas(childPosIter, lc);
         }
 
-        addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(false, isFirst(firstPos), isLast(lastPos));
 
         TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
                 effSpaceBefore, effSpaceAfter);
index 76a1cb9e45837f8857c9156fbca795ee32406e43..5f4aa5725849d4292080bd876e0d292eaff212cc 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.area.AreaTreeHandler;
 import org.apache.fop.fo.FOElementMapping;
 import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FONode.FONodeIterator;
 import org.apache.fop.fo.FOText;
 import org.apache.fop.fo.FObjMixed;
 import org.apache.fop.fo.extensions.ExternalDocument;
@@ -117,7 +118,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
         registerMaker(Block.class, new BlockLayoutManagerMaker());
         registerMaker(Leader.class, new LeaderLayoutManagerMaker());
         registerMaker(RetrieveMarker.class, new RetrieveMarkerLayoutManagerMaker());
-        registerMaker(RetrieveTableMarker.class, new Maker());
+        registerMaker(RetrieveTableMarker.class, new RetrieveTableMarkerLayoutManagerMaker());
         registerMaker(Character.class, new CharacterLayoutManagerMaker());
         registerMaker(ExternalGraphic.class,
                    new ExternalGraphicLayoutManagerMaker());
@@ -407,6 +408,24 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
         }
     }
 
+    public class RetrieveTableMarkerLayoutManagerMaker extends Maker {
+        public void make(FONode node, List lms) {
+            FONodeIterator baseIter = node.getChildNodes();
+            if (baseIter == null) {
+                // this happens when the retrieve-table-marker cannot be resolved yet
+                RetrieveTableMarker rtm = (RetrieveTableMarker) node;
+                RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm);
+                lms.add(rtmlm);
+                return;
+            }
+            while (baseIter.hasNext()) {
+                // this happens when the retrieve-table-marker has been resolved
+                FONode child = (FONode) baseIter.next();
+                makeLayoutManagers(child, lms);
+            }
+        }
+    }
+
     /** a layout manager maker */
     public class WrapperLayoutManagerMaker extends Maker {
         /** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/layoutmgr/LocalBreaker.java b/src/java/org/apache/fop/layoutmgr/LocalBreaker.java
new file mode 100644 (file)
index 0000000..bb08446
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.fop.fo.FObj;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
+import org.apache.fop.layoutmgr.inline.TextLayoutManager;
+
+public abstract class LocalBreaker extends AbstractBreaker {
+    protected BlockStackingLayoutManager lm;
+    private int displayAlign;
+    private int ipd;
+    private int overflow = 0;
+
+    public LocalBreaker(BlockStackingLayoutManager lm, int ipd, int displayAlign) {
+        this.lm = lm;
+        this.ipd = ipd;
+        this.displayAlign = displayAlign;
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isPartOverflowRecoveryActivated() {
+        // For side regions, this must be disabled because of wanted overflow.
+        return false;
+    }
+
+    public boolean isOverflow() {
+        return (this.overflow != 0);
+    }
+
+    public int getOverflowAmount() {
+        return this.overflow;
+    }
+
+    /** {@inheritDoc} */
+    protected PageBreakingLayoutListener createLayoutListener() {
+        return new PageBreakingLayoutListener() {
+
+            public void notifyOverflow(int part, int amount, FObj obj) {
+                if (LocalBreaker.this.overflow == 0) {
+                    LocalBreaker.this.overflow = amount;
+                }
+            }
+
+        };
+    }
+
+    protected LayoutManager getTopLevelLM() {
+        return lm;
+    }
+
+    protected LayoutContext createLayoutContext() {
+        LayoutContext lc = super.createLayoutContext();
+        lc.setRefIPD(ipd);
+        return lc;
+    }
+
+    protected List getNextKnuthElements(LayoutContext context, int alignment) {
+        LayoutManager curLM; // currently active LM
+        List returnList = new LinkedList();
+
+        while ((curLM = lm.getChildLM()) != null) {
+            LayoutContext childLC = LayoutContext.newInstance();
+            childLC.setStackLimitBP(context.getStackLimitBP());
+            childLC.setRefIPD(context.getRefIPD());
+            childLC.setWritingMode(context.getWritingMode());
+
+            List returnedList = null;
+            // The following is a HACK! Ignore leading and trailing white space
+            boolean ignore = curLM instanceof TextLayoutManager;
+            if (!curLM.isFinished()) {
+                returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            }
+            if (returnedList != null && !ignore) {
+                lm.wrapPositionElements(returnedList, returnList);
+            }
+        }
+        SpaceResolver.resolveElementList(returnList);
+        lm.setFinished(true);
+        return returnList;
+    }
+
+    protected int getCurrentDisplayAlign() {
+        return displayAlign;
+    }
+
+    protected boolean hasMoreContent() {
+        return !lm.isFinished();
+    }
+
+    protected void addAreas(PositionIterator posIter, LayoutContext context) {
+        AreaAdditionUtil.addAreas(lm, posIter, context);
+    }
+
+    protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList,
+            BlockSequence effectiveList) {
+        if (partCount > 1) {
+            PageBreakPosition pos = (PageBreakPosition) alg.getPageBreaks().getFirst();
+            int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
+                    effectiveList.ignoreAtStart, pos.getLeafPos());
+            overflow += alg.totalWidth - firstPartLength;
+        }
+        // Rendering all parts (not just the first) at once for the case where the parts that
+        // overflow should be visible.
+        alg.removeAllPageBreaks();
+        // Directly add areas after finding the breaks
+        this.addAreas(alg, 1, originalList, effectiveList);
+    }
+
+    protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
+        // nop for static content
+    }
+
+    protected LayoutManager getCurrentChildLM() {
+        return null; // TODO NYI
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java
new file mode 100644 (file)
index 0000000..9d0979a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
+import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
+import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager;
+import org.apache.fop.layoutmgr.table.TableLayoutManager;
+
+public class RetrieveTableMarkerLayoutManager extends LeafNodeLayoutManager {
+
+    private static Log log = LogFactory.getLog(RetrieveTableMarkerLayoutManager.class);
+
+    public RetrieveTableMarkerLayoutManager(RetrieveTableMarker node) {
+        super(node);
+    }
+
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment) {
+        setFinished(true);
+        FONode foNode = (FONode) getFObj();
+        foNode = getTableLayoutManager().resolveRetrieveTableMarker((RetrieveTableMarker) foNode);
+        if (foNode != null) {
+            // resolve the RTM and replace current LM by the resolved target LM
+            InlineLevelLayoutManager illm = (InlineLevelLayoutManager) getPSLM().getLayoutManagerMaker()
+                    .makeLayoutManager(foNode);
+            if (illm instanceof RetrieveTableMarkerLayoutManager) {
+                // happens if the retrieve-marker was empty
+                return null;
+            }
+            illm.setParent(getParent());
+            illm.initialize();
+            return illm.getNextKnuthElements(context, alignment);
+        } else {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void addAreas(PositionIterator posIter, LayoutContext context) {
+    }
+
+    private TableLayoutManager getTableLayoutManager() {
+        LayoutManager parentLM = getParent();
+        while (!(parentLM instanceof TableLayoutManager)) {
+            parentLM = parentLM.getParent();
+        }
+        TableLayoutManager tlm = (TableLayoutManager) parentLM;
+        return tlm;
+    }
+
+}
index 403daceb352a5b7683e13660a76671fff463638c..058b022706525178ea22fd934260804947978a73 100644 (file)
@@ -167,125 +167,22 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
         return (StaticContent) fobj;
     }
 
-    private class StaticContentBreaker extends AbstractBreaker {
-        private StaticContentLayoutManager lm;
-        private int displayAlign;
-        private int ipd;
-        private int overflow = 0;
-
-        public StaticContentBreaker(StaticContentLayoutManager lm, int ipd,
-                int displayAlign) {
-            this.lm = lm;
-            this.ipd = ipd;
-            this.displayAlign = displayAlign;
+    private class StaticContentBreaker extends LocalBreaker {
+
+        public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, int displayAlign) {
+            super(lm, ipd, displayAlign);
         }
 
         /** {@inheritDoc} */
         protected void observeElementList(List elementList) {
             String elementListID = getStaticContentFO().getFlowName();
-            String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId();
+            String pageSequenceID = ((PageSequence) lm.getParent().getFObj()).getId();
             if (pageSequenceID != null && pageSequenceID.length() > 0) {
                 elementListID += "-" + pageSequenceID;
             }
             ElementListObserver.observe(elementList, "static-content", elementListID);
         }
 
-        /** {@inheritDoc} */
-        protected boolean isPartOverflowRecoveryActivated() {
-            //For side regions, this must be disabled because of wanted overflow.
-            return false;
-        }
-
-        public boolean isOverflow() {
-            return (this.overflow != 0);
-        }
-
-        public int getOverflowAmount() {
-            return this.overflow;
-        }
-
-        /** {@inheritDoc} */
-        protected PageBreakingLayoutListener createLayoutListener() {
-            return new PageBreakingLayoutListener() {
-
-                public void notifyOverflow(int part, int amount, FObj obj) {
-                    if (StaticContentBreaker.this.overflow == 0) {
-                        StaticContentBreaker.this.overflow = amount;
-                    }
-                }
-
-            };
-        }
-
-        protected LayoutManager getTopLevelLM() {
-            return lm;
-        }
-
-        protected LayoutContext createLayoutContext() {
-            LayoutContext lc = super.createLayoutContext();
-            lc.setRefIPD(ipd);
-            return lc;
-        }
-
-        protected List getNextKnuthElements(LayoutContext context, int alignment) {
-            LayoutManager curLM; // currently active LM
-            List returnList = new LinkedList();
-
-            while ((curLM = getChildLM()) != null) {
-                LayoutContext childLC = LayoutContext.newInstance();
-                childLC.setStackLimitBP(context.getStackLimitBP());
-                childLC.setRefIPD(context.getRefIPD());
-                childLC.setWritingMode(context.getWritingMode());
-
-                List returnedList = null;
-                //The following is a HACK! Ignore leading and trailing white space
-                boolean ignore = curLM instanceof TextLayoutManager;
-                if (!curLM.isFinished()) {
-                    returnedList = curLM.getNextKnuthElements(childLC, alignment);
-                }
-                if (returnedList != null && !ignore) {
-                    lm.wrapPositionElements(returnedList, returnList);
-                }
-            }
-            SpaceResolver.resolveElementList(returnList);
-            setFinished(true);
-            return returnList;
-        }
-
-        protected int getCurrentDisplayAlign() {
-            return displayAlign;
-        }
-
-        protected boolean hasMoreContent() {
-            return !lm.isFinished();
-        }
-
-        protected void addAreas(PositionIterator posIter, LayoutContext context) {
-            AreaAdditionUtil.addAreas(lm, posIter, context);
-        }
-
-        protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
-                BlockSequence originalList, BlockSequence effectiveList) {
-            if (partCount > 1) {
-                PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst();
-                int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
-                        effectiveList.ignoreAtStart, pos.getLeafPos());
-                overflow += alg.totalWidth - firstPartLength;
-            }
-            //Rendering all parts (not just the first) at once for the case where the parts that
-            //overflow should be visible.
-            alg.removeAllPageBreaks();
-            //Directly add areas after finding the breaks
-            this.addAreas(alg, 1, originalList, effectiveList);
-        }
-
-        protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
-            //nop for static content
-        }
-
-        protected LayoutManager getCurrentChildLM() {
-            return null; //TODO NYI
-        }
     }
 
     /**
index 77ac8c4d60a1b43545bbd3ac774ad1069b0d4d2a..ebf7a9ccbcf012068c5ca4374c43fc5344492199 100644 (file)
@@ -480,7 +480,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
             context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
         }
 
-        addMarkersToPage(
+        registerMarkers(
                 true,
                 !areaCreated,
                 lastPos == null || isLast(lastPos));
@@ -542,7 +542,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         setTraits(areaCreated, lastPos == null || !isLast(lastPos));
         parentLayoutManager.addChildArea(getCurrentArea());
 
-        addMarkersToPage(
+        registerMarkers(
                 false,
                 !areaCreated,
                 lastPos == null || isLast(lastPos));
index 480934bf3fed79bc29ae70d7ed618e91dc764159..61d8a891d48cacb4a3e53f7980fb715eec3c2420 100644 (file)
@@ -171,7 +171,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
             }
         }
 
-        addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(true, isFirst(firstPos), isLast(lastPos));
 
         PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
         while ((childLM = childPosIter.getNextChildLM()) != null) {
@@ -184,7 +184,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
             childLM.addAreas(childPosIter, lc);
         }
 
-        addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(false, isFirst(firstPos), isLast(lastPos));
 
         // We are done with this area add the background
         TraitSetter.addBackground(curBlockArea,
index 3204a867ef5e3cce73181628c0be05074f890cb8..f017da3811fc8a0aa4b682ebeb052fc9a99b1c22 100644 (file)
@@ -136,7 +136,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp
             }
         }
 
-        addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(true, isFirst(firstPos), isLast(lastPos));
 
         PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
         while ((childLM = childPosIter.getNextChildLM()) != null) {
@@ -149,7 +149,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp
             childLM.addAreas(childPosIter, lc);
         }
 
-        addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(false, isFirst(firstPos), isLast(lastPos));
 
         flush();
 
index 32aa4c674aae15819cf533d323ebffac43efc74c..083e4ee1bb5bf86348023bac7ad918a0d990f886 100644 (file)
@@ -509,7 +509,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
             }
         }
 
-        addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(true, isFirst(firstPos), isLast(lastPos));
 
         // use the first and the last ListItemPosition to determine the
         // corresponding indexes in the original labelList and bodyList
@@ -563,7 +563,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
         }
         curBlockArea.setBPD(itemBPD);
 
-        addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+        registerMarkers(false, isFirst(firstPos), isLast(lastPos));
 
         // We are done with this area add the background
         TraitSetter.addBackground(curBlockArea,
index 955dafabd3b317ec906d9ed30ecdb82a53874171..6b0ef6ebbe227ae6e93b349fac1630a19503d35e 100644 (file)
@@ -303,11 +303,16 @@ class RowPainter {
                     borderAfterWhich = ConditionalBorder.REST;
                 }
 
+                // when adding the areas for the TableCellLayoutManager this helps with the isLast trait
+                // if, say, the first cell of a row has content that fits in the page, but the content of
+                // the second cell does not fit this will assure that the isLast trait for the first cell
+                // will also be false
+                lastCellParts[i].pgu.getCellLM().setLastTrait(lastCellParts[i].isLastPart());
                 addAreasForCell(firstCellParts[i].pgu,
                         firstCellParts[i].start, lastCellParts[i].end,
                         actualRowHeight, borderBeforeWhich, borderAfterWhich,
                         lastOnPage);
-                firstCellParts[i] = null;
+                firstCellParts[i] = null; // why? what about the lastCellParts[i]?
                 Arrays.fill(firstCellOnPage, i, i + currentGU.getCell().getNumberColumnsSpanned(),
                         false);
             }
index 2ca5a26d183fc1227badcbd99fb4bbb0d1d80181..f810d20c58a8a5d007a441055cf2f475ae0d4abb 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table;
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -28,19 +29,24 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.Trait;
+import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.flow.table.ConditionalBorder;
 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.TableCell;
 import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.fo.flow.table.TableFooter;
+import org.apache.fop.fo.flow.table.TableHeader;
 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.AbstractLayoutManager;
 import org.apache.fop.layoutmgr.AreaAdditionUtil;
 import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.ElementListObserver;
 import org.apache.fop.layoutmgr.ElementListUtils;
 import org.apache.fop.layoutmgr.Keep;
 import org.apache.fop.layoutmgr.KnuthBox;
@@ -49,8 +55,10 @@ import org.apache.fop.layoutmgr.KnuthGlue;
 import org.apache.fop.layoutmgr.KnuthPenalty;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.LocalBreaker;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;
 import org.apache.fop.layoutmgr.SpaceResolver;
 import org.apache.fop.layoutmgr.TraitSetter;
 import org.apache.fop.traits.BorderProps;
@@ -79,6 +87,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
     private int totalHeight;
     private int usedBPD;
     private boolean emptyCell = true;
+    private boolean isDescendantOfTableFooter;
+    private boolean isDescendantOfTableHeader;
+    private boolean hasRetrieveTableMarker;
+
+    // place holder for the addAreas arguments
+    private boolean savedAddAreasArguments;
+    private PositionIterator savedParentIter;
+    private LayoutContext savedLayoutContext;
+    private int[] savedSpannedGridRowHeights;
+    private int savedStartRow;
+    private int savedEndRow;
+    private int savedBorderBeforeWhich;
+    private int savedBorderAfterWhich;
+    private boolean savedFirstOnPage;
+    private boolean savedLastOnPage;
+    private RowPainter savedPainter;
+    private int savedFirstRowHeight;
+    // this is set to false when the table-cell has a retrieve-table-marker and is in the table-header
+    private boolean flushArea = true;
+
+    // this information is set by the RowPainter
+    private boolean isLastTrait;
 
     /**
      * Create a new Cell layout manager.
@@ -88,6 +118,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
     public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
         super(node);
         this.primaryGridUnit = pgu;
+        this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader
+                || node.getParent() instanceof TableHeader;
+        this.isDescendantOfTableFooter = node.getParent().getParent() instanceof TableFooter
+                || node.getParent() instanceof TableFooter;
+        this.hasRetrieveTableMarker = node.hasRetrieveTableMarker();
     }
 
     /** @return the table-cell FO */
@@ -248,6 +283,84 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
         totalHeight = h;
     }
 
+    private void clearRetrieveTableMarkerChildNodes(List<LayoutManager> childrenLMs) {
+        if (childrenLMs == null) {
+            return;
+        }
+        int n = childrenLMs.size();
+        for (int j = 0; j < n; j++) {
+            LayoutManager lm = (LayoutManager) childrenLMs.get(j);
+            if (lm == null) {
+                return;
+            } else if (lm instanceof RetrieveTableMarkerLayoutManager) {
+                ((AbstractLayoutManager) lm).getFObj().clearChildNodes();
+            } else {
+                List<LayoutManager> lms = lm.getChildLMs();
+                clearRetrieveTableMarkerChildNodes(lms);
+            }
+        }
+    }
+
+    /**
+     * Checks whether the associated table cell of this LM is in a table header or footer.
+     * @return true if descendant of table header or footer
+     */
+    private boolean isDescendantOfTableHeaderOrFooter() {
+        return (isDescendantOfTableFooter || isDescendantOfTableHeader);
+    }
+
+    private void saveAddAreasArguments(PositionIterator parentIter, LayoutContext layoutContext,
+            int[] spannedGridRowHeights, int startRow, int endRow, int borderBeforeWhich,
+            int borderAfterWhich, boolean firstOnPage, boolean lastOnPage, RowPainter painter,
+            int firstRowHeight) {
+        // checks for savedAddAreasArguments and isDescendantOfTableHeader were already made but repeat them
+        if (savedAddAreasArguments) {
+            return;
+        }
+        if (isDescendantOfTableHeader) {
+            savedAddAreasArguments = true;
+            savedParentIter = null /* parentIter */;
+            savedLayoutContext = null /* layoutContext */;
+            savedSpannedGridRowHeights = spannedGridRowHeights;
+            savedStartRow = startRow;
+            savedEndRow = endRow;
+            savedBorderBeforeWhich = borderBeforeWhich;
+            savedBorderAfterWhich = borderAfterWhich;
+            savedFirstOnPage = firstOnPage;
+            savedLastOnPage = lastOnPage;
+            savedPainter = painter;
+            savedFirstRowHeight = firstRowHeight;
+            TableLayoutManager parentTableLayoutManager = getTableLayoutManager();
+            parentTableLayoutManager.saveTableHeaderTableCellLayoutManagers(this);
+            // this saving is done the first time the addArea() is called; since the retrieve-table-markers
+            // cannot be resolved at this time we do not want to flush the area; the area needs nevertheless
+            // be built so that space is allocated for it.
+            flushArea = false;
+        }
+    }
+
+    private TableLayoutManager getTableLayoutManager() {
+        LayoutManager parentLM = getParent();
+        while (!(parentLM instanceof TableLayoutManager)) {
+            parentLM = parentLM.getParent();
+        }
+        TableLayoutManager tlm = (TableLayoutManager) parentLM;
+        return tlm;
+    }
+
+    /**
+     * Calls the addAreas() using the original arguments.
+     */
+    protected void repeatAddAreas() {
+        if (savedAddAreasArguments) {
+            addAreas(savedParentIter, savedLayoutContext, savedSpannedGridRowHeights, savedStartRow,
+                    savedEndRow, savedBorderBeforeWhich, savedBorderAfterWhich, savedFirstOnPage,
+                    savedLastOnPage, savedPainter, savedFirstRowHeight);
+            // so that the arguments of the next table fragment header can be saved
+            savedAddAreasArguments = false;
+        }
+    }
+
     /**
      * Add the areas for the break points. The cell contains block stacking layout
      * managers that add block areas.
@@ -407,7 +520,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
             }
         }
 
-        AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
+        if (isDescendantOfTableHeaderOrFooter()) {
+            if (hasRetrieveTableMarker) {
+                if (isDescendantOfTableHeader && !savedAddAreasArguments) {
+                    saveAddAreasArguments(parentIter, layoutContext, spannedGridRowHeights, startRow, endRow,
+                            borderBeforeWhich, borderAfterWhich, firstOnPage, lastOnPage, painter,
+                            firstRowHeight);
+                }
+                recreateChildrenLMs();
+                int displayAlign = ((TableCell) this.getFObj()).getDisplayAlign();
+                TableCellBreaker breaker = new TableCellBreaker(this, cellIPD, displayAlign);
+                breaker.doLayout(usedBPD, false);
+                // this is needed so the next time the LMs are recreated they look like the originals; this
+                // is due to the fact that during the doLayout() above the FO tree changes when the
+                // retrieve-table-markers are resolved
+                clearRetrieveTableMarkerChildNodes(getChildLMs());
+            }
+        }
+
+        // if hasRetrieveTableMarker == true the areas were already added when the re-layout was done above
+        if (!hasRetrieveTableMarker) {
+            AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
+        }
         // Re-adjust the cell's bpd as it may have been modified by the previous call
         // for some reason (?)
         curBlockArea.setBPD(cellBPD);
@@ -418,7 +552,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
                     getTableCell().getCommonBorderPaddingBackground(), this);
         }
 
-        flush();
+        if (flushArea) {
+            flush();
+        } else {
+            flushArea = true;
+        }
 
         curBlockArea = null;
 
@@ -604,4 +742,49 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
         return true;
     }
 
+    private class TableCellBreaker extends LocalBreaker {
+
+        public TableCellBreaker(TableCellLayoutManager lm, int ipd, int displayAlign) {
+            super(lm, ipd, displayAlign);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected void observeElementList(List elementList) {
+            String elementListID = lm.getParent().getFObj().getId() + "-" + lm.getFObj().getId();
+            ElementListObserver.observe(elementList, "table-cell", elementListID);
+        }
+
+    }
+
+    /**
+     * Registers the FO's markers on the current PageViewport and parent Table.
+     *
+     * @param isStarting    boolean indicating whether the markers qualify as 'starting'
+     * @param isFirst   boolean indicating whether the markers qualify as 'first'
+     * @param isLast    boolean indicating whether the markers qualify as 'last'
+     */
+    protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) {
+        Map<String, Marker> markers = getTableCell().getMarkers();
+        if (markers != null) {
+            getCurrentPV().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
+            if (!isDescendantOfTableHeaderOrFooter()) {
+                getTableLayoutManager().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
+            }
+        }
+    }
+
+    void setLastTrait(boolean isLast) {
+        isLastTrait = isLast;
+    }
+
+    /** {@inheritDoc} */
+    public void setParent(LayoutManager lm) {
+        this.parentLayoutManager = lm;
+        if (this.hasRetrieveTableMarker) {
+            this.getTableLayoutManager().flagAsHavingRetrieveTableMarker();
+        }
+    }
+
 }
index ea8774716a16a698ea6499425f4b83886b00ad1d..d423b339d659705735fc0be7be3f6a50b0b63f29 100644 (file)
@@ -31,9 +31,11 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.datatypes.PercentBaseContext;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.flow.table.EffRow;
 import org.apache.fop.fo.flow.table.PrimaryGridUnit;
 import org.apache.fop.fo.flow.table.Table;
+import org.apache.fop.fo.flow.table.TableBody;
 import org.apache.fop.fo.flow.table.TablePart;
 import org.apache.fop.layoutmgr.BreakElement;
 import org.apache.fop.layoutmgr.ElementListUtils;
@@ -400,9 +402,13 @@ public class TableContentLayoutManager implements PercentBaseContext {
             }
         }
 
-        Map markers = getTableLM().getTable().getMarkers();
+        // there may be table fragment markers stored; clear them since we are starting a new fragment
+        tableLM.clearTableFragmentMarkers();
+
+        // note: markers at table level are to be retrieved by the page, not by the table itself
+        Map<String, Marker> markers = getTableLM().getTable().getMarkers();
         if (markers != null) {
-            getTableLM().getCurrentPV().addMarkers(markers,
+            getTableLM().getCurrentPV().registerMarkers(markers,
                     true, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
         }
 
@@ -430,6 +436,10 @@ public class TableContentLayoutManager implements PercentBaseContext {
             addBodyAreas(tablePositions.iterator(), painter, footerElements == null);
         }
 
+        // if there are TCLMs saved because they have a RetrieveTableMarker, we repeat the header areas now;
+        // this can also be done after the areas for the footer are added but should be the same as here
+        tableLM.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
+
         if (footerElements != null) {
             boolean ancestorTreatAsArtifact = layoutContext.treatAsArtifact();
             layoutContext.setTreatAsArtifact(treatFooterAsArtifact);
@@ -442,7 +452,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
         this.usedBPD += painter.getAccumulatedBPD();
 
         if (markers != null) {
-            getTableLM().getCurrentPV().addMarkers(markers,
+            getTableLM().getCurrentPV().registerMarkers(markers,
                     false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
         }
     }
@@ -503,14 +513,20 @@ public class TableContentLayoutManager implements PercentBaseContext {
      */
     private void addTablePartAreas(List positions, RowPainter painter, TablePart body,
             boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) {
-        getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
+        getTableLM().getCurrentPV().registerMarkers(body.getMarkers(),
                 true, isFirstPos, isLastPos);
+        if (body instanceof TableBody) {
+            getTableLM().registerMarkers(body.getMarkers(), true, isFirstPos, isLastPos);
+        }
         painter.startTablePart(body);
         for (Iterator iter = positions.iterator(); iter.hasNext();) {
             painter.handleTableContentPosition((TableContentPosition) iter.next());
         }
-        getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
+        getTableLM().getCurrentPV().registerMarkers(body.getMarkers(),
                 false, isFirstPos, isLastPos);
+        if (body instanceof TableBody) {
+            getTableLM().registerMarkers(body.getMarkers(), false, isFirstPos, isLastPos);
+        }
         painter.endTablePart(lastInBody, lastOnPage);
     }
 
index 928be43c4210ff18187b1506e79b5a3af18b5f86..7d4c2654662e3659e8ad1795bef008e23d07c6b3 100644 (file)
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -33,6 +34,9 @@ import org.apache.fop.datatypes.LengthBase;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.Markers;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
 import org.apache.fop.fo.flow.table.Table;
 import org.apache.fop.fo.flow.table.TableColumn;
 import org.apache.fop.fo.properties.KeepProperty;
@@ -93,6 +97,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager
 
     private Position auxiliaryPosition;
 
+    // this holds a possible list of TCLMs that needed to have their addAreas() repeated
+    private List<TableCellLayoutManager> savedTCLMs;
+    private boolean areAllTCLMsSaved;
+
+    private Markers tableMarkers;
+    private Markers tableFragmentMarkers;
+
+    private boolean hasRetrieveTableMarker;
+
     /**
      * Temporary holder of column background informations for a table-cell's area.
      *
@@ -560,4 +573,112 @@ public class TableLayoutManager extends BlockStackingLayoutManager
         tableUnit = 0.0;
     }
 
+    /**
+     * Saves a TableCellLayoutManager for later use.
+     * 
+     * @param tclm a TableCellLayoutManager that has a RetrieveTableMarker
+     */
+    protected void saveTableHeaderTableCellLayoutManagers(TableCellLayoutManager tclm) {
+        if (savedTCLMs == null) {
+            savedTCLMs = new ArrayList<TableCellLayoutManager>();
+        }
+        if (!areAllTCLMsSaved) {
+            savedTCLMs.add(tclm);
+        }
+    }
+
+    /**
+     * Calls addAreas() for each of the saved TableCellLayoutManagers.
+     */
+    protected void repeatAddAreasForSavedTableHeaderTableCellLayoutManagers() {
+        if (savedTCLMs == null) {
+            return;
+        }
+        // if we get to this stage then we are at the footer of the table fragment; this means that no more
+        // different TCLM need to be saved (we already have all); we flag the list as being complete then
+        areAllTCLMsSaved = true;
+        for (int i = 0; i < savedTCLMs.size(); i++) {
+            TableCellLayoutManager tclm = savedTCLMs.get(i);
+            tclm.repeatAddAreas();
+        }
+    }
+
+    /**
+     * Resolves a RetrieveTableMarker by finding a qualifying Marker to which it is bound to.
+     * @param rtm the RetrieveTableMarker to be resolved
+     * @return a bound RetrieveTableMarker instance or null if no qualifying Marker found
+     */
+    public RetrieveTableMarker resolveRetrieveTableMarker(RetrieveTableMarker rtm) {
+        String name = rtm.getRetrieveClassName();
+        int originalPosition = rtm.getPosition();
+        boolean changedPosition = false;
+
+        Marker mark = null;
+        // try the primary retrieve scope area, which is the same as table-fragment
+        mark = (tableFragmentMarkers == null) ? null : tableFragmentMarkers.resolve(rtm);
+        if (mark == null && rtm.getBoundary() != Constants.EN_TABLE_FRAGMENT) {
+            rtm.changePositionTo(Constants.EN_LAST_ENDING);
+            changedPosition = true;
+            // try the page scope area
+            mark = getCurrentPV().resolveMarker(rtm);
+            if (mark == null && rtm.getBoundary() != Constants.EN_PAGE) {
+                // try the table scope area
+                mark = (tableMarkers == null) ? null : tableMarkers.resolve(rtm);
+            }
+        }
+        if (changedPosition) {
+            // so that the next time it is called looks unchanged
+            rtm.changePositionTo(originalPosition);
+        }
+        if (mark == null) {
+            log.debug("found no marker with name: " + name);
+            return null;
+        } else {
+            rtm.bindMarker(mark);
+            return rtm;
+        }
+    }
+
+    /**
+     * Register the markers for this table.
+     *
+     * @param marks the map of markers to add
+     * @param starting if the area being added is starting or ending
+     * @param isfirst if the area being added has is-first trait
+     * @param islast if the area being added has is-last trait
+     */
+    public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst,
+            boolean islast) {
+        if (tableMarkers == null) {
+            tableMarkers = new Markers();
+        }
+        tableMarkers.register(marks, starting, isfirst, islast);
+        if (tableFragmentMarkers == null) {
+            tableFragmentMarkers = new Markers();
+        }
+        tableFragmentMarkers.register(marks, starting, isfirst, islast);
+    }
+
+    /**
+     * Clears the list of markers in the current table fragment. Should be called just before starting a new
+     * header (that belongs to the next table fragment).
+     */
+    protected void clearTableFragmentMarkers() {
+        tableFragmentMarkers = null;
+    }
+
+    public void flagAsHavingRetrieveTableMarker() {
+        hasRetrieveTableMarker = true;
+    }
+
+    protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting,
+            boolean isFirst, boolean isLast) {
+        // note: if we allow table-footer after a table-body this check should not be made and the markers
+        // should be registered regardless because the retrieval may be done only in the footer
+        if (hasRetrieveTableMarker) {
+            registerMarkers(markers, isStarting, isFirst, isLast);
+        }
+        super.possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
+    }
+
 }
index 40e4c8db624c6e51faaff441af1b6e16f8e06803..76b25a9eb44841083709f68b125b412fd6525be5 100644 (file)
@@ -62,6 +62,9 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Layout" dev="MH" type="add" fixes-bug="53924" due-to="Luis Bernardo">
+        Support for retrieve-table-markers
+      </action>
       <action context="Renderers" dev="VH" type="add" fixes-bug="53902">
         Added possibility to define â€˜header’ table columns (the same way as fo:table-header allows 
         to define header rows). When accessibility is enabled, this allows to set the appropriate 
diff --git a/test/java/org/apache/fop/fo/flow/MarkersTestCase.java b/test/java/org/apache/fop/fo/flow/MarkersTestCase.java
new file mode 100644 (file)
index 0000000..983a531
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.fo.flow;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.Markers;
+import org.apache.fop.fo.flow.RetrieveMarker;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
+
+public class MarkersTestCase {
+
+    @Test
+    public void testRegisterAndResolve() {
+        // consider 3 regions, and a boundary; the first region starts before the boundary and ends inside
+        // the boundary, the second region is fully inside the boundary, and the third region starts inside
+        // the boundary and ends after the boundary. in every region there are 2 markers, A and B.
+        // ======== region 1
+        Map<String, Marker> markers_region_1 = new HashMap<String, Marker>();
+        Marker marker_1A = mock(Marker.class);
+        Marker marker_1B = mock(Marker.class);
+        markers_region_1.put("A", marker_1A);
+        markers_region_1.put("B", marker_1B);
+        // ======== region 2
+        Map<String, Marker> markers_region_2 = new HashMap<String, Marker>();
+        Marker marker_2A = mock(Marker.class);
+        Marker marker_2B = mock(Marker.class);
+        markers_region_2.put("A", marker_2A);
+        markers_region_2.put("B", marker_2B);
+        // ======== region 3
+        Map<String, Marker> markers_region_3 = new HashMap<String, Marker>();
+        Marker marker_3A = mock(Marker.class);
+        Marker marker_3B = mock(Marker.class);
+        markers_region_3.put("A", marker_3A);
+        markers_region_3.put("B", marker_3B);
+        // instantiate markers for the boundary
+        Markers markers = new Markers();
+        // register the markers for the different regions
+        // region 1
+        markers.register(markers_region_1, true, false, true);
+        markers.register(markers_region_1, false, false, true);
+        // region 2
+        markers.register(markers_region_2, true, true, true);
+        markers.register(markers_region_2, false, true, true);
+        // region 3
+        markers.register(markers_region_3, true, true, false);
+        markers.register(markers_region_3, false, true, false);
+        // now prepare a RetrieveMarker
+        RetrieveMarker rm = mock(RetrieveMarker.class);
+        when(rm.getRetrieveClassName()).thenReturn("A");
+        when(rm.getLocalName()).thenReturn("retrieve-marker");
+        when(rm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test
+        // and resolve the marker for different positions
+        // EN_FSWP
+        when(rm.getPosition()).thenReturn(Constants.EN_FSWP);
+        // expect marker_2A
+        assertEquals(marker_2A, markers.resolve(rm));
+        // EN_LSWP
+        when(rm.getPosition()).thenReturn(Constants.EN_LSWP);
+        // expect marker_3A
+        assertEquals(marker_3A, markers.resolve(rm));
+        // EN_LEWP
+        when(rm.getPosition()).thenReturn(Constants.EN_LEWP);
+        // expect marker_2A
+        assertEquals(marker_2A, markers.resolve(rm));
+        // EN_FIC
+        when(rm.getPosition()).thenReturn(Constants.EN_FIC);
+        // expect marker_1A
+        assertEquals(marker_1A, markers.resolve(rm));
+        // now prepare a RetrieveTableMarker
+        RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
+        when(rtm.getRetrieveClassName()).thenReturn("B");
+        when(rtm.getLocalName()).thenReturn("retrieve-table-marker");
+        when(rtm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test
+        // and resolve the marker for different positions
+        // EN_FIRST_STARTING
+        when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
+        // expect marker_2B
+        assertEquals(marker_2B, markers.resolve(rtm));
+        // EN_LAST_STARTING
+        when(rtm.getPosition()).thenReturn(Constants.EN_LAST_STARTING);
+        // expect marker_3B
+        assertEquals(marker_3B, markers.resolve(rtm));
+        // EN_LAST_ENDING
+        when(rtm.getPosition()).thenReturn(Constants.EN_LAST_ENDING);
+        // expect marker_2B
+        assertEquals(marker_2B, markers.resolve(rtm));
+        // EN_FIRST_INCLUDING_CARRYOVER
+        when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_INCLUDING_CARRYOVER);
+        // expect marker_1B
+        assertEquals(marker_1B, markers.resolve(rtm));
+        // test also an invalid position
+        when(rm.getPosition()).thenReturn(Constants.EN_ABSOLUTE);
+        try {
+            Marker m = markers.resolve(rm);
+            fail("Expected an exception... instead got:" + m.toString());
+        } catch (RuntimeException re) {
+            // do nothing
+        }
+    }
+}
index eb2a4fc923fe2304eaa128f2c23447502273f14d..e810f80c5a492fb7c21fd7c5c27f764b43cf2b5d 100644 (file)
@@ -19,6 +19,8 @@
 
 package org.apache.fop.layoutmgr;
 
+import org.junit.Test;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
@@ -31,7 +33,6 @@ import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.Region;
 import org.apache.fop.fo.pagination.Root;
 import org.apache.fop.fo.pagination.SimplePageMaster;
-import org.junit.Test;
 
 public class PageSequenceLayoutManagerTestCase {
 
diff --git a/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java
new file mode 100644 (file)
index 0000000..6a37de3
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FObj.FObjIterator;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
+import org.apache.fop.layoutmgr.LayoutManagerMapping.RetrieveTableMarkerLayoutManagerMaker;
+
+public class RetrieveTableMarkerLayoutManagerMakerTestCase {
+
+    @Test
+    public void testMake() throws FOPException {
+        // mock
+        FObjIterator foi = mock(FObjIterator.class);
+        when(foi.hasNext()).thenReturn(true).thenReturn(false);
+        // mock
+        RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
+        // real RTMLMM, not mock
+        List l = new ArrayList();
+        LayoutManagerMapping lmm = new LayoutManagerMapping();
+        RetrieveTableMarkerLayoutManagerMaker rtmlmm = lmm.new RetrieveTableMarkerLayoutManagerMaker();
+        // test the case rtm has no child nodes
+        when(rtm.getChildNodes()).thenReturn(null);
+        rtmlmm.make(rtm, l);
+        assertTrue(l.size() == 1);
+        assertTrue(l.get(0) instanceof RetrieveTableMarkerLayoutManager);
+    }
+
+}
diff --git a/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java
new file mode 100644 (file)
index 0000000..fda9cbc
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
+import org.apache.fop.fo.flow.table.Table;
+import org.apache.fop.layoutmgr.inline.TextLayoutManager;
+import org.apache.fop.layoutmgr.table.TableLayoutManager;
+
+public class RetrieveTableMarkerLayoutManagerTestCase {
+
+    @Test
+    public void testGetNextKnuthElementsLayoutContextInt() {
+        LayoutContext lc = LayoutContext.newInstance();
+        // mock
+        Table t = mock(Table.class);
+        // mock
+        RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
+        when(rtm.getRetrieveClassName()).thenReturn("A");
+        when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
+        when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT);
+        // mock
+        TextLayoutManager tlm = mock(TextLayoutManager.class);
+        // mock
+        LayoutManagerMapping lmm = mock(LayoutManagerMapping.class);
+        when(lmm.makeLayoutManager(rtm)).thenReturn(tlm);
+        // mock
+        PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
+        when(pslm.getPSLM()).thenReturn(pslm);
+        when(pslm.getLayoutManagerMaker()).thenReturn(lmm);
+        // mock
+        TableLayoutManager tablelm = mock(TableLayoutManager.class);
+        when(tablelm.getTable()).thenReturn(t);
+        // mock
+        BlockLayoutManager blm = mock(BlockLayoutManager.class);
+        when(blm.getPSLM()).thenReturn(pslm);
+        when(blm.getParent()).thenReturn(tablelm);
+        // real RTMLM, not mock
+        RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm);
+        rtmlm.setParent(blm);
+        // test the case where resolution returns null
+        when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(null);
+        assertNull(rtmlm.getNextKnuthElements(lc, 0));
+        // test the case where resolution returns non null
+        List l = new ArrayList();
+        when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(rtm);
+        when(tlm.getNextKnuthElements(lc, 0)).thenReturn(l);
+        assertEquals(l, rtmlm.getNextKnuthElements(lc, 0));
+        verify(tlm).setParent(blm);
+        verify(tlm).initialize();
+    }
+
+}
diff --git a/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java
new file mode 100644 (file)
index 0000000..a370e5d
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.table;
+
+import java.awt.Color;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.fo.flow.table.PrimaryGridUnit;
+import org.apache.fop.fo.flow.table.Table;
+import org.apache.fop.fo.flow.table.TableCell;
+import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.fo.flow.table.TableHeader;
+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.fo.properties.CondLengthProperty;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;
+
+public class TableCellLayoutManagerTestCase {
+
+    // this test aims to check that the first call to addAreas() calls
+    // TLM.saveTableHeaderTableCellLayoutManagers() but the second call, through repeatAddAreas()
+    // does not call it again; there are a lot of mocks here, but just the necessary so that the
+    // addAreas() call can run to completion without NPE; also, the mocking needs to be done so
+    // the methods isDecendantOfTableHeaderOrFooter() and hasRetrieveTableMarker() return true.
+    @Test
+    public void testRepeatAddAreas() {
+        LayoutContext lc = LayoutContext.newInstance();
+        // mock background
+        CommonBorderPaddingBackground cbpb = mock(CommonBorderPaddingBackground.class);
+        // mock conditional length property
+        CondLengthProperty clp = mock(CondLengthProperty.class);
+        when(clp.getLengthValue()).thenReturn(0);
+        // real border info
+        BorderInfo bi = BorderInfo.getInstance(0, clp, Color.BLACK);
+        // mock column
+        TableColumn tcol = mock(TableColumn.class);
+        when(tcol.getCommonBorderPaddingBackground()).thenReturn(cbpb);
+        // mock table
+        Table t = mock(Table.class);
+        when(t.getColumn(0)).thenReturn(tcol);
+        // mock header
+        TableHeader th = mock(TableHeader.class);
+        when(th.getCommonBorderPaddingBackground()).thenReturn(cbpb);
+        // mock row
+        TableRow tr = mock(TableRow.class);
+        when(tr.getParent()).thenReturn(th);
+        // mock cell
+        TableCell tc = mock(TableCell.class);
+        when(tc.hasRetrieveTableMarker()).thenReturn(true);
+        when(tc.getTable()).thenReturn(t);
+        when(tc.getId()).thenReturn("cellId");
+        when(tc.getCommonBorderPaddingBackground()).thenReturn(cbpb);
+        when(tc.getParent()).thenReturn(tr);
+        // mock PGU
+        PrimaryGridUnit pgu = mock(PrimaryGridUnit.class);
+        when(pgu.getCell()).thenReturn(tc);
+        when(pgu.getColIndex()).thenReturn(0);
+        when(pgu.getBorderBefore(0)).thenReturn(bi);
+        when(pgu.getBorderAfter(0)).thenReturn(bi);
+        when(pgu.getBorderEnd()).thenReturn(bi);
+        when(pgu.getBorderStart()).thenReturn(bi);
+        when(pgu.getTablePart()).thenReturn(th);
+        // mock RTMLM
+        RetrieveTableMarkerLayoutManager rtmlm = mock(RetrieveTableMarkerLayoutManager.class);
+        when(rtmlm.isFinished()).thenReturn(true); // avoids infinite loop
+        // mock PSLM
+        PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
+        // mock TLM
+        TableLayoutManager tlm = mock(TableLayoutManager.class);
+        when(tlm.getPSLM()).thenReturn(pslm);
+        // mock PI
+        PositionIterator pi = mock(PositionIterator.class);
+        // mock RP
+        RowPainter rp = mock(RowPainter.class);
+
+        // real TCLM, not a mock!
+        TableCellLayoutManager tclm = new TableCellLayoutManager(tc, pgu);
+        tclm.addChildLM(rtmlm);
+        tclm.setParent(tlm);
+        // lets call addAreas
+        int[] n = {};
+        tclm.addAreas(pi, lc, n, 0, 0, 0, 0, true, true, rp, 0);
+        // check the TCLM is added to the TLM
+        verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm);
+        // call the repeat
+        tclm.repeatAddAreas();
+        // check the TCLM was not added again
+        verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm);
+    }
+}
diff --git a/test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java
new file mode 100644 (file)
index 0000000..2bc4197
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.table;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.fo.FONode.FONodeIterator;
+import org.apache.fop.fo.flow.table.Table;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.PositionIterator;
+
+public class TableContentLayoutManagerTestCase {
+
+    @Test
+    public void testAddAreas() {
+        LayoutContext lc = LayoutContext.newInstance();
+        // mock
+        ColumnSetup cs = mock(ColumnSetup.class);
+        when(cs.getColumnCount()).thenReturn(3);
+        // mock
+        FONodeIterator foni = mock(FONodeIterator.class);
+        when(foni.hasNext()).thenReturn(false);
+        // mock
+        Table t = mock(Table.class);
+        when(t.getChildNodes()).thenReturn(foni);
+        when(t.getMarkers()).thenReturn(null);
+        // mock
+        TableLayoutManager tlm = mock(TableLayoutManager.class);
+        when(tlm.getTable()).thenReturn(t);
+        when(tlm.getColumns()).thenReturn(cs);
+        // mock
+        PositionIterator pi = mock(PositionIterator.class);
+        when(pi.hasNext()).thenReturn(false);
+        // real TCLM, not a mock
+        TableContentLayoutManager tclm = new TableContentLayoutManager(tlm);
+        // check that addAreas() calls the clearTableFragments() on the table and the
+        // repeatAddAreasForSavedTableHeaderTableCellLayoutManagers on the TLM
+        tclm.addAreas(pi, lc);
+        verify(tlm).clearTableFragmentMarkers();
+        verify(tlm).repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
+    }
+
+}
diff --git a/test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java
new file mode 100644 (file)
index 0000000..46a394e
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.table;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.RetrieveTableMarker;
+import org.apache.fop.fo.flow.table.Table;
+import org.apache.fop.layoutmgr.BlockLayoutManager;
+import org.apache.fop.layoutmgr.Page;
+import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
+
+public class TableLayoutManagerTestCase {
+
+    @Test
+    public void testSavedTableCellLayoutManagersFunctionality() {
+        Table t = mock(Table.class);
+        TableCellLayoutManager tclm1 = mock(TableCellLayoutManager.class);
+        TableLayoutManager tlm = new TableLayoutManager(t);
+        tlm.saveTableHeaderTableCellLayoutManagers(tclm1);
+        tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
+        verify(tclm1).repeatAddAreas(); // called once
+        // test that after the first repeatAddAreas() call the list closes to new additions
+        TableCellLayoutManager tclm2 = mock(TableCellLayoutManager.class);
+        tlm.saveTableHeaderTableCellLayoutManagers(tclm2);
+        tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
+        verify(tclm1, times(2)).repeatAddAreas(); // called twice
+        verify(tclm2, never()).repeatAddAreas(); // never called
+    }
+
+    @Test
+    public void testResolveRetrieveTableMarker() {
+        // mock
+        Table t = mock(Table.class);
+        // mock
+        Marker m = mock(Marker.class);
+        // mock
+        RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
+        when(rtm.getRetrieveClassName()).thenReturn("A");
+        when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
+        // mock
+        PageViewport pv = mock(PageViewport.class);
+        when(pv.resolveMarker(rtm)).thenReturn(m);
+        // mock
+        Page p = mock(Page.class);
+        when(p.getPageViewport()).thenReturn(pv);
+        // mock
+        PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
+        when(pslm.getPSLM()).thenReturn(pslm);
+        when(pslm.getCurrentPage()).thenReturn(p);
+        // mock
+        BlockLayoutManager blm = mock(BlockLayoutManager.class);
+        blm.setParent(pslm);
+        when(blm.getPSLM()).thenReturn(pslm);
+        // real TLM, not mock
+        TableLayoutManager tlm = new TableLayoutManager(t);
+        tlm.setParent(blm);
+        // register a marker
+        HashMap<String, Marker> markers1 = new HashMap<String, Marker>();
+        Marker m1 = mock(Marker.class);
+        markers1.put("A", m1);
+        tlm.registerMarkers(markers1, true, true, true);
+        tlm.registerMarkers(markers1, false, true, true);
+        // check that if there is a marker at table fragment level the RTM is returned
+        assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm));
+        verify(rtm, never()).getBoundary();
+        // check that if there is no marker at table fragment level and that is the boundary
+        // we get a null return value
+        when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT);
+        when(rtm.getRetrieveClassName()).thenReturn("B");
+        assertNull(tlm.resolveRetrieveTableMarker(rtm));
+        verify(rtm).getBoundary();
+        verify(rtm, never()).changePositionTo(Constants.EN_LAST_ENDING);
+        // check that if there is no marker at table fragment level and the boundary is page
+        // then we try to do the resolution at page level; test the case a marker is found
+        when(rtm.getBoundary()).thenReturn(Constants.EN_PAGE);
+        assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm));
+        verify(rtm).changePositionTo(Constants.EN_LAST_ENDING);
+        verify(rtm).changePositionTo(Constants.EN_FIRST_STARTING);
+        verify(pv).resolveMarker(rtm);
+        // test the same situation but in this case the marker is not found
+        when(pv.resolveMarker(rtm)).thenReturn(null);
+        assertNull(tlm.resolveRetrieveTableMarker(rtm));
+        // test the situation where the marker is not found at page level but the boundary is table
+        when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE);
+        assertNull(tlm.resolveRetrieveTableMarker(rtm));
+    }
+
+}
diff --git a/test/layoutengine/standard-testcases/markers_10.xml b/test/layoutengine/standard-testcases/markers_10.xml
new file mode 100644 (file)
index 0000000..0921da0
--- /dev/null
@@ -0,0 +1,144 @@
+<?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 markers on broken tables.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="2in">
+          <fo:region-body margin="0.5in 0"/>
+          <fo:region-before extent="0.5in"/>
+          <fo:region-after extent="0.5in"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:static-content flow-name="xsl-region-before">
+          <fo:block background-color="yellow">
+            f-i-c: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="first-including-carryover"/>
+          </fo:block>
+          <fo:block background-color="yellow">
+            f-s-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="first-starting-within-page"/>
+          </fo:block>
+        </fo:static-content>
+        <fo:static-content flow-name="xsl-region-after">
+          <fo:block text-align="end" background-color="yellow">
+            l-s-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="last-starting-within-page"/>
+          </fo:block>
+          <fo:block text-align="end" background-color="yellow">
+            l-e-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="last-ending-within-page"/>
+          </fo:block>
+        </fo:static-content>
+        <fo:flow flow-name="xsl-region-body">
+          <fo:table color="black" table-layout="fixed">
+            <fo:table-column number-columns-repeated="2"/>
+            <fo:table-body>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row1</fo:marker>
+                  <fo:block>row1</fo:block>
+                  <fo:block>row1</fo:block>
+                  <fo:block>row1</fo:block>
+                  <fo:block>row1</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row1</fo:marker>
+                  <fo:block>row1</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row2</fo:marker>
+                  <fo:block>row2</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row2</fo:marker>
+                  <fo:block>row2</fo:block>
+                  <fo:block>row2</fo:block>
+                  <fo:block>row2</fo:block>
+                  <fo:block>row2</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row3</fo:marker>
+                  <fo:block>row3</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row3</fo:marker>
+                  <fo:block>row3</fo:block>
+                  <fo:block>row3</fo:block>
+                  <fo:block>row3</fo:block>
+                  <fo:block>row3</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row4</fo:marker>
+                  <fo:block>row4</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row5</fo:marker>
+                  <fo:block>row5</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">row5</fo:marker>
+                  <fo:block>row5</fo:block>
+                  <fo:block>row5</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell column-number="2">
+                  <fo:marker marker-class-name="test">row6</fo:marker>
+                  <fo:block>row6</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+            </fo:table-body>
+          </fo:table>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <true xpath="starts-with(//pageViewport[@nr=1]//regionBefore/block[1],'f-i-c: row1')"/>
+    <true xpath="starts-with(//pageViewport[@nr=1]//regionBefore/block[2],'f-s-w-p: row1')"/>
+    <true xpath="starts-with(//pageViewport[@nr=1]//regionAfter/block[1],'l-s-w-p: row2')"/>
+    <true xpath="starts-with(//pageViewport[@nr=1]//regionAfter/block[2],'l-e-w-p: row1')"/>
+
+    <true xpath="starts-with(//pageViewport[@nr=2]//regionBefore/block[1],'f-i-c: row2')"/>
+    <true xpath="starts-with(//pageViewport[@nr=2]//regionBefore/block[2],'f-s-w-p: row3')"/>
+    <true xpath="starts-with(//pageViewport[@nr=2]//regionAfter/block[1],'l-s-w-p: row3')"/>
+    <true xpath="starts-with(//pageViewport[@nr=2]//regionAfter/block[2],'l-e-w-p: row2')"/>
+
+    <true xpath="starts-with(//pageViewport[@nr=3]//regionBefore/block[1],'f-i-c: row3')"/>
+    <true xpath="starts-with(//pageViewport[@nr=3]//regionBefore/block[2],'f-s-w-p: row4')"/>
+    <true xpath="starts-with(//pageViewport[@nr=3]//regionAfter/block[1],'l-s-w-p: row5')"/>
+    <true xpath="starts-with(//pageViewport[@nr=3]//regionAfter/block[2],'l-e-w-p: row5')"/>
+
+    <true xpath="starts-with(//pageViewport[@nr=4]//regionBefore/block[1],'f-i-c: row6')"/>
+    <true xpath="starts-with(//pageViewport[@nr=4]//regionBefore/block[2],'f-s-w-p: row6')"/>
+    <true xpath="starts-with(//pageViewport[@nr=4]//regionAfter/block[1],'l-s-w-p: row6')"/>
+    <true xpath="starts-with(//pageViewport[@nr=4]//regionAfter/block[2],'l-e-w-p: row6')"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml b/test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml
new file mode 100644 (file)
index 0000000..db578f9
--- /dev/null
@@ -0,0 +1,720 @@
+<?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 the retrieve-table-marker implementation. Rather, it serves as a regression test.</p>
+  </info>
+  <fo>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="10pt" font-family="sans-serif"
+  line-height="120%">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="base-page" page-height="210mm" page-width="297mm">
+      <fo:region-body margin="15mm" column-count="3" />
+      <fo:region-before extent="15mm" />
+      <fo:region-after extent="11.4mm" />
+    </fo:simple-page-master>
+  </fo:layout-master-set>
+  <fo:page-sequence master-reference="base-page">
+    <fo:flow flow-name="xsl-region-body" font-family="sans-serif">
+      <fo:block>The table has two columns, State and Facts. In the rows corresponding to states in the
+        Pacific Time Zone (PST), the State and Facts table
+        cells have a marker with the value of the State. That marker is retrieved in the table header and
+        footer. All the possible boundary and positions are considered in this example.
+      </fo:block>
+      <fo:block> rbwt = retrieve-boundary-within-table; rpwt = retrieve-position-within-table; 
+        f-i-c = first-including-carryover; f-s = first-starting; l-s = last-starting; l-e = last-ending;
+        t = table; p = page; p-s = page-sequence; t-f = table-fragment.
+      </fo:block>
+      <fo:wrapper>
+        <fo:table space-before="1em" width="100%" table-layout="fixed">
+          <fo:table-column column-width="proportional-column-width(3)" />
+          <fo:table-column column-width="proportional-column-width(5)" />
+          <fo:table-header>
+            <fo:table-row>
+              <fo:table-cell padding="4pt" number-columns-spanned="2"
+                background-color="yellow">
+                <fo:block>
+                  [rbwt:t][rpwt:f-i-c](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="table" />
+                  )
+                </fo:block>
+                <fo:block>
+                   [rbwt:t][rpwt:f-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-starting" retrieve-boundary-within-table="table" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:p][rpwt:f-i-c](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="page" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:p][rpwt:f-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-starting" retrieve-boundary-within-table="page" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:t-f][rpwt:f-i-c](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="table-fragment" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:t-f][rpwt:f-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="first-starting" retrieve-boundary-within-table="table-fragment" />
+                  )
+                </fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block font-weight="bold">State</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block font-weight="bold">Facts</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+          </fo:table-header>
+          <fo:table-footer>
+            <fo:table-row>
+              <fo:table-cell padding="4pt" number-columns-spanned="2"
+                background-color="yellow">
+                <fo:block>
+                  [rbwt:t][rpwt:l-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-starting" retrieve-boundary-within-table="table" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:t][rpwt:l-e](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-ending" retrieve-boundary-within-table="table" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:p][rpwt:l-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-starting" retrieve-boundary-within-table="page" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:p][rpwt:l-e](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-ending" retrieve-boundary-within-table="page" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:t-f][rpwt:l-s](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-starting" retrieve-boundary-within-table="table-fragment" />
+                  )
+                </fo:block>
+                <fo:block>
+                  [rbwt:t-f][rpwt:l-e](
+                  <fo:retrieve-table-marker retrieve-class-name="state"
+                    retrieve-position-within-table="last-ending" retrieve-boundary-within-table="table-fragment" />
+                  )
+                </fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+          </fo:table-footer>
+          <fo:table-body>
+            <!--fo:marker marker-class-name="state">Alabama</fo:marker-->
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Alabama</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Montgomery</fo:block>
+                <fo:block>Area: 52,423 square miles</fo:block>
+                <fo:block>Population: 4,447,100</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Alaska</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Juneau</fo:block>
+                <fo:block>Area: 656,425 square miles</fo:block>
+                <fo:block>Population: 626,932</fo:block>
+                <fo:block>Time Zone: GMT -9:00</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Arizona</fo:marker>
+                <fo:block>Arizona</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Arizona</fo:marker>
+                <fo:block>Capital: Phoenix</fo:block>
+                <fo:block>Area: 114,006 square miles</fo:block>
+                <fo:block>Population: 5,130,632</fo:block>
+                <fo:block>Time Zone: PST and MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Arkansas</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Little Rock</fo:block>
+                <fo:block>Area: 53,182 square miles</fo:block>
+                <fo:block>Population: 2,673,400</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">California</fo:marker>
+                <fo:block>California</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">California</fo:marker>
+                <fo:block>Capital: Sacramento</fo:block>
+                <fo:block>Area: 163,707 square miles</fo:block>
+                <fo:block>Population: 33,871,648</fo:block>
+                <fo:block>Time Zone: PST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Colorado</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Denver</fo:block>
+                <fo:block>Area: 104,100 square miles</fo:block>
+                <fo:block>Population: 4,301,261</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Connecticut</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Hartford</fo:block>
+                <fo:block>Area: 5,544 square miles</fo:block>
+                <fo:block>Population: 3,405,565</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Delaware</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Dover</fo:block>
+                <fo:block>Area: 1,954 square miles</fo:block>
+                <fo:block>Population: 783,600</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Florida</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Tallahassee</fo:block>
+                <fo:block>Area: 65,758 square miles</fo:block>
+                <fo:block>Population: 15,982,378</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Georgia</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Atlanta</fo:block>
+                <fo:block>Area: 59,441 square miles</fo:block>
+                <fo:block>Population: 8,186,453</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Hawaii</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Honolulu</fo:block>
+                <fo:block>Area: 10,932 square miles</fo:block>
+                <fo:block>Population: 1,211,537</fo:block>
+                <fo:block>Time Zone: GMT -11:00</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Idaho</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Boise</fo:block>
+                <fo:block>Area: 83,574 square miles</fo:block>
+                <fo:block>Population: 1,293,953</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Illinois</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Springfield</fo:block>
+                <fo:block>Area: 57,918 square miles</fo:block>
+                <fo:block>Population: 12,419,293</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Indiana</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Indianapolis</fo:block>
+                <fo:block>Area: 36,420 square miles</fo:block>
+                <fo:block>Population: 6,080,485</fo:block>
+                <fo:block>Time Zone: EST and CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Iowa</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Des Moines</fo:block>
+                <fo:block>Area: 56,276 square miles</fo:block>
+                <fo:block>Population: 2,926,324</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Kansas</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Topeka</fo:block>
+                <fo:block>Area: 82,282 square miles</fo:block>
+                <fo:block>Population: 2,688,418</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Kentucky</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Frankfort</fo:block>
+                <fo:block>Area: 40,411 square miles</fo:block>
+                <fo:block>Population: 4,041,769</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Louisiana</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Baton Rouge</fo:block>
+                <fo:block>Area: 51,843 square miles</fo:block>
+                <fo:block>Population: 4,468,976</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Maine</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Augusta</fo:block>
+                <fo:block>Area: 35,387 square miles</fo:block>
+                <fo:block>Population: 1,274,923</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Maryland</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Annapolis</fo:block>
+                <fo:block>Area: 12,407 square miles</fo:block>
+                <fo:block>Population: 5,296,486</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Massachusetts</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Boston</fo:block>
+                <fo:block>Area: 10,555 square miles</fo:block>
+                <fo:block>Population: 6,349,097</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Michigan</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Lansing</fo:block>
+                <fo:block>Area: 96,810 square miles</fo:block>
+                <fo:block>Population: 9,938,444</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Minnesota</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: St. Paul</fo:block>
+                <fo:block>Area: 86,943 square miles</fo:block>
+                <fo:block>Population: 4,919,479</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Mississippi</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Jackson</fo:block>
+                <fo:block>Area: 48,434 square miles</fo:block>
+                <fo:block>Population: 2,844,658</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Missouri</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Jefferson City</fo:block>
+                <fo:block>Area: 69,709 square miles</fo:block>
+                <fo:block>Population: 5,595,211</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Montana</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Helena</fo:block>
+                <fo:block>Area: 147,046 square miles</fo:block>
+                <fo:block>Population: 902,195</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Nebraska</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Lincoln</fo:block>
+                <fo:block>Area: XXXXXX square miles</fo:block>
+                <fo:block>Population: 77,358</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Nevada</fo:marker>
+                <fo:block>Nevada</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Nevada</fo:marker>
+                <fo:block>Capital: Carson City</fo:block>
+                <fo:block>Area: 110,567 square miles</fo:block>
+                <fo:block>Population: 1,998,257</fo:block>
+                <fo:block>Time Zone: PST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>New Hampshire</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Concord</fo:block>
+                <fo:block>Area: 9,351 square miles</fo:block>
+                <fo:block>Population: 1,235,786</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>New Jersey</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Trenton</fo:block>
+                <fo:block>Area: 8,722 square miles</fo:block>
+                <fo:block>Population: 8,414,350</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>New Mexico</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Santa Fe</fo:block>
+                <fo:block>Area: 121,593 square miles</fo:block>
+                <fo:block>Population: 1,819,046</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>New York</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Albany</fo:block>
+                <fo:block>Area: 54,475 square miles</fo:block>
+                <fo:block>Population: 18,976,457</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>North Carolina</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Raleigh</fo:block>
+                <fo:block>Area: 53,821 square miles</fo:block>
+                <fo:block>Population: 8,049,313</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>North Dakota</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Bismarck</fo:block>
+                <fo:block>Area: 70,704 square miles</fo:block>
+                <fo:block>Population: 642,200</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Ohio</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Columbus</fo:block>
+                <fo:block>Area: 44,828 square miles</fo:block>
+                <fo:block>Population: 11,353,140</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Oklahoma</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Oklahoma City</fo:block>
+                <fo:block>Area: 69,903 square miles</fo:block>
+                <fo:block>Population: 3,450,654</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Oregon</fo:marker>
+                <fo:block>Oregon</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Oregon</fo:marker>
+                <fo:block>Capital: Salem</fo:block>
+                <fo:block>Area: 98,386 square miles</fo:block>
+                <fo:block>Population: 3,421,399</fo:block>
+                <fo:block>Time Zone: PST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Pennsylvania</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Harrisburg</fo:block>
+                <fo:block>Area: 46,058 square miles</fo:block>
+                <fo:block>Population: 12,281,054</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Rhode Island</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Providence</fo:block>
+                <fo:block>Area: 1,045 square miles</fo:block>
+                <fo:block>Population: 1,048,319</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>South Carolina</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Columbia</fo:block>
+                <fo:block>Area: 32,007 square miles</fo:block>
+                <fo:block>Population: 4,012,012</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>South Dakota</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Pierre</fo:block>
+                <fo:block>Area: 77,121 square miles</fo:block>
+                <fo:block>Population: 754,844</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Tennessee</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Nashville</fo:block>
+                <fo:block>Area: 42,146 square miles</fo:block>
+                <fo:block>Population: 5,689,283</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Texas</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Austin</fo:block>
+                <fo:block>Area: 268,601 square miles</fo:block>
+                <fo:block>Population: 20,851,820</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Utah</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Salt Lake City</fo:block>
+                <fo:block>Area: 84,904 square miles</fo:block>
+                <fo:block>Population: 2,233,169</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Vermont</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Montpelier</fo:block>
+                <fo:block>Area: 9,615 square miles</fo:block>
+                <fo:block>Population: 608,827</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Virginia</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Richmond</fo:block>
+                <fo:block>Area: 42,769 square miles</fo:block>
+                <fo:block>Population: 7,078,515</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Washington</fo:marker>
+                <fo:block>Washington</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:marker marker-class-name="state">Washington</fo:marker>
+                <fo:block>Capital: Olympia</fo:block>
+                <fo:block>Area: 71,303 square miles</fo:block>
+                <fo:block>Population: 5,894,121</fo:block>
+                <fo:block>Time Zone: PST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>West Virginia</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Charleston</fo:block>
+                <fo:block>Area: 24,231 square miles</fo:block>
+                <fo:block>Population: 1,808,344</fo:block>
+                <fo:block>Time Zone: EST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Wisconsin</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Madison</fo:block>
+                <fo:block>Area: 65,503 square miles</fo:block>
+                <fo:block>Population: 5,363,675</fo:block>
+                <fo:block>Time Zone: CST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+            <fo:table-row>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Wyoming</fo:block>
+              </fo:table-cell>
+              <fo:table-cell padding-before="4pt" padding-after="4pt">
+                <fo:block>Capital: Cheyenne</fo:block>
+                <fo:block>Area: 97,818 square miles</fo:block>
+                <fo:block>Population: 493,782</fo:block>
+                <fo:block>Time Zone: MST</fo:block>
+              </fo:table-cell>
+            </fo:table-row>
+          </fo:table-body>
+        </fo:table>
+      </fo:wrapper>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="//lineArea[starts-with(., '[rbwt:t-f][rpwt:f-i-c](  )')]/ancestor::pageViewport/@nr" />
+    <eval expected="2" xpath="//lineArea[starts-with(., '[rbwt:t][rpwt:f-i-c]( Nevada )')]/ancestor::pageViewport/@nr" />
+    <eval expected="2" xpath="//lineArea[starts-with(., '[rbwt:p][rpwt:f-i-c]( Nevada )')]/ancestor::pageViewport/@nr" />
+    <eval expected="3" xpath="//lineArea[starts-with(., '[rbwt:t][rpwt:f-i-c]( Oregon )')]/ancestor::pageViewport/@nr" />
+    <eval expected="[rbwt:t][rpwt:f-i-c]( California )" xpath="//pageViewport[2]/page/regionViewport[3]//flow[1]/block[1]/block[17]/block[1]/lineArea" />
+    <eval expected="[rbwt:t][rpwt:f-i-c]( Oregon )" xpath="//pageViewport[3]/page/regionViewport[3]//flow[1]/block[1]/block[15]/block[1]/lineArea" />
+    <eval expected="[rbwt:t][rpwt:f-i-c]( Washington )" xpath="//pageViewport[4]/page/regionViewport[3]//flow[1]/block[1]/block[5]/block[1]/lineArea" />
+  </checks>
+</testcase>
\ No newline at end of file