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;
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
}
/**
- * 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.
*
* @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();
}
}
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;
}
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>
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");
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);
}
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
<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>
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;
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;
/**
private boolean isOutOfLineFODescendant = false;
/** Markers added to this element. */
- private Map markers = null;
+ private Map<String, Marker> markers;
private int bidiLevel = -1;
}
}
if (markers == null) {
- markers = new java.util.HashMap();
+ markers = new HashMap<String, Marker>();
}
if (!markers.containsKey(mcname)) {
markers.put(mcname, marker);
/**
* @return the collection of Markers attached to this object
*/
- public Map getMarkers() {
+ public Map<String, Marker> getMarkers() {
return markers;
}
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;
}
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}
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;
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
*/
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}.
/** {@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());
}
/**
* {@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} */
*/
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}.
/** {@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());
}
/**
* {@link org.apache.fop.fo.Constants#EN_LAST_ENDING}.
*/
public int getRetrievePositionWithinTable() {
- return this.retrievePositionWithinTable;
+ return getPosition();
}
/**
* {@link org.apache.fop.fo.Constants#EN_PAGE}.
*/
public int getRetrieveBoundaryWithinTable() {
- return this.retrieveBoundaryWithinTable;
+ return getBoundary();
}
/** {@inheritDoc} */
public int getNameId() {
return FO_RETRIEVE_TABLE_MARKER;
}
+
+ /** {@inheritDoc} */
+ public void clearChildNodes() {
+ super.clearChildNodes();
+ this.currentTextNode = null;
+ this.lastFOTextProcessed = null;
+ }
+
}
/** used for FO validation */
private boolean blockItemFound = false;
+ private boolean hasRetrieveTableMarker;
+
/**
* Create a TableCell instance with the given {@link FONode}
* as parent.
return FO_TABLE_CELL;
}
+ public void flagAsHavingRetrieveTableMarker() {
+ hasRetrieveTableMarker = true;
+ }
+
+ public boolean hasRetrieveTableMarker() {
+ return hasRetrieveTableMarker;
+ }
}
getUserAgent().getEventBroadcaster());
eventProducer.noMixRowsAndCells(this, getName(), getLocator());
}
+ } else if (localName.equals("retrieve-table-marker")) {
+ notSupportedChildError(loc, nsURI, localName);
} else {
invalidChildError(loc, nsURI, localName);
}
throw new UnsupportedOperationException("Not implemented");
}
+ public void preserveChildrenAtEndOfLayout() {
+
+ }
+
+ public void recreateChildrenLMs() {
+
+ }
}
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.
private int lastGeneratedPosition = -1;
private int smallestPosNumberChecked = Integer.MAX_VALUE;
+ private boolean preserveChildrenAtEndOfLayout;
+
/**
* Abstract layout manager.
*/
}
/**
- * 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);
}
}
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
*/
|| 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() {
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);
+ }
}
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) {
}
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;
}
}
if (bslm != null) {
- bslm.addMarkersToPage(
+ bslm.registerMarkers(
true,
bslm.isFirst(firstPos),
bslm.isLast(lastPos));
}
if (bslm != null) {
- bslm.addMarkersToPage(
+ bslm.registerMarkers(
false,
bslm.isFirst(firstPos),
bslm.isLast(lastPos));
- bslm.checkEndOfLayout(lastPos);
}
addId();
- addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(true, isFirst(firstPos), isLast(lastPos));
if (bcpos == null) {
// the Positions in positionList were inside the elements
bcpos.getBreaker().addContainedAreas(layoutContext);
}
- addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(false, isFirst(firstPos), isLast(lastPos));
TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
effSpaceBefore, effSpaceAfter);
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
childLM.addAreas(childPosIter, lc);
}
- addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(false, isFirst(firstPos), isLast(lastPos));
TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
effSpaceBefore, effSpaceAfter);
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;
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());
}
}
+ 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} */
--- /dev/null
+/*
+ * 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
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
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
- }
}
/**
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
}
- addMarkersToPage(
+ registerMarkers(
true,
!areaCreated,
lastPos == null || isLast(lastPos));
setTraits(areaCreated, lastPos == null || !isLast(lastPos));
parentLayoutManager.addChildArea(getCurrentArea());
- addMarkersToPage(
+ registerMarkers(
false,
!areaCreated,
lastPos == null || isLast(lastPos));
}
}
- addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(true, isFirst(firstPos), isLast(lastPos));
PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
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,
}
}
- addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(true, isFirst(firstPos), isLast(lastPos));
PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
childLM.addAreas(childPosIter, lc);
}
- addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
+ registerMarkers(false, isFirst(firstPos), isLast(lastPos));
flush();
}
}
- 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
}
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,
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);
}
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.Trait;
+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;
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;
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.
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 */
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.
}
}
- 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);
getTableCell().getCommonBorderPaddingBackground(), this);
}
- flush();
+ if (flushArea) {
+ flush();
+ } else {
+ flushArea = true;
+ }
curBlockArea = null;
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();
+ }
+ }
+
}
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;
}
}
- 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));
}
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);
this.usedBPD += painter.getAccumulatedBPD();
if (markers != null) {
- getTableLM().getCurrentPV().addMarkers(markers,
+ getTableLM().getCurrentPV().registerMarkers(markers,
false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
}
}
*/
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);
}
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;
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;
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.
*
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);
+ }
+
}
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
--- /dev/null
+/*
+ * 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
+ }
+ }
+}
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;
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 {
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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