Browse Source

Bugzilla#53924: Support for retrieve-table-markers, submitted by Luis Bernardo.


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1391502 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_0
Mehdi Houshmand 11 years ago
parent
commit
d261730a4f
41 changed files with 2432 additions and 332 deletions
  1. 18
    159
      src/java/org/apache/fop/area/PageViewport.java
  2. 3
    1
      src/java/org/apache/fop/fo/Constants.java
  3. 13
    0
      src/java/org/apache/fop/fo/FONode.java
  4. 3
    2
      src/java/org/apache/fop/fo/FOPropertyMapping.java
  5. 12
    0
      src/java/org/apache/fop/fo/FOValidationEventProducer.java
  6. 1
    0
      src/java/org/apache/fop/fo/FOValidationEventProducer.xml
  7. 10
    3
      src/java/org/apache/fop/fo/FObj.java
  8. 44
    0
      src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java
  9. 212
    0
      src/java/org/apache/fop/fo/flow/Markers.java
  10. 7
    10
      src/java/org/apache/fop/fo/flow/RetrieveMarker.java
  11. 14
    11
      src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java
  12. 9
    0
      src/java/org/apache/fop/fo/flow/table/TableCell.java
  13. 2
    0
      src/java/org/apache/fop/fo/flow/table/TablePart.java
  14. 7
    0
      src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
  15. 52
    9
      src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
  16. 7
    4
      src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
  17. 2
    3
      src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
  18. 2
    2
      src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
  19. 2
    2
      src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
  20. 20
    1
      src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
  21. 137
    0
      src/java/org/apache/fop/layoutmgr/LocalBreaker.java
  22. 75
    0
      src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java
  23. 5
    108
      src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
  24. 2
    2
      src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
  25. 2
    2
      src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
  26. 2
    2
      src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
  27. 2
    2
      src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
  28. 6
    1
      src/java/org/apache/fop/layoutmgr/table/RowPainter.java
  29. 185
    2
      src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
  30. 21
    5
      src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
  31. 121
    0
      src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
  32. 3
    0
      status.xml
  33. 128
    0
      test/java/org/apache/fop/fo/flow/MarkersTestCase.java
  34. 2
    1
      test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java
  35. 56
    0
      test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java
  36. 82
    0
      test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java
  37. 116
    0
      test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java
  38. 64
    0
      test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java
  39. 119
    0
      test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java
  40. 144
    0
      test/layoutengine/standard-testcases/markers_10.xml
  41. 720
    0
      test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml

+ 18
- 159
src/java/org/apache/fop/area/PageViewport.java View File

@@ -34,6 +34,10 @@ import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.AbstractRetrieveMarker;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.Markers;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.traits.WritingModeTraitsGetter;

@@ -81,13 +85,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable {

private Map<String, List<PageViewport>> pendingResolved = null;

// hashmap of markers for this page
// start and end are added by the fo that contains the markers
private Map<String, Marker> markerFirstStart = null;
private Map<String, Marker> markerLastStart = null;
private Map<String, Marker> markerFirstAny = null;
private Map<String, Marker> markerLastEnd = null;
private Map<String, Marker> markerLastAny = null;
private Markers pageMarkers;

/**
* logging instance
@@ -352,115 +350,23 @@ public class PageViewport extends AreaTreeObject implements Resolvable {
}

/**
* Add the markers for this page.
* Only the required markers are kept.
* For "first-starting-within-page" it adds the markers
* that are starting only if the marker class name is not
* already added.
* For "first-including-carryover" it adds any starting marker
* if the marker class name is not already added.
* For "last-starting-within-page" it adds all marks that
* are starting, replacing earlier markers.
* For "last-ending-within-page" it adds all markers that
* are ending, replacing earlier markers.
*
* Should this logic be placed in the Page layout manager.
* Register the markers for this page.
*
* @param marks the map of markers to add
* @param starting if the area being added is starting or ending
* @param isfirst if the area being added has is-first trait
* @param islast if the area being added has is-last trait
*/
public void addMarkers(Map<String, Marker> marks, boolean starting,
boolean isfirst, boolean islast) {

if (marks == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("--" + marks.keySet() + ": "
+ (starting ? "starting" : "ending")
+ (isfirst ? ", first" : "")
+ (islast ? ", last" : ""));
}

// at the start of the area, register is-first and any areas
if (starting) {
if (isfirst) {
if (markerFirstStart == null) {
markerFirstStart = new HashMap<String, Marker>();
}
if (markerFirstAny == null) {
markerFirstAny = new HashMap<String, Marker>();
}
// first on page: only put in new values, leave current
for (String key : marks.keySet()) {
if (!markerFirstStart.containsKey(key)) {
markerFirstStart.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding marker " + key + " to FirstStart");
}
}
if (!markerFirstAny.containsKey(key)) {
markerFirstAny.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding marker " + key + " to FirstAny");
}
}
}
if (markerLastStart == null) {
markerLastStart = new HashMap<String, Marker>();
}
// last on page: replace all
markerLastStart.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding all markers to LastStart");
}
} else {
if (markerFirstAny == null) {
markerFirstAny = new HashMap<String, Marker>();
}
// first on page: only put in new values, leave current
for (String key : marks.keySet()) {
if (!markerFirstAny.containsKey(key)) {
markerFirstAny.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding marker " + key + " to FirstAny");
}
}
}
}
} else {
// at the end of the area, register is-last and any areas
if (islast) {
if (markerLastEnd == null) {
markerLastEnd = new HashMap<String, Marker>();
}
// last on page: replace all
markerLastEnd.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding all markers to LastEnd");
}
}
if (markerLastAny == null) {
markerLastAny = new HashMap<String, Marker>();
}
// last on page: replace all
markerLastAny.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": "
+ "Adding all markers to LastAny");
}
public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) {
if (pageMarkers == null) {
pageMarkers = new Markers();
}
pageMarkers.register(marks, starting, isfirst, islast);
}


/**
* Get a marker from this page.
* Resolve a marker from this page.
* This will retrieve a marker with the class name
* and position.
*
@@ -468,64 +374,17 @@ public class PageViewport extends AreaTreeObject implements Resolvable {
* @param pos the position to retrieve
* @return Object the marker found or null
*/
public Marker getMarker(String name, int pos) {
Marker mark = null;
String posName = null;
switch (pos) {
case EN_FSWP:
if (markerFirstStart != null) {
mark = markerFirstStart.get(name);
posName = "FSWP";
}
if (mark == null && markerFirstAny != null) {
mark = markerFirstAny.get(name);
posName = "FirstAny after " + posName;
}
break;
case EN_FIC:
if (markerFirstAny != null) {
mark = markerFirstAny.get(name);
posName = "FIC";
}
break;
case EN_LSWP:
if (markerLastStart != null) {
mark = markerLastStart.get(name);
posName = "LSWP";
}
if (mark == null && markerLastAny != null) {
mark = markerLastAny.get(name);
posName = "LastAny after " + posName;
}
break;
case EN_LEWP:
if (markerLastEnd != null) {
mark = markerLastEnd.get(name);
posName = "LEWP";
}
if (mark == null && markerLastAny != null) {
mark = markerLastAny.get(name);
posName = "LastAny after " + posName;
}
break;
default:
assert false;
}
if (log.isTraceEnabled()) {
log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name
+ " at position " + posName);
public Marker resolveMarker(AbstractRetrieveMarker rm) {
if (pageMarkers == null) {
return null;
}
return mark;
return pageMarkers.resolve(rm);
}

/** Dumps the current marker data to the logger. */
public void dumpMarkers() {
if (log.isTraceEnabled()) {
log.trace("FirstAny: " + this.markerFirstAny);
log.trace("FirstStart: " + this.markerFirstStart);
log.trace("LastAny: " + this.markerLastAny);
log.trace("LastEnd: " + this.markerLastEnd);
log.trace("LastStart: " + this.markerLastStart);
if (pageMarkers != null) {
pageMarkers.dump();
}
}


+ 3
- 1
src/java/org/apache/fop/fo/Constants.java View File

@@ -1227,6 +1227,8 @@ public interface Constants {
int EN_BT = 204; // bottom to top
/** Enumeration constant */
int EN_TB_LR = 205; // for top-to-bottom, left-to-right writing mode
/** Enumeration constant -- for fo:retrieve-table-marker */
int EN_FIRST_INCLUDING_CARRYOVER = 206;
/** Number of enumeration constants defined */
int ENUM_COUNT = 205;
int ENUM_COUNT = 206;
}

+ 13
- 0
src/java/org/apache/fop/fo/FONode.java View File

@@ -562,6 +562,19 @@ public abstract class FONode implements Cloneable {
getFOValidationEventProducer().invalidChild(this, parentName, qn, ruleViolated, loc);
}

/**
* Helper function to return "not supported child" exceptions. Note that the child is valid, just not
* supported yet by FOP.
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
* @throws ValidationException the validation error provoked by the method call
*/
protected void notSupportedChildError(Locator loc, String nsURI, String lName) throws ValidationException {
getFOValidationEventProducer().notSupportedChild(this, getName(), new QName(nsURI, lName), loc);
}

/**
* Helper function to throw an error caused by missing mandatory child elements.
* (e.g., <code>fo:layout-master-set</code> not having any <code>fo:page-master</code>

+ 3
- 2
src/java/org/apache/fop/fo/FOPropertyMapping.java View File

@@ -2178,7 +2178,8 @@ public final class FOPropertyMapping implements Constants {
m = new EnumProperty.Maker(PR_RETRIEVE_POSITION_WITHIN_TABLE);
m.setInherited(false);
m.addEnum("first-starting", getEnumProperty(EN_FIRST_STARTING, "FIRST_STARTING"));
m.addEnum("first-including-carryover", getEnumProperty(EN_FIC, "FIC"));
m.addEnum("first-including-carryover",
getEnumProperty(EN_FIRST_INCLUDING_CARRYOVER, "FIRST_INCLUDING_CARRYOVER"));
m.addEnum("last-starting", getEnumProperty(EN_LAST_STARTING, "LAST_STARTING"));
m.addEnum("last-ending", getEnumProperty(EN_LAST_ENDING, "LAST_ENDING"));
m.setDefault("first-starting");
@@ -2189,7 +2190,7 @@ public final class FOPropertyMapping implements Constants {
m.setInherited(false);
m.addEnum("table", getEnumProperty(EN_TABLE, "TABLE"));
m.addEnum("table-fragment", getEnumProperty(EN_TABLE_FRAGMENT, "TABLE_FRAGMENT"));
m.addEnum("page", getEnumProperty(EN_DOCUMENT, "PAGE"));
m.addEnum("page", getEnumProperty(EN_PAGE, "PAGE"));
m.setDefault("table");
addPropertyMaker("retrieve-boundary-within-table", m);
}

+ 12
- 0
src/java/org/apache/fop/fo/FOValidationEventProducer.java View File

@@ -89,6 +89,18 @@ public interface FOValidationEventProducer extends EventProducer {
void invalidChild(Object source, String elementName, QName offendingNode, String ruleViolated,
Locator loc) throws ValidationException;

/**
* A valid but not yet supported child was encountered.
*
* @param source the event source
* @param elementName the name of the context node
* @param offendingNode the offending node
* @param loc the location of the error or null
* @throws ValidationException the validation error provoked by the method call
*/
void notSupportedChild(Object source, String elementName, QName offendingNode, Locator loc)
throws ValidationException;

/**
* A required child element is missing.
* @param source the event source

+ 1
- 0
src/java/org/apache/fop/fo/FOValidationEventProducer.xml View File

@@ -17,6 +17,7 @@
<message key="tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message>
<message key="nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message>
<message key="invalidChild">"{offendingNode}" is not a valid child of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message>
<message key="notSupportedChild">"{offendingNode}" as a child of "{elementName}" is not supported yet!{{locator}}</message>
<message key="missingChildElement">"{elementName}" is missing child elements.[ Required content model: {contentModel}]{{locator}}</message>
<message key="missingProperty">Element "{elementName}" is missing required property "{propertyName}"!{{locator}}</message>
<message key="idNotUnique">Property ID "{id}" (found on "{elementName}") previously used; ID values must be unique within a document!{severity,equals,EventSeverity:FATAL,, Any reference to it will be considered a reference to the first occurrence in the document.}{{locator}}</message>

+ 10
- 3
src/java/org/apache/fop/fo/FObj.java View File

@@ -20,6 +20,7 @@
package org.apache.fop.fo;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -35,6 +36,7 @@ import org.apache.xmlgraphics.util.QName;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.properties.PropertyMaker;

/**
@@ -65,7 +67,7 @@ public abstract class FObj extends FONode implements Constants {
private boolean isOutOfLineFODescendant = false;

/** Markers added to this element. */
private Map markers = null;
private Map<String, Marker> markers;

private int bidiLevel = -1;

@@ -356,7 +358,7 @@ public abstract class FObj extends FONode implements Constants {
}
}
if (markers == null) {
markers = new java.util.HashMap();
markers = new HashMap<String, Marker>();
}
if (!markers.containsKey(mcname)) {
markers.put(mcname, marker);
@@ -376,7 +378,7 @@ public abstract class FObj extends FONode implements Constants {
/**
* @return the collection of Markers attached to this object
*/
public Map getMarkers() {
public Map<String, Marker> getMarkers() {
return markers;
}

@@ -522,6 +524,11 @@ public abstract class FObj extends FONode implements Constants {
int found = 1;
FONode temp = getParent();
while (temp != null) {
if (temp instanceof TableCell && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) {
// note that if the retrieve-table-marker is not in a table-header/footer an exception is
// thrown, so no need to reset this flag in that case
((TableCell) temp).flagAsHavingRetrieveTableMarker();
}
if (temp.getNameId() == ancestorID) {
return found;
}

+ 44
- 0
src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java View File

@@ -46,6 +46,11 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {

private String retrieveClassName;

private int position;
private String positionLabel;
private int boundary;
private String boundaryLabel;

/**
* Create a new AbstractRetrieveMarker instance that
* is a child of the given {@link FONode}
@@ -206,4 +211,43 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {
return this.retrieveClassName;
}

protected void setBoundaryLabel(String label) {
this.boundaryLabel = label;
}

protected void setPositionLabel(String label) {
this.positionLabel = label;
}

public String getBoundaryLabel() {
return this.boundaryLabel;
}

public String getPositionLabel() {
return this.positionLabel;
}

protected void setPosition(int position) {
this.position = position;
}

protected void setBoundary(int boundary) {
this.boundary = boundary;
}

public int getPosition() {
return this.position;
}

public int getBoundary() {
return this.boundary;
}

public abstract String getLocalName();

public abstract int getNameId();

public void changePositionTo(int position) {
this.position = position;
}
}

+ 212
- 0
src/java/org/apache/fop/fo/flow/Markers.java View File

@@ -0,0 +1,212 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fo.flow;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.fo.Constants;

/**
* A class to register and resolve markers.
*/
public final class Markers {

// IsAny means either IsFirst or IsLast
private Map<String, Marker> firstQualifyingIsFirst;
private Map<String, Marker> firstQualifyingIsAny;
private Map<String, Marker> lastQualifyingIsFirst;
private Map<String, Marker> lastQualifyingIsLast;
private Map<String, Marker> lastQualifyingIsAny;

private static Log log = LogFactory.getLog(Markers.class);

/**
* Registers a marker with the position traits set.
* Only the required markers are kept.
* For "first-starting-within-page" it adds the markers
* that are starting only if the marker class name is not
* already added.
* For "first-including-carryover" it adds any starting marker
* if the marker class name is not already added.
* For "last-starting-within-page" it adds all marks that
* are starting, replacing earlier markers.
* For "last-ending-within-page" it adds all markers that
* are ending, replacing earlier markers.
*
* @param marks a map of markers to register
* @param starting whether the registration happens at the start (true) or end (false) the the area
* @param isfirst whether it is the first area of the parent LM
* @param islast whether it is the last area of the parent LM
*/
public void register(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) {
// TODO: find way to put the page number in the log tracing

if (marks == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("--" + marks.keySet() + ": " + (starting ? "starting" : "ending")
+ (isfirst ? ", first" : "") + (islast ? ", last" : ""));
}

if (starting) {
// at the start of the area, register is-first and any areas
if (firstQualifyingIsAny == null) {
firstQualifyingIsAny = new HashMap<String, Marker>();
}
if (isfirst) {
if (firstQualifyingIsFirst == null) {
firstQualifyingIsFirst = new HashMap<String, Marker>();
}
// first on scope: only put in new values, leave current
for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
if (!firstQualifyingIsFirst.containsKey(key)) {
firstQualifyingIsFirst.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("Adding marker " + key + " to firstQualifyingIsFirst");
}
}
if (!firstQualifyingIsAny.containsKey(key)) {
firstQualifyingIsAny.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("Adding marker " + key + " to firstQualifyingIsAny");
}
}
}
if (lastQualifyingIsFirst == null) {
lastQualifyingIsFirst = new HashMap<String, Marker>();
}
// last on scope: replace all
lastQualifyingIsFirst.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("Adding all markers to LastStart");
}
} else {
// first on scope: only put in new values, leave current
for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
if (!firstQualifyingIsAny.containsKey(key)) {
firstQualifyingIsAny.put(key, marks.get(key));
if (log.isTraceEnabled()) {
log.trace("Adding marker " + key + " to firstQualifyingIsAny");
}
}
}
}
} else {
// at the end of the area, register is-last and any areas
if (islast) {
if (lastQualifyingIsLast == null) {
lastQualifyingIsLast = new HashMap<String, Marker>();
}
// last on page: replace all
lastQualifyingIsLast.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("Adding all markers to lastQualifyingIsLast");
}
}
if (lastQualifyingIsAny == null) {
lastQualifyingIsAny = new HashMap<String, Marker>();
}
// last on page: replace all
lastQualifyingIsAny.putAll(marks);
if (log.isTraceEnabled()) {
log.trace("Adding all markers to lastQualifyingIsAny");
}
}
}

/**
* Retrieves the best candidate marker for the given position.
* @param name the key used to register the marker
* @param pos the retrieval scope position
* @return a Marker instance
*/
public Marker resolve(AbstractRetrieveMarker arm) {
Marker mark = null;
int pos = arm.getPosition();
String name = arm.getRetrieveClassName();
String posName = arm.getPositionLabel();
String localName = arm.getLocalName();
switch (pos) {
case Constants.EN_FSWP: // retrieve-marker
case Constants.EN_FIRST_STARTING: // retrieve-table-marker
if (firstQualifyingIsFirst != null) {
mark = firstQualifyingIsFirst.get(name);
}
if (mark == null && firstQualifyingIsAny != null) {
mark = firstQualifyingIsAny.get(name);
posName = "FirstAny after " + posName;
}
break;
case Constants.EN_FIC: // retrieve-marker
case Constants.EN_FIRST_INCLUDING_CARRYOVER: // retrieve-table-marker
if (firstQualifyingIsAny != null) {
mark = firstQualifyingIsAny.get(name);
}
break;
case Constants.EN_LSWP: // retrieve-marker
case Constants.EN_LAST_STARTING: // retrieve-table-marker
if (lastQualifyingIsFirst != null) {
mark = lastQualifyingIsFirst.get(name);
}
if (mark == null && lastQualifyingIsAny != null) {
mark = lastQualifyingIsAny.get(name);
posName = "LastAny after " + posName;
}
break;
case Constants.EN_LEWP: // retrieve-marker
case Constants.EN_LAST_ENDING: // retrieve-table-marker
if (lastQualifyingIsLast != null) {
mark = lastQualifyingIsLast.get(name);
}
if (mark == null && lastQualifyingIsAny != null) {
mark = lastQualifyingIsAny.get(name);
posName = "LastAny after " + posName;
}
break;
default:
throw new RuntimeException("Invalid position attribute in " + localName + ".");
}
if (log.isTraceEnabled()) {
// TODO: find way to put the page number here
log.trace(localName + ": name[" + name + "]; position [" + posName + "]");
}
return mark;
}

/** Dumps the current marker data to the logger. */
public void dump() {
if (log.isTraceEnabled()) {
log.trace("FirstAny: " + this.firstQualifyingIsAny);
log.trace("FirstStart: " + this.firstQualifyingIsFirst);
log.trace("LastAny: " + this.lastQualifyingIsAny);
log.trace("LastEnd: " + this.lastQualifyingIsLast);
log.trace("LastStart: " + this.lastQualifyingIsFirst);
}
}

}

+ 7
- 10
src/java/org/apache/fop/fo/flow/RetrieveMarker.java View File

@@ -34,11 +34,6 @@ import org.apache.fop.fo.PropertyList;
*/
public class RetrieveMarker extends AbstractRetrieveMarker {

// The value of properties relevant for fo:retrieve-marker.
private int retrievePosition;
private int retrieveBoundary;
// End of property values

/**
* Create a new RetrieveMarker instance that is a
* child of the given {@link FONode}.
@@ -70,8 +65,10 @@ public class RetrieveMarker extends AbstractRetrieveMarker {
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
this.retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum();
this.retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum();
setPosition(pList.get(PR_RETRIEVE_POSITION).getEnum());
setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION).getObject());
setBoundary(pList.get(PR_RETRIEVE_BOUNDARY).getEnum());
setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY).getObject());
}

/**
@@ -84,19 +81,19 @@ public class RetrieveMarker extends AbstractRetrieveMarker {
* {@link org.apache.fop.fo.Constants#EN_LEWP}.
*/
public int getRetrievePosition() {
return this.retrievePosition;
return getPosition();
}

/**
* Return the value for the <code>retrieve-boundary</code>
* property
* @return the value for retrieve-boundary-within-table; one of
* @return the value for retrieve-boundary; one of
* {@link org.apache.fop.fo.Constants#EN_PAGE},
* {@link org.apache.fop.fo.Constants#EN_PAGE_SEQUENCE},
* {@link org.apache.fop.fo.Constants#EN_DOCUMENT}.
*/
public int getRetrieveBoundary() {
return this.retrieveBoundary;
return getBoundary();
}

/** {@inheritDoc} */

+ 14
- 11
src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java View File

@@ -32,11 +32,6 @@ import org.apache.fop.fo.PropertyList;
*/
public class RetrieveTableMarker extends AbstractRetrieveMarker {

// The value of properties relevant for fo:retrieve-table-marker.
private int retrievePositionWithinTable;
private int retrieveBoundaryWithinTable;
// end property values

/**
* Create a new RetrieveTableMarker instance that is
* a child of the given {@link FONode}.
@@ -67,10 +62,10 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
this.retrievePositionWithinTable
= pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum();
this.retrieveBoundaryWithinTable
= pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum();
setPosition(pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum());
setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getObject());
setBoundary(pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum());
setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getObject());
}

/**
@@ -83,7 +78,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
* {@link org.apache.fop.fo.Constants#EN_LAST_ENDING}.
*/
public int getRetrievePositionWithinTable() {
return this.retrievePositionWithinTable;
return getPosition();
}

/**
@@ -95,7 +90,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
* {@link org.apache.fop.fo.Constants#EN_PAGE}.
*/
public int getRetrieveBoundaryWithinTable() {
return this.retrieveBoundaryWithinTable;
return getBoundary();
}

/** {@inheritDoc} */
@@ -110,4 +105,12 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
public int getNameId() {
return FO_RETRIEVE_TABLE_MARKER;
}

/** {@inheritDoc} */
public void clearChildNodes() {
super.clearChildNodes();
this.currentTextNode = null;
this.lastFOTextProcessed = null;
}

}

+ 9
- 0
src/java/org/apache/fop/fo/flow/table/TableCell.java View File

@@ -62,6 +62,8 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder {
/** used for FO validation */
private boolean blockItemFound = false;

private boolean hasRetrieveTableMarker;

/**
* Create a TableCell instance with the given {@link FONode}
* as parent.
@@ -247,4 +249,11 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder {
return FO_TABLE_CELL;
}

public void flagAsHavingRetrieveTableMarker() {
hasRetrieveTableMarker = true;
}

public boolean hasRetrieveTableMarker() {
return hasRetrieveTableMarker;
}
}

+ 2
- 0
src/java/org/apache/fop/fo/flow/table/TablePart.java View File

@@ -169,6 +169,8 @@ public abstract class TablePart extends TableCellContainer {
getUserAgent().getEventBroadcaster());
eventProducer.noMixRowsAndCells(this, getName(), getLocator());
}
} else if (localName.equals("retrieve-table-marker")) {
notSupportedChildError(loc, nsURI, localName);
} else {
invalidChildError(loc, nsURI, localName);
}

+ 7
- 0
src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java View File

@@ -273,4 +273,11 @@ public abstract class AbstractBaseLayoutManager
throw new UnsupportedOperationException("Not implemented");
}

public void preserveChildrenAtEndOfLayout() {

}

public void recreateChildrenLMs() {

}
}

+ 52
- 9
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java View File

@@ -37,6 +37,7 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.RetrieveMarker;
import org.apache.fop.layoutmgr.table.TableLayoutManager;

/**
* The base class for most LayoutManagers.
@@ -67,6 +68,8 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
private int lastGeneratedPosition = -1;
private int smallestPosNumberChecked = Integer.MAX_VALUE;

private boolean preserveChildrenAtEndOfLayout;

/**
* Abstract layout manager.
*/
@@ -370,19 +373,20 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
}

/**
* Registers the FO's markers on the current PageViewport
* Registers the FO's markers on the current PageViewport, and if applicable on the parent TableLM.
*
* @param isStarting boolean indicating whether the markers qualify as 'starting'
* @param isFirst boolean indicating whether the markers qualify as 'first'
* @param isLast boolean indicating whether the markers qualify as 'last'
*/
protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) {
protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) {
if (this.markers != null) {
getCurrentPV().addMarkers(
getCurrentPV().registerMarkers(
this.markers,
isStarting,
isFirst,
isLast);
possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
}
}

@@ -419,11 +423,12 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im

notifyEndOfLayout();

/* References to the child LMs are no longer needed
*/
childLMs = null;
curChildLM = null;
childLMiter = null;
if (!preserveChildrenAtEndOfLayout) {
// References to the child LMs are no longer needed
childLMs = null;
curChildLM = null;
childLMiter = null;
}

/* markers that qualify have been transferred to the page
*/
@@ -438,13 +443,21 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
|| lm instanceof PageSequenceLayoutManager)) {
lm = lm.getParent();
}
if (lm instanceof FlowLayoutManager) {
if (lm instanceof FlowLayoutManager && !preserveChildrenAtEndOfLayout) {
fobj.clearChildNodes();
fobjIter = null;
}
}
}

/*
* Preserves the children LMs at the end of layout. This is necessary if the layout is expected to be
* repeated, as when using retrieve-table-markers.
*/
public void preserveChildrenAtEndOfLayout() {
preserveChildrenAtEndOfLayout = true;
}

/** {@inheritDoc} */
@Override
public String toString() {
@@ -467,4 +480,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
lastGeneratedPosition = -1;
}

public void recreateChildrenLMs() {
childLMs = new ArrayList();
isFinished = false;
if (fobj == null) {
return;
}
fobjIter = fobj.getChildNodes();
int position = 0;
while (createNextChildLMs(position++)) {
//
}
childLMiter = new LMiter(this);
for (LMiter iter = new LMiter(this); iter.hasNext();) {
AbstractBaseLayoutManager alm = (AbstractBaseLayoutManager) iter.next();
alm.initialize();
alm.recreateChildrenLMs();
alm.preserveChildrenAtEndOfLayout();
}
curChildLM = getChildLM();
}

protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting,
boolean isFirst, boolean isLast) {
LayoutManager lm = this.parentLayoutManager;
if (lm instanceof FlowLayoutManager || lm instanceof PageSequenceLayoutManager
|| !(lm instanceof AbstractLayoutManager)) {
return;
}
((AbstractLayoutManager) lm).possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
}
}

+ 7
- 4
src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java View File

@@ -226,15 +226,14 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
String name = rm.getRetrieveClassName();
int pos = rm.getRetrievePosition();
int boundary = rm.getRetrieveBoundary();

// get marker from the current markers on area tree
Marker mark = (Marker)getCurrentPV().getMarker(name, pos);
Marker mark = getCurrentPV().resolveMarker(rm);
if (mark == null && boundary != EN_PAGE) {
// go back over pages until mark found
// if document boundary then keep going
boolean doc = boundary == EN_DOCUMENT;
boolean doc = (boundary == EN_DOCUMENT);
int seq = areaTreeModel.getPageSequenceCount();
int page = areaTreeModel.getPageCount(seq) - 1;
while (page < 0 && doc && seq > 1) {
@@ -243,7 +242,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
}
while (page >= 0) {
PageViewport pv = areaTreeModel.getPage(seq, page);
mark = (Marker)pv.getMarker(name, Constants.EN_LEWP);
int originalPosition = rm.getPosition();
rm.changePositionTo(Constants.EN_LEWP);
mark = (Marker) pv.resolveMarker(rm);
// this is probably not necessary since the RM will not be used again, but to be safe...
rm.changePositionTo(originalPosition);
if (mark != null) {
break;
}

+ 2
- 3
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java View File

@@ -87,7 +87,7 @@ public final class AreaAdditionUtil {
}

if (bslm != null) {
bslm.addMarkersToPage(
bslm.registerMarkers(
true,
bslm.isFirst(firstPos),
bslm.isLast(lastPos));
@@ -114,11 +114,10 @@ public final class AreaAdditionUtil {
}

if (bslm != null) {
bslm.addMarkersToPage(
bslm.registerMarkers(
false,
bslm.isFirst(firstPos),
bslm.isLast(lastPos));
bslm.checkEndOfLayout(lastPos);
}



+ 2
- 2
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java View File

@@ -806,7 +806,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl

addId();

addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
registerMarkers(true, isFirst(firstPos), isLast(lastPos));

if (bcpos == null) {
// the Positions in positionList were inside the elements
@@ -826,7 +826,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
bcpos.getBreaker().addContainedAreas(layoutContext);
}

addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
registerMarkers(false, isFirst(firstPos), isLast(lastPos));

TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(),
effSpaceBefore, effSpaceAfter);

+ 2
- 2
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java View File

@@ -312,7 +312,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co

addId();

addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
registerMarkers(true, isFirst(firstPos), isLast(lastPos));

// the Positions in positionList were inside the elements
// created by the LineLM
@@ -327,7 +327,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
childLM.addAreas(childPosIter, lc);
}

addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
registerMarkers(false, isFirst(firstPos), isLast(lastPos));

TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
effSpaceBefore, effSpaceAfter);

+ 20
- 1
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java View File

@@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.FOElementMapping;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FONode.FONodeIterator;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.extensions.ExternalDocument;
@@ -117,7 +118,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
registerMaker(Block.class, new BlockLayoutManagerMaker());
registerMaker(Leader.class, new LeaderLayoutManagerMaker());
registerMaker(RetrieveMarker.class, new RetrieveMarkerLayoutManagerMaker());
registerMaker(RetrieveTableMarker.class, new Maker());
registerMaker(RetrieveTableMarker.class, new RetrieveTableMarkerLayoutManagerMaker());
registerMaker(Character.class, new CharacterLayoutManagerMaker());
registerMaker(ExternalGraphic.class,
new ExternalGraphicLayoutManagerMaker());
@@ -407,6 +408,24 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
}
}

public class RetrieveTableMarkerLayoutManagerMaker extends Maker {
public void make(FONode node, List lms) {
FONodeIterator baseIter = node.getChildNodes();
if (baseIter == null) {
// this happens when the retrieve-table-marker cannot be resolved yet
RetrieveTableMarker rtm = (RetrieveTableMarker) node;
RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm);
lms.add(rtmlm);
return;
}
while (baseIter.hasNext()) {
// this happens when the retrieve-table-marker has been resolved
FONode child = (FONode) baseIter.next();
makeLayoutManagers(child, lms);
}
}
}

/** a layout manager maker */
public class WrapperLayoutManagerMaker extends Maker {
/** {@inheritDoc} */

+ 137
- 0
src/java/org/apache/fop/layoutmgr/LocalBreaker.java View File

@@ -0,0 +1,137 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.layoutmgr;

import java.util.LinkedList;
import java.util.List;

import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;

public abstract class LocalBreaker extends AbstractBreaker {
protected BlockStackingLayoutManager lm;
private int displayAlign;
private int ipd;
private int overflow = 0;

public LocalBreaker(BlockStackingLayoutManager lm, int ipd, int displayAlign) {
this.lm = lm;
this.ipd = ipd;
this.displayAlign = displayAlign;
}

/** {@inheritDoc} */
protected boolean isPartOverflowRecoveryActivated() {
// For side regions, this must be disabled because of wanted overflow.
return false;
}

public boolean isOverflow() {
return (this.overflow != 0);
}

public int getOverflowAmount() {
return this.overflow;
}

/** {@inheritDoc} */
protected PageBreakingLayoutListener createLayoutListener() {
return new PageBreakingLayoutListener() {

public void notifyOverflow(int part, int amount, FObj obj) {
if (LocalBreaker.this.overflow == 0) {
LocalBreaker.this.overflow = amount;
}
}

};
}

protected LayoutManager getTopLevelLM() {
return lm;
}

protected LayoutContext createLayoutContext() {
LayoutContext lc = super.createLayoutContext();
lc.setRefIPD(ipd);
return lc;
}

protected List getNextKnuthElements(LayoutContext context, int alignment) {
LayoutManager curLM; // currently active LM
List returnList = new LinkedList();

while ((curLM = lm.getChildLM()) != null) {
LayoutContext childLC = LayoutContext.newInstance();
childLC.setStackLimitBP(context.getStackLimitBP());
childLC.setRefIPD(context.getRefIPD());
childLC.setWritingMode(context.getWritingMode());

List returnedList = null;
// The following is a HACK! Ignore leading and trailing white space
boolean ignore = curLM instanceof TextLayoutManager;
if (!curLM.isFinished()) {
returnedList = curLM.getNextKnuthElements(childLC, alignment);
}
if (returnedList != null && !ignore) {
lm.wrapPositionElements(returnedList, returnList);
}
}
SpaceResolver.resolveElementList(returnList);
lm.setFinished(true);
return returnList;
}

protected int getCurrentDisplayAlign() {
return displayAlign;
}

protected boolean hasMoreContent() {
return !lm.isFinished();
}

protected void addAreas(PositionIterator posIter, LayoutContext context) {
AreaAdditionUtil.addAreas(lm, posIter, context);
}

protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList,
BlockSequence effectiveList) {
if (partCount > 1) {
PageBreakPosition pos = (PageBreakPosition) alg.getPageBreaks().getFirst();
int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
effectiveList.ignoreAtStart, pos.getLeafPos());
overflow += alg.totalWidth - firstPartLength;
}
// Rendering all parts (not just the first) at once for the case where the parts that
// overflow should be visible.
alg.removeAllPageBreaks();
// Directly add areas after finding the breaks
this.addAreas(alg, 1, originalList, effectiveList);
}

protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
// nop for static content
}

protected LayoutManager getCurrentChildLM() {
return null; // TODO NYI
}
}

+ 75
- 0
src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java View File

@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.layoutmgr;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager;
import org.apache.fop.layoutmgr.table.TableLayoutManager;

public class RetrieveTableMarkerLayoutManager extends LeafNodeLayoutManager {

private static Log log = LogFactory.getLog(RetrieveTableMarkerLayoutManager.class);

public RetrieveTableMarkerLayoutManager(RetrieveTableMarker node) {
super(node);
}

/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
setFinished(true);
FONode foNode = (FONode) getFObj();
foNode = getTableLayoutManager().resolveRetrieveTableMarker((RetrieveTableMarker) foNode);
if (foNode != null) {
// resolve the RTM and replace current LM by the resolved target LM
InlineLevelLayoutManager illm = (InlineLevelLayoutManager) getPSLM().getLayoutManagerMaker()
.makeLayoutManager(foNode);
if (illm instanceof RetrieveTableMarkerLayoutManager) {
// happens if the retrieve-marker was empty
return null;
}
illm.setParent(getParent());
illm.initialize();
return illm.getNextKnuthElements(context, alignment);
} else {
return null;
}
}

/** {@inheritDoc} */
public void addAreas(PositionIterator posIter, LayoutContext context) {
}

private TableLayoutManager getTableLayoutManager() {
LayoutManager parentLM = getParent();
while (!(parentLM instanceof TableLayoutManager)) {
parentLM = parentLM.getParent();
}
TableLayoutManager tlm = (TableLayoutManager) parentLM;
return tlm;
}

}

+ 5
- 108
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java View File

@@ -167,125 +167,22 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
return (StaticContent) fobj;
}

private class StaticContentBreaker extends AbstractBreaker {
private StaticContentLayoutManager lm;
private int displayAlign;
private int ipd;
private int overflow = 0;

public StaticContentBreaker(StaticContentLayoutManager lm, int ipd,
int displayAlign) {
this.lm = lm;
this.ipd = ipd;
this.displayAlign = displayAlign;
private class StaticContentBreaker extends LocalBreaker {

public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, int displayAlign) {
super(lm, ipd, displayAlign);
}

/** {@inheritDoc} */
protected void observeElementList(List elementList) {
String elementListID = getStaticContentFO().getFlowName();
String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId();
String pageSequenceID = ((PageSequence) lm.getParent().getFObj()).getId();
if (pageSequenceID != null && pageSequenceID.length() > 0) {
elementListID += "-" + pageSequenceID;
}
ElementListObserver.observe(elementList, "static-content", elementListID);
}

/** {@inheritDoc} */
protected boolean isPartOverflowRecoveryActivated() {
//For side regions, this must be disabled because of wanted overflow.
return false;
}

public boolean isOverflow() {
return (this.overflow != 0);
}

public int getOverflowAmount() {
return this.overflow;
}

/** {@inheritDoc} */
protected PageBreakingLayoutListener createLayoutListener() {
return new PageBreakingLayoutListener() {

public void notifyOverflow(int part, int amount, FObj obj) {
if (StaticContentBreaker.this.overflow == 0) {
StaticContentBreaker.this.overflow = amount;
}
}

};
}

protected LayoutManager getTopLevelLM() {
return lm;
}

protected LayoutContext createLayoutContext() {
LayoutContext lc = super.createLayoutContext();
lc.setRefIPD(ipd);
return lc;
}

protected List getNextKnuthElements(LayoutContext context, int alignment) {
LayoutManager curLM; // currently active LM
List returnList = new LinkedList();

while ((curLM = getChildLM()) != null) {
LayoutContext childLC = LayoutContext.newInstance();
childLC.setStackLimitBP(context.getStackLimitBP());
childLC.setRefIPD(context.getRefIPD());
childLC.setWritingMode(context.getWritingMode());

List returnedList = null;
//The following is a HACK! Ignore leading and trailing white space
boolean ignore = curLM instanceof TextLayoutManager;
if (!curLM.isFinished()) {
returnedList = curLM.getNextKnuthElements(childLC, alignment);
}
if (returnedList != null && !ignore) {
lm.wrapPositionElements(returnedList, returnList);
}
}
SpaceResolver.resolveElementList(returnList);
setFinished(true);
return returnList;
}

protected int getCurrentDisplayAlign() {
return displayAlign;
}

protected boolean hasMoreContent() {
return !lm.isFinished();
}

protected void addAreas(PositionIterator posIter, LayoutContext context) {
AreaAdditionUtil.addAreas(lm, posIter, context);
}

protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
if (partCount > 1) {
PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst();
int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
effectiveList.ignoreAtStart, pos.getLeafPos());
overflow += alg.totalWidth - firstPartLength;
}
//Rendering all parts (not just the first) at once for the case where the parts that
//overflow should be visible.
alg.removeAllPageBreaks();
//Directly add areas after finding the breaks
this.addAreas(alg, 1, originalList, effectiveList);
}

protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
//nop for static content
}

protected LayoutManager getCurrentChildLM() {
return null; //TODO NYI
}
}

/**

+ 2
- 2
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java View File

@@ -480,7 +480,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
}

addMarkersToPage(
registerMarkers(
true,
!areaCreated,
lastPos == null || isLast(lastPos));
@@ -542,7 +542,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
setTraits(areaCreated, lastPos == null || !isLast(lastPos));
parentLayoutManager.addChildArea(getCurrentArea());

addMarkersToPage(
registerMarkers(
false,
!areaCreated,
lastPos == null || isLast(lastPos));

+ 2
- 2
src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java View File

@@ -171,7 +171,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
}
}

addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
registerMarkers(true, isFirst(firstPos), isLast(lastPos));

PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
@@ -184,7 +184,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
childLM.addAreas(childPosIter, lc);
}

addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
registerMarkers(false, isFirst(firstPos), isLast(lastPos));

// We are done with this area add the background
TraitSetter.addBackground(curBlockArea,

+ 2
- 2
src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java View File

@@ -136,7 +136,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp
}
}

addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
registerMarkers(true, isFirst(firstPos), isLast(lastPos));

PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
@@ -149,7 +149,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp
childLM.addAreas(childPosIter, lc);
}

addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
registerMarkers(false, isFirst(firstPos), isLast(lastPos));

flush();


+ 2
- 2
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java View File

@@ -509,7 +509,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
}
}

addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
registerMarkers(true, isFirst(firstPos), isLast(lastPos));

// use the first and the last ListItemPosition to determine the
// corresponding indexes in the original labelList and bodyList
@@ -563,7 +563,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
}
curBlockArea.setBPD(itemBPD);

addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
registerMarkers(false, isFirst(firstPos), isLast(lastPos));

// We are done with this area add the background
TraitSetter.addBackground(curBlockArea,

+ 6
- 1
src/java/org/apache/fop/layoutmgr/table/RowPainter.java View File

@@ -303,11 +303,16 @@ class RowPainter {
borderAfterWhich = ConditionalBorder.REST;
}

// when adding the areas for the TableCellLayoutManager this helps with the isLast trait
// if, say, the first cell of a row has content that fits in the page, but the content of
// the second cell does not fit this will assure that the isLast trait for the first cell
// will also be false
lastCellParts[i].pgu.getCellLM().setLastTrait(lastCellParts[i].isLastPart());
addAreasForCell(firstCellParts[i].pgu,
firstCellParts[i].start, lastCellParts[i].end,
actualRowHeight, borderBeforeWhich, borderAfterWhich,
lastOnPage);
firstCellParts[i] = null;
firstCellParts[i] = null; // why? what about the lastCellParts[i]?
Arrays.fill(firstCellOnPage, i, i + currentGU.getCell().getNumberColumnsSpanned(),
false);
}

+ 185
- 2
src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java View File

@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -28,19 +29,24 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.Trait;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.table.ConditionalBorder;
import org.apache.fop.fo.flow.table.GridUnit;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.flow.table.TableFooter;
import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TablePart;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
import org.apache.fop.layoutmgr.AbstractLayoutManager;
import org.apache.fop.layoutmgr.AreaAdditionUtil;
import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.layoutmgr.ElementListUtils;
import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBox;
@@ -49,8 +55,10 @@ import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.LocalBreaker;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;
import org.apache.fop.layoutmgr.SpaceResolver;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.BorderProps;
@@ -79,6 +87,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
private int totalHeight;
private int usedBPD;
private boolean emptyCell = true;
private boolean isDescendantOfTableFooter;
private boolean isDescendantOfTableHeader;
private boolean hasRetrieveTableMarker;

// place holder for the addAreas arguments
private boolean savedAddAreasArguments;
private PositionIterator savedParentIter;
private LayoutContext savedLayoutContext;
private int[] savedSpannedGridRowHeights;
private int savedStartRow;
private int savedEndRow;
private int savedBorderBeforeWhich;
private int savedBorderAfterWhich;
private boolean savedFirstOnPage;
private boolean savedLastOnPage;
private RowPainter savedPainter;
private int savedFirstRowHeight;
// this is set to false when the table-cell has a retrieve-table-marker and is in the table-header
private boolean flushArea = true;

// this information is set by the RowPainter
private boolean isLastTrait;

/**
* Create a new Cell layout manager.
@@ -88,6 +118,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
super(node);
this.primaryGridUnit = pgu;
this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader
|| node.getParent() instanceof TableHeader;
this.isDescendantOfTableFooter = node.getParent().getParent() instanceof TableFooter
|| node.getParent() instanceof TableFooter;
this.hasRetrieveTableMarker = node.hasRetrieveTableMarker();
}

/** @return the table-cell FO */
@@ -248,6 +283,84 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
totalHeight = h;
}

private void clearRetrieveTableMarkerChildNodes(List<LayoutManager> childrenLMs) {
if (childrenLMs == null) {
return;
}
int n = childrenLMs.size();
for (int j = 0; j < n; j++) {
LayoutManager lm = (LayoutManager) childrenLMs.get(j);
if (lm == null) {
return;
} else if (lm instanceof RetrieveTableMarkerLayoutManager) {
((AbstractLayoutManager) lm).getFObj().clearChildNodes();
} else {
List<LayoutManager> lms = lm.getChildLMs();
clearRetrieveTableMarkerChildNodes(lms);
}
}
}

/**
* Checks whether the associated table cell of this LM is in a table header or footer.
* @return true if descendant of table header or footer
*/
private boolean isDescendantOfTableHeaderOrFooter() {
return (isDescendantOfTableFooter || isDescendantOfTableHeader);
}

private void saveAddAreasArguments(PositionIterator parentIter, LayoutContext layoutContext,
int[] spannedGridRowHeights, int startRow, int endRow, int borderBeforeWhich,
int borderAfterWhich, boolean firstOnPage, boolean lastOnPage, RowPainter painter,
int firstRowHeight) {
// checks for savedAddAreasArguments and isDescendantOfTableHeader were already made but repeat them
if (savedAddAreasArguments) {
return;
}
if (isDescendantOfTableHeader) {
savedAddAreasArguments = true;
savedParentIter = null /* parentIter */;
savedLayoutContext = null /* layoutContext */;
savedSpannedGridRowHeights = spannedGridRowHeights;
savedStartRow = startRow;
savedEndRow = endRow;
savedBorderBeforeWhich = borderBeforeWhich;
savedBorderAfterWhich = borderAfterWhich;
savedFirstOnPage = firstOnPage;
savedLastOnPage = lastOnPage;
savedPainter = painter;
savedFirstRowHeight = firstRowHeight;
TableLayoutManager parentTableLayoutManager = getTableLayoutManager();
parentTableLayoutManager.saveTableHeaderTableCellLayoutManagers(this);
// this saving is done the first time the addArea() is called; since the retrieve-table-markers
// cannot be resolved at this time we do not want to flush the area; the area needs nevertheless
// be built so that space is allocated for it.
flushArea = false;
}
}

private TableLayoutManager getTableLayoutManager() {
LayoutManager parentLM = getParent();
while (!(parentLM instanceof TableLayoutManager)) {
parentLM = parentLM.getParent();
}
TableLayoutManager tlm = (TableLayoutManager) parentLM;
return tlm;
}

/**
* Calls the addAreas() using the original arguments.
*/
protected void repeatAddAreas() {
if (savedAddAreasArguments) {
addAreas(savedParentIter, savedLayoutContext, savedSpannedGridRowHeights, savedStartRow,
savedEndRow, savedBorderBeforeWhich, savedBorderAfterWhich, savedFirstOnPage,
savedLastOnPage, savedPainter, savedFirstRowHeight);
// so that the arguments of the next table fragment header can be saved
savedAddAreasArguments = false;
}
}

/**
* Add the areas for the break points. The cell contains block stacking layout
* managers that add block areas.
@@ -407,7 +520,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
}
}

AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
if (isDescendantOfTableHeaderOrFooter()) {
if (hasRetrieveTableMarker) {
if (isDescendantOfTableHeader && !savedAddAreasArguments) {
saveAddAreasArguments(parentIter, layoutContext, spannedGridRowHeights, startRow, endRow,
borderBeforeWhich, borderAfterWhich, firstOnPage, lastOnPage, painter,
firstRowHeight);
}
recreateChildrenLMs();
int displayAlign = ((TableCell) this.getFObj()).getDisplayAlign();
TableCellBreaker breaker = new TableCellBreaker(this, cellIPD, displayAlign);
breaker.doLayout(usedBPD, false);
// this is needed so the next time the LMs are recreated they look like the originals; this
// is due to the fact that during the doLayout() above the FO tree changes when the
// retrieve-table-markers are resolved
clearRetrieveTableMarkerChildNodes(getChildLMs());
}
}

// if hasRetrieveTableMarker == true the areas were already added when the re-layout was done above
if (!hasRetrieveTableMarker) {
AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
}
// Re-adjust the cell's bpd as it may have been modified by the previous call
// for some reason (?)
curBlockArea.setBPD(cellBPD);
@@ -418,7 +552,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
getTableCell().getCommonBorderPaddingBackground(), this);
}

flush();
if (flushArea) {
flush();
} else {
flushArea = true;
}

curBlockArea = null;

@@ -604,4 +742,49 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
return true;
}

private class TableCellBreaker extends LocalBreaker {

public TableCellBreaker(TableCellLayoutManager lm, int ipd, int displayAlign) {
super(lm, ipd, displayAlign);
}

/**
* {@inheritDoc}
*/
protected void observeElementList(List elementList) {
String elementListID = lm.getParent().getFObj().getId() + "-" + lm.getFObj().getId();
ElementListObserver.observe(elementList, "table-cell", elementListID);
}

}

/**
* Registers the FO's markers on the current PageViewport and parent Table.
*
* @param isStarting boolean indicating whether the markers qualify as 'starting'
* @param isFirst boolean indicating whether the markers qualify as 'first'
* @param isLast boolean indicating whether the markers qualify as 'last'
*/
protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) {
Map<String, Marker> markers = getTableCell().getMarkers();
if (markers != null) {
getCurrentPV().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
if (!isDescendantOfTableHeaderOrFooter()) {
getTableLayoutManager().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
}
}
}

void setLastTrait(boolean isLast) {
isLastTrait = isLast;
}

/** {@inheritDoc} */
public void setParent(LayoutManager lm) {
this.parentLayoutManager = lm;
if (this.hasRetrieveTableMarker) {
this.getTableLayoutManager().flagAsHavingRetrieveTableMarker();
}
}

}

+ 21
- 5
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java View File

@@ -31,9 +31,11 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.table.EffRow;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableBody;
import org.apache.fop.fo.flow.table.TablePart;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.ElementListUtils;
@@ -400,9 +402,13 @@ public class TableContentLayoutManager implements PercentBaseContext {
}
}

Map markers = getTableLM().getTable().getMarkers();
// there may be table fragment markers stored; clear them since we are starting a new fragment
tableLM.clearTableFragmentMarkers();

// note: markers at table level are to be retrieved by the page, not by the table itself
Map<String, Marker> markers = getTableLM().getTable().getMarkers();
if (markers != null) {
getTableLM().getCurrentPV().addMarkers(markers,
getTableLM().getCurrentPV().registerMarkers(markers,
true, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
}

@@ -430,6 +436,10 @@ public class TableContentLayoutManager implements PercentBaseContext {
addBodyAreas(tablePositions.iterator(), painter, footerElements == null);
}

// if there are TCLMs saved because they have a RetrieveTableMarker, we repeat the header areas now;
// this can also be done after the areas for the footer are added but should be the same as here
tableLM.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();

if (footerElements != null) {
boolean ancestorTreatAsArtifact = layoutContext.treatAsArtifact();
layoutContext.setTreatAsArtifact(treatFooterAsArtifact);
@@ -442,7 +452,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
this.usedBPD += painter.getAccumulatedBPD();

if (markers != null) {
getTableLM().getCurrentPV().addMarkers(markers,
getTableLM().getCurrentPV().registerMarkers(markers,
false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
}
}
@@ -503,14 +513,20 @@ public class TableContentLayoutManager implements PercentBaseContext {
*/
private void addTablePartAreas(List positions, RowPainter painter, TablePart body,
boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) {
getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
getTableLM().getCurrentPV().registerMarkers(body.getMarkers(),
true, isFirstPos, isLastPos);
if (body instanceof TableBody) {
getTableLM().registerMarkers(body.getMarkers(), true, isFirstPos, isLastPos);
}
painter.startTablePart(body);
for (Iterator iter = positions.iterator(); iter.hasNext();) {
painter.handleTableContentPosition((TableContentPosition) iter.next());
}
getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
getTableLM().getCurrentPV().registerMarkers(body.getMarkers(),
false, isFirstPos, isLastPos);
if (body instanceof TableBody) {
getTableLM().registerMarkers(body.getMarkers(), false, isFirstPos, isLastPos);
}
painter.endTablePart(lastInBody, lastOnPage);
}


+ 121
- 0
src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java View File

@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,6 +34,9 @@ import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.Markers;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.properties.KeepProperty;
@@ -93,6 +97,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager

private Position auxiliaryPosition;

// this holds a possible list of TCLMs that needed to have their addAreas() repeated
private List<TableCellLayoutManager> savedTCLMs;
private boolean areAllTCLMsSaved;

private Markers tableMarkers;
private Markers tableFragmentMarkers;

private boolean hasRetrieveTableMarker;

/**
* Temporary holder of column background informations for a table-cell's area.
*
@@ -560,4 +573,112 @@ public class TableLayoutManager extends BlockStackingLayoutManager
tableUnit = 0.0;
}

/**
* Saves a TableCellLayoutManager for later use.
*
* @param tclm a TableCellLayoutManager that has a RetrieveTableMarker
*/
protected void saveTableHeaderTableCellLayoutManagers(TableCellLayoutManager tclm) {
if (savedTCLMs == null) {
savedTCLMs = new ArrayList<TableCellLayoutManager>();
}
if (!areAllTCLMsSaved) {
savedTCLMs.add(tclm);
}
}

/**
* Calls addAreas() for each of the saved TableCellLayoutManagers.
*/
protected void repeatAddAreasForSavedTableHeaderTableCellLayoutManagers() {
if (savedTCLMs == null) {
return;
}
// if we get to this stage then we are at the footer of the table fragment; this means that no more
// different TCLM need to be saved (we already have all); we flag the list as being complete then
areAllTCLMsSaved = true;
for (int i = 0; i < savedTCLMs.size(); i++) {
TableCellLayoutManager tclm = savedTCLMs.get(i);
tclm.repeatAddAreas();
}
}

/**
* Resolves a RetrieveTableMarker by finding a qualifying Marker to which it is bound to.
* @param rtm the RetrieveTableMarker to be resolved
* @return a bound RetrieveTableMarker instance or null if no qualifying Marker found
*/
public RetrieveTableMarker resolveRetrieveTableMarker(RetrieveTableMarker rtm) {
String name = rtm.getRetrieveClassName();
int originalPosition = rtm.getPosition();
boolean changedPosition = false;

Marker mark = null;
// try the primary retrieve scope area, which is the same as table-fragment
mark = (tableFragmentMarkers == null) ? null : tableFragmentMarkers.resolve(rtm);
if (mark == null && rtm.getBoundary() != Constants.EN_TABLE_FRAGMENT) {
rtm.changePositionTo(Constants.EN_LAST_ENDING);
changedPosition = true;
// try the page scope area
mark = getCurrentPV().resolveMarker(rtm);
if (mark == null && rtm.getBoundary() != Constants.EN_PAGE) {
// try the table scope area
mark = (tableMarkers == null) ? null : tableMarkers.resolve(rtm);
}
}
if (changedPosition) {
// so that the next time it is called looks unchanged
rtm.changePositionTo(originalPosition);
}
if (mark == null) {
log.debug("found no marker with name: " + name);
return null;
} else {
rtm.bindMarker(mark);
return rtm;
}
}

/**
* Register the markers for this table.
*
* @param marks the map of markers to add
* @param starting if the area being added is starting or ending
* @param isfirst if the area being added has is-first trait
* @param islast if the area being added has is-last trait
*/
public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst,
boolean islast) {
if (tableMarkers == null) {
tableMarkers = new Markers();
}
tableMarkers.register(marks, starting, isfirst, islast);
if (tableFragmentMarkers == null) {
tableFragmentMarkers = new Markers();
}
tableFragmentMarkers.register(marks, starting, isfirst, islast);
}

/**
* Clears the list of markers in the current table fragment. Should be called just before starting a new
* header (that belongs to the next table fragment).
*/
protected void clearTableFragmentMarkers() {
tableFragmentMarkers = null;
}

public void flagAsHavingRetrieveTableMarker() {
hasRetrieveTableMarker = true;
}

protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting,
boolean isFirst, boolean isLast) {
// note: if we allow table-footer after a table-body this check should not be made and the markers
// should be registered regardless because the retrieval may be done only in the footer
if (hasRetrieveTableMarker) {
registerMarkers(markers, isStarting, isFirst, isLast);
}
super.possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
}

}

+ 3
- 0
status.xml View File

@@ -62,6 +62,9 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
<action context="Layout" dev="MH" type="add" fixes-bug="53924" due-to="Luis Bernardo">
Support for retrieve-table-markers
</action>
<action context="Renderers" dev="VH" type="add" fixes-bug="53902">
Added possibility to define ‘header’ table columns (the same way as fo:table-header allows
to define header rows). When accessibility is enabled, this allows to set the appropriate

+ 128
- 0
test/java/org/apache/fop/fo/flow/MarkersTestCase.java View File

@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id:$ */

package org.apache.fop.fo.flow;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.Markers;
import org.apache.fop.fo.flow.RetrieveMarker;
import org.apache.fop.fo.flow.RetrieveTableMarker;

public class MarkersTestCase {

@Test
public void testRegisterAndResolve() {
// consider 3 regions, and a boundary; the first region starts before the boundary and ends inside
// the boundary, the second region is fully inside the boundary, and the third region starts inside
// the boundary and ends after the boundary. in every region there are 2 markers, A and B.
// ======== region 1
Map<String, Marker> markers_region_1 = new HashMap<String, Marker>();
Marker marker_1A = mock(Marker.class);
Marker marker_1B = mock(Marker.class);
markers_region_1.put("A", marker_1A);
markers_region_1.put("B", marker_1B);
// ======== region 2
Map<String, Marker> markers_region_2 = new HashMap<String, Marker>();
Marker marker_2A = mock(Marker.class);
Marker marker_2B = mock(Marker.class);
markers_region_2.put("A", marker_2A);
markers_region_2.put("B", marker_2B);
// ======== region 3
Map<String, Marker> markers_region_3 = new HashMap<String, Marker>();
Marker marker_3A = mock(Marker.class);
Marker marker_3B = mock(Marker.class);
markers_region_3.put("A", marker_3A);
markers_region_3.put("B", marker_3B);
// instantiate markers for the boundary
Markers markers = new Markers();
// register the markers for the different regions
// region 1
markers.register(markers_region_1, true, false, true);
markers.register(markers_region_1, false, false, true);
// region 2
markers.register(markers_region_2, true, true, true);
markers.register(markers_region_2, false, true, true);
// region 3
markers.register(markers_region_3, true, true, false);
markers.register(markers_region_3, false, true, false);
// now prepare a RetrieveMarker
RetrieveMarker rm = mock(RetrieveMarker.class);
when(rm.getRetrieveClassName()).thenReturn("A");
when(rm.getLocalName()).thenReturn("retrieve-marker");
when(rm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test
// and resolve the marker for different positions
// EN_FSWP
when(rm.getPosition()).thenReturn(Constants.EN_FSWP);
// expect marker_2A
assertEquals(marker_2A, markers.resolve(rm));
// EN_LSWP
when(rm.getPosition()).thenReturn(Constants.EN_LSWP);
// expect marker_3A
assertEquals(marker_3A, markers.resolve(rm));
// EN_LEWP
when(rm.getPosition()).thenReturn(Constants.EN_LEWP);
// expect marker_2A
assertEquals(marker_2A, markers.resolve(rm));
// EN_FIC
when(rm.getPosition()).thenReturn(Constants.EN_FIC);
// expect marker_1A
assertEquals(marker_1A, markers.resolve(rm));
// now prepare a RetrieveTableMarker
RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
when(rtm.getRetrieveClassName()).thenReturn("B");
when(rtm.getLocalName()).thenReturn("retrieve-table-marker");
when(rtm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test
// and resolve the marker for different positions
// EN_FIRST_STARTING
when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
// expect marker_2B
assertEquals(marker_2B, markers.resolve(rtm));
// EN_LAST_STARTING
when(rtm.getPosition()).thenReturn(Constants.EN_LAST_STARTING);
// expect marker_3B
assertEquals(marker_3B, markers.resolve(rtm));
// EN_LAST_ENDING
when(rtm.getPosition()).thenReturn(Constants.EN_LAST_ENDING);
// expect marker_2B
assertEquals(marker_2B, markers.resolve(rtm));
// EN_FIRST_INCLUDING_CARRYOVER
when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_INCLUDING_CARRYOVER);
// expect marker_1B
assertEquals(marker_1B, markers.resolve(rtm));
// test also an invalid position
when(rm.getPosition()).thenReturn(Constants.EN_ABSOLUTE);
try {
Marker m = markers.resolve(rm);
fail("Expected an exception... instead got:" + m.toString());
} catch (RuntimeException re) {
// do nothing
}
}
}

+ 2
- 1
test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java View File

@@ -19,6 +19,8 @@

package org.apache.fop.layoutmgr;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -31,7 +33,6 @@ import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Region;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.junit.Test;

public class PageSequenceLayoutManagerTestCase {


+ 56
- 0
test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.layoutmgr;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FObj.FObjIterator;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.layoutmgr.LayoutManagerMapping.RetrieveTableMarkerLayoutManagerMaker;

public class RetrieveTableMarkerLayoutManagerMakerTestCase {

@Test
public void testMake() throws FOPException {
// mock
FObjIterator foi = mock(FObjIterator.class);
when(foi.hasNext()).thenReturn(true).thenReturn(false);
// mock
RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
// real RTMLMM, not mock
List l = new ArrayList();
LayoutManagerMapping lmm = new LayoutManagerMapping();
RetrieveTableMarkerLayoutManagerMaker rtmlmm = lmm.new RetrieveTableMarkerLayoutManagerMaker();
// test the case rtm has no child nodes
when(rtm.getChildNodes()).thenReturn(null);
rtmlmm.make(rtm, l);
assertTrue(l.size() == 1);
assertTrue(l.get(0) instanceof RetrieveTableMarkerLayoutManager);
}

}

+ 82
- 0
test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java View File

@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.layoutmgr;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;
import org.apache.fop.layoutmgr.table.TableLayoutManager;

public class RetrieveTableMarkerLayoutManagerTestCase {

@Test
public void testGetNextKnuthElementsLayoutContextInt() {
LayoutContext lc = LayoutContext.newInstance();
// mock
Table t = mock(Table.class);
// mock
RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
when(rtm.getRetrieveClassName()).thenReturn("A");
when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT);
// mock
TextLayoutManager tlm = mock(TextLayoutManager.class);
// mock
LayoutManagerMapping lmm = mock(LayoutManagerMapping.class);
when(lmm.makeLayoutManager(rtm)).thenReturn(tlm);
// mock
PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
when(pslm.getPSLM()).thenReturn(pslm);
when(pslm.getLayoutManagerMaker()).thenReturn(lmm);
// mock
TableLayoutManager tablelm = mock(TableLayoutManager.class);
when(tablelm.getTable()).thenReturn(t);
// mock
BlockLayoutManager blm = mock(BlockLayoutManager.class);
when(blm.getPSLM()).thenReturn(pslm);
when(blm.getParent()).thenReturn(tablelm);
// real RTMLM, not mock
RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm);
rtmlm.setParent(blm);
// test the case where resolution returns null
when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(null);
assertNull(rtmlm.getNextKnuthElements(lc, 0));
// test the case where resolution returns non null
List l = new ArrayList();
when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(rtm);
when(tlm.getNextKnuthElements(lc, 0)).thenReturn(l);
assertEquals(l, rtmlm.getNextKnuthElements(lc, 0));
verify(tlm).setParent(blm);
verify(tlm).initialize();
}

}

+ 116
- 0
test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java View File

@@ -0,0 +1,116 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id:$ */

package org.apache.fop.layoutmgr.table;

import java.awt.Color;

import org.junit.Test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
import org.apache.fop.fo.properties.CondLengthProperty;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;

public class TableCellLayoutManagerTestCase {

// this test aims to check that the first call to addAreas() calls
// TLM.saveTableHeaderTableCellLayoutManagers() but the second call, through repeatAddAreas()
// does not call it again; there are a lot of mocks here, but just the necessary so that the
// addAreas() call can run to completion without NPE; also, the mocking needs to be done so
// the methods isDecendantOfTableHeaderOrFooter() and hasRetrieveTableMarker() return true.
@Test
public void testRepeatAddAreas() {
LayoutContext lc = LayoutContext.newInstance();
// mock background
CommonBorderPaddingBackground cbpb = mock(CommonBorderPaddingBackground.class);
// mock conditional length property
CondLengthProperty clp = mock(CondLengthProperty.class);
when(clp.getLengthValue()).thenReturn(0);
// real border info
BorderInfo bi = BorderInfo.getInstance(0, clp, Color.BLACK);
// mock column
TableColumn tcol = mock(TableColumn.class);
when(tcol.getCommonBorderPaddingBackground()).thenReturn(cbpb);
// mock table
Table t = mock(Table.class);
when(t.getColumn(0)).thenReturn(tcol);
// mock header
TableHeader th = mock(TableHeader.class);
when(th.getCommonBorderPaddingBackground()).thenReturn(cbpb);
// mock row
TableRow tr = mock(TableRow.class);
when(tr.getParent()).thenReturn(th);
// mock cell
TableCell tc = mock(TableCell.class);
when(tc.hasRetrieveTableMarker()).thenReturn(true);
when(tc.getTable()).thenReturn(t);
when(tc.getId()).thenReturn("cellId");
when(tc.getCommonBorderPaddingBackground()).thenReturn(cbpb);
when(tc.getParent()).thenReturn(tr);
// mock PGU
PrimaryGridUnit pgu = mock(PrimaryGridUnit.class);
when(pgu.getCell()).thenReturn(tc);
when(pgu.getColIndex()).thenReturn(0);
when(pgu.getBorderBefore(0)).thenReturn(bi);
when(pgu.getBorderAfter(0)).thenReturn(bi);
when(pgu.getBorderEnd()).thenReturn(bi);
when(pgu.getBorderStart()).thenReturn(bi);
when(pgu.getTablePart()).thenReturn(th);
// mock RTMLM
RetrieveTableMarkerLayoutManager rtmlm = mock(RetrieveTableMarkerLayoutManager.class);
when(rtmlm.isFinished()).thenReturn(true); // avoids infinite loop
// mock PSLM
PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
// mock TLM
TableLayoutManager tlm = mock(TableLayoutManager.class);
when(tlm.getPSLM()).thenReturn(pslm);
// mock PI
PositionIterator pi = mock(PositionIterator.class);
// mock RP
RowPainter rp = mock(RowPainter.class);

// real TCLM, not a mock!
TableCellLayoutManager tclm = new TableCellLayoutManager(tc, pgu);
tclm.addChildLM(rtmlm);
tclm.setParent(tlm);
// lets call addAreas
int[] n = {};
tclm.addAreas(pi, lc, n, 0, 0, 0, 0, true, true, rp, 0);
// check the TCLM is added to the TLM
verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm);
// call the repeat
tclm.repeatAddAreas();
// check the TCLM was not added again
verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm);
}
}

+ 64
- 0
test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java View File

@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.layoutmgr.table;

import org.junit.Test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.apache.fop.fo.FONode.FONodeIterator;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;

public class TableContentLayoutManagerTestCase {

@Test
public void testAddAreas() {
LayoutContext lc = LayoutContext.newInstance();
// mock
ColumnSetup cs = mock(ColumnSetup.class);
when(cs.getColumnCount()).thenReturn(3);
// mock
FONodeIterator foni = mock(FONodeIterator.class);
when(foni.hasNext()).thenReturn(false);
// mock
Table t = mock(Table.class);
when(t.getChildNodes()).thenReturn(foni);
when(t.getMarkers()).thenReturn(null);
// mock
TableLayoutManager tlm = mock(TableLayoutManager.class);
when(tlm.getTable()).thenReturn(t);
when(tlm.getColumns()).thenReturn(cs);
// mock
PositionIterator pi = mock(PositionIterator.class);
when(pi.hasNext()).thenReturn(false);
// real TCLM, not a mock
TableContentLayoutManager tclm = new TableContentLayoutManager(tlm);
// check that addAreas() calls the clearTableFragments() on the table and the
// repeatAddAreasForSavedTableHeaderTableCellLayoutManagers on the TLM
tclm.addAreas(pi, lc);
verify(tlm).clearTableFragmentMarkers();
verify(tlm).repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
}

}

+ 119
- 0
test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id:$ */

package org.apache.fop.layoutmgr.table;

import java.util.HashMap;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.layoutmgr.BlockLayoutManager;
import org.apache.fop.layoutmgr.Page;
import org.apache.fop.layoutmgr.PageSequenceLayoutManager;

public class TableLayoutManagerTestCase {

@Test
public void testSavedTableCellLayoutManagersFunctionality() {
Table t = mock(Table.class);
TableCellLayoutManager tclm1 = mock(TableCellLayoutManager.class);
TableLayoutManager tlm = new TableLayoutManager(t);
tlm.saveTableHeaderTableCellLayoutManagers(tclm1);
tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
verify(tclm1).repeatAddAreas(); // called once
// test that after the first repeatAddAreas() call the list closes to new additions
TableCellLayoutManager tclm2 = mock(TableCellLayoutManager.class);
tlm.saveTableHeaderTableCellLayoutManagers(tclm2);
tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers();
verify(tclm1, times(2)).repeatAddAreas(); // called twice
verify(tclm2, never()).repeatAddAreas(); // never called
}

@Test
public void testResolveRetrieveTableMarker() {
// mock
Table t = mock(Table.class);
// mock
Marker m = mock(Marker.class);
// mock
RetrieveTableMarker rtm = mock(RetrieveTableMarker.class);
when(rtm.getRetrieveClassName()).thenReturn("A");
when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING);
// mock
PageViewport pv = mock(PageViewport.class);
when(pv.resolveMarker(rtm)).thenReturn(m);
// mock
Page p = mock(Page.class);
when(p.getPageViewport()).thenReturn(pv);
// mock
PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class);
when(pslm.getPSLM()).thenReturn(pslm);
when(pslm.getCurrentPage()).thenReturn(p);
// mock
BlockLayoutManager blm = mock(BlockLayoutManager.class);
blm.setParent(pslm);
when(blm.getPSLM()).thenReturn(pslm);
// real TLM, not mock
TableLayoutManager tlm = new TableLayoutManager(t);
tlm.setParent(blm);
// register a marker
HashMap<String, Marker> markers1 = new HashMap<String, Marker>();
Marker m1 = mock(Marker.class);
markers1.put("A", m1);
tlm.registerMarkers(markers1, true, true, true);
tlm.registerMarkers(markers1, false, true, true);
// check that if there is a marker at table fragment level the RTM is returned
assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm));
verify(rtm, never()).getBoundary();
// check that if there is no marker at table fragment level and that is the boundary
// we get a null return value
when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT);
when(rtm.getRetrieveClassName()).thenReturn("B");
assertNull(tlm.resolveRetrieveTableMarker(rtm));
verify(rtm).getBoundary();
verify(rtm, never()).changePositionTo(Constants.EN_LAST_ENDING);
// check that if there is no marker at table fragment level and the boundary is page
// then we try to do the resolution at page level; test the case a marker is found
when(rtm.getBoundary()).thenReturn(Constants.EN_PAGE);
assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm));
verify(rtm).changePositionTo(Constants.EN_LAST_ENDING);
verify(rtm).changePositionTo(Constants.EN_FIRST_STARTING);
verify(pv).resolveMarker(rtm);
// test the same situation but in this case the marker is not found
when(pv.resolveMarker(rtm)).thenReturn(null);
assertNull(tlm.resolveRetrieveTableMarker(rtm));
// test the situation where the marker is not found at page level but the boundary is table
when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE);
assertNull(tlm.resolveRetrieveTableMarker(rtm));
}

}

+ 144
- 0
test/layoutengine/standard-testcases/markers_10.xml View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- $Id$ -->
<testcase>
<info>
<p>
This test checks markers on broken tables.
</p>
</info>
<fo>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
<fo:layout-master-set>
<fo:simple-page-master master-name="normal" page-width="5in" page-height="2in">
<fo:region-body margin="0.5in 0"/>
<fo:region-before extent="0.5in"/>
<fo:region-after extent="0.5in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="normal" white-space-collapse="true">
<fo:static-content flow-name="xsl-region-before">
<fo:block background-color="yellow">
f-i-c: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="first-including-carryover"/>
</fo:block>
<fo:block background-color="yellow">
f-s-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="first-starting-within-page"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="end" background-color="yellow">
l-s-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="last-starting-within-page"/>
</fo:block>
<fo:block text-align="end" background-color="yellow">
l-e-w-p: <fo:retrieve-marker retrieve-class-name="test" retrieve-boundary="page" retrieve-position="last-ending-within-page"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:table color="black" table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:marker marker-class-name="test">row1</fo:marker>
<fo:block>row1</fo:block>
<fo:block>row1</fo:block>
<fo:block>row1</fo:block>
<fo:block>row1</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:marker marker-class-name="test">row1</fo:marker>
<fo:block>row1</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:marker marker-class-name="test">row2</fo:marker>
<fo:block>row2</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:marker marker-class-name="test">row2</fo:marker>
<fo:block>row2</fo:block>
<fo:block>row2</fo:block>
<fo:block>row2</fo:block>
<fo:block>row2</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:marker marker-class-name="test">row3</fo:marker>
<fo:block>row3</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:marker marker-class-name="test">row3</fo:marker>
<fo:block>row3</fo:block>
<fo:block>row3</fo:block>
<fo:block>row3</fo:block>
<fo:block>row3</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:marker marker-class-name="test">row4</fo:marker>
<fo:block>row4</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:marker marker-class-name="test">row5</fo:marker>
<fo:block>row5</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:marker marker-class-name="test">row5</fo:marker>
<fo:block>row5</fo:block>
<fo:block>row5</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell column-number="2">
<fo:marker marker-class-name="test">row6</fo:marker>
<fo:block>row6</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
<true xpath="starts-with(//pageViewport[@nr=1]//regionBefore/block[1],'f-i-c: row1')"/>
<true xpath="starts-with(//pageViewport[@nr=1]//regionBefore/block[2],'f-s-w-p: row1')"/>
<true xpath="starts-with(//pageViewport[@nr=1]//regionAfter/block[1],'l-s-w-p: row2')"/>
<true xpath="starts-with(//pageViewport[@nr=1]//regionAfter/block[2],'l-e-w-p: row1')"/>

<true xpath="starts-with(//pageViewport[@nr=2]//regionBefore/block[1],'f-i-c: row2')"/>
<true xpath="starts-with(//pageViewport[@nr=2]//regionBefore/block[2],'f-s-w-p: row3')"/>
<true xpath="starts-with(//pageViewport[@nr=2]//regionAfter/block[1],'l-s-w-p: row3')"/>
<true xpath="starts-with(//pageViewport[@nr=2]//regionAfter/block[2],'l-e-w-p: row2')"/>

<true xpath="starts-with(//pageViewport[@nr=3]//regionBefore/block[1],'f-i-c: row3')"/>
<true xpath="starts-with(//pageViewport[@nr=3]//regionBefore/block[2],'f-s-w-p: row4')"/>
<true xpath="starts-with(//pageViewport[@nr=3]//regionAfter/block[1],'l-s-w-p: row5')"/>
<true xpath="starts-with(//pageViewport[@nr=3]//regionAfter/block[2],'l-e-w-p: row5')"/>

<true xpath="starts-with(//pageViewport[@nr=4]//regionBefore/block[1],'f-i-c: row6')"/>
<true xpath="starts-with(//pageViewport[@nr=4]//regionBefore/block[2],'f-s-w-p: row6')"/>
<true xpath="starts-with(//pageViewport[@nr=4]//regionAfter/block[1],'l-s-w-p: row6')"/>
<true xpath="starts-with(//pageViewport[@nr=4]//regionAfter/block[2],'l-e-w-p: row6')"/>
</checks>
</testcase>

+ 720
- 0
test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml View File

@@ -0,0 +1,720 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- $Id$ -->
<testcase>
<info>
<p>This test checks the retrieve-table-marker implementation. Rather, it serves as a regression test.</p>
</info>
<fo>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="10pt" font-family="sans-serif"
line-height="120%">
<fo:layout-master-set>
<fo:simple-page-master master-name="base-page" page-height="210mm" page-width="297mm">
<fo:region-body margin="15mm" column-count="3" />
<fo:region-before extent="15mm" />
<fo:region-after extent="11.4mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="base-page">
<fo:flow flow-name="xsl-region-body" font-family="sans-serif">
<fo:block>The table has two columns, State and Facts. In the rows corresponding to states in the
Pacific Time Zone (PST), the State and Facts table
cells have a marker with the value of the State. That marker is retrieved in the table header and
footer. All the possible boundary and positions are considered in this example.
</fo:block>
<fo:block> rbwt = retrieve-boundary-within-table; rpwt = retrieve-position-within-table;
f-i-c = first-including-carryover; f-s = first-starting; l-s = last-starting; l-e = last-ending;
t = table; p = page; p-s = page-sequence; t-f = table-fragment.
</fo:block>
<fo:wrapper>
<fo:table space-before="1em" width="100%" table-layout="fixed">
<fo:table-column column-width="proportional-column-width(3)" />
<fo:table-column column-width="proportional-column-width(5)" />
<fo:table-header>
<fo:table-row>
<fo:table-cell padding="4pt" number-columns-spanned="2"
background-color="yellow">
<fo:block>
[rbwt:t][rpwt:f-i-c](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="table" />
)
</fo:block>
<fo:block>
[rbwt:t][rpwt:f-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-starting" retrieve-boundary-within-table="table" />
)
</fo:block>
<fo:block>
[rbwt:p][rpwt:f-i-c](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="page" />
)
</fo:block>
<fo:block>
[rbwt:p][rpwt:f-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-starting" retrieve-boundary-within-table="page" />
)
</fo:block>
<fo:block>
[rbwt:t-f][rpwt:f-i-c](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-including-carryover" retrieve-boundary-within-table="table-fragment" />
)
</fo:block>
<fo:block>
[rbwt:t-f][rpwt:f-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="first-starting" retrieve-boundary-within-table="table-fragment" />
)
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block font-weight="bold">State</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block font-weight="bold">Facts</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-footer>
<fo:table-row>
<fo:table-cell padding="4pt" number-columns-spanned="2"
background-color="yellow">
<fo:block>
[rbwt:t][rpwt:l-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-starting" retrieve-boundary-within-table="table" />
)
</fo:block>
<fo:block>
[rbwt:t][rpwt:l-e](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-ending" retrieve-boundary-within-table="table" />
)
</fo:block>
<fo:block>
[rbwt:p][rpwt:l-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-starting" retrieve-boundary-within-table="page" />
)
</fo:block>
<fo:block>
[rbwt:p][rpwt:l-e](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-ending" retrieve-boundary-within-table="page" />
)
</fo:block>
<fo:block>
[rbwt:t-f][rpwt:l-s](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-starting" retrieve-boundary-within-table="table-fragment" />
)
</fo:block>
<fo:block>
[rbwt:t-f][rpwt:l-e](
<fo:retrieve-table-marker retrieve-class-name="state"
retrieve-position-within-table="last-ending" retrieve-boundary-within-table="table-fragment" />
)
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-footer>
<fo:table-body>
<!--fo:marker marker-class-name="state">Alabama</fo:marker-->
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Alabama</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Montgomery</fo:block>
<fo:block>Area: 52,423 square miles</fo:block>
<fo:block>Population: 4,447,100</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Alaska</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Juneau</fo:block>
<fo:block>Area: 656,425 square miles</fo:block>
<fo:block>Population: 626,932</fo:block>
<fo:block>Time Zone: GMT -9:00</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Arizona</fo:marker>
<fo:block>Arizona</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Arizona</fo:marker>
<fo:block>Capital: Phoenix</fo:block>
<fo:block>Area: 114,006 square miles</fo:block>
<fo:block>Population: 5,130,632</fo:block>
<fo:block>Time Zone: PST and MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Arkansas</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Little Rock</fo:block>
<fo:block>Area: 53,182 square miles</fo:block>
<fo:block>Population: 2,673,400</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">California</fo:marker>
<fo:block>California</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">California</fo:marker>
<fo:block>Capital: Sacramento</fo:block>
<fo:block>Area: 163,707 square miles</fo:block>
<fo:block>Population: 33,871,648</fo:block>
<fo:block>Time Zone: PST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Colorado</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Denver</fo:block>
<fo:block>Area: 104,100 square miles</fo:block>
<fo:block>Population: 4,301,261</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Connecticut</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Hartford</fo:block>
<fo:block>Area: 5,544 square miles</fo:block>
<fo:block>Population: 3,405,565</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Delaware</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Dover</fo:block>
<fo:block>Area: 1,954 square miles</fo:block>
<fo:block>Population: 783,600</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Florida</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Tallahassee</fo:block>
<fo:block>Area: 65,758 square miles</fo:block>
<fo:block>Population: 15,982,378</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Georgia</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Atlanta</fo:block>
<fo:block>Area: 59,441 square miles</fo:block>
<fo:block>Population: 8,186,453</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Hawaii</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Honolulu</fo:block>
<fo:block>Area: 10,932 square miles</fo:block>
<fo:block>Population: 1,211,537</fo:block>
<fo:block>Time Zone: GMT -11:00</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Idaho</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Boise</fo:block>
<fo:block>Area: 83,574 square miles</fo:block>
<fo:block>Population: 1,293,953</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Illinois</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Springfield</fo:block>
<fo:block>Area: 57,918 square miles</fo:block>
<fo:block>Population: 12,419,293</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Indiana</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Indianapolis</fo:block>
<fo:block>Area: 36,420 square miles</fo:block>
<fo:block>Population: 6,080,485</fo:block>
<fo:block>Time Zone: EST and CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Iowa</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Des Moines</fo:block>
<fo:block>Area: 56,276 square miles</fo:block>
<fo:block>Population: 2,926,324</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Kansas</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Topeka</fo:block>
<fo:block>Area: 82,282 square miles</fo:block>
<fo:block>Population: 2,688,418</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Kentucky</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Frankfort</fo:block>
<fo:block>Area: 40,411 square miles</fo:block>
<fo:block>Population: 4,041,769</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Louisiana</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Baton Rouge</fo:block>
<fo:block>Area: 51,843 square miles</fo:block>
<fo:block>Population: 4,468,976</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Maine</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Augusta</fo:block>
<fo:block>Area: 35,387 square miles</fo:block>
<fo:block>Population: 1,274,923</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Maryland</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Annapolis</fo:block>
<fo:block>Area: 12,407 square miles</fo:block>
<fo:block>Population: 5,296,486</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Massachusetts</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Boston</fo:block>
<fo:block>Area: 10,555 square miles</fo:block>
<fo:block>Population: 6,349,097</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Michigan</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Lansing</fo:block>
<fo:block>Area: 96,810 square miles</fo:block>
<fo:block>Population: 9,938,444</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Minnesota</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: St. Paul</fo:block>
<fo:block>Area: 86,943 square miles</fo:block>
<fo:block>Population: 4,919,479</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Mississippi</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Jackson</fo:block>
<fo:block>Area: 48,434 square miles</fo:block>
<fo:block>Population: 2,844,658</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Missouri</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Jefferson City</fo:block>
<fo:block>Area: 69,709 square miles</fo:block>
<fo:block>Population: 5,595,211</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Montana</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Helena</fo:block>
<fo:block>Area: 147,046 square miles</fo:block>
<fo:block>Population: 902,195</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Nebraska</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Lincoln</fo:block>
<fo:block>Area: XXXXXX square miles</fo:block>
<fo:block>Population: 77,358</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Nevada</fo:marker>
<fo:block>Nevada</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Nevada</fo:marker>
<fo:block>Capital: Carson City</fo:block>
<fo:block>Area: 110,567 square miles</fo:block>
<fo:block>Population: 1,998,257</fo:block>
<fo:block>Time Zone: PST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>New Hampshire</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Concord</fo:block>
<fo:block>Area: 9,351 square miles</fo:block>
<fo:block>Population: 1,235,786</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>New Jersey</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Trenton</fo:block>
<fo:block>Area: 8,722 square miles</fo:block>
<fo:block>Population: 8,414,350</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>New Mexico</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Santa Fe</fo:block>
<fo:block>Area: 121,593 square miles</fo:block>
<fo:block>Population: 1,819,046</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>New York</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Albany</fo:block>
<fo:block>Area: 54,475 square miles</fo:block>
<fo:block>Population: 18,976,457</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>North Carolina</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Raleigh</fo:block>
<fo:block>Area: 53,821 square miles</fo:block>
<fo:block>Population: 8,049,313</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>North Dakota</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Bismarck</fo:block>
<fo:block>Area: 70,704 square miles</fo:block>
<fo:block>Population: 642,200</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Ohio</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Columbus</fo:block>
<fo:block>Area: 44,828 square miles</fo:block>
<fo:block>Population: 11,353,140</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Oklahoma</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Oklahoma City</fo:block>
<fo:block>Area: 69,903 square miles</fo:block>
<fo:block>Population: 3,450,654</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Oregon</fo:marker>
<fo:block>Oregon</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Oregon</fo:marker>
<fo:block>Capital: Salem</fo:block>
<fo:block>Area: 98,386 square miles</fo:block>
<fo:block>Population: 3,421,399</fo:block>
<fo:block>Time Zone: PST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Pennsylvania</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Harrisburg</fo:block>
<fo:block>Area: 46,058 square miles</fo:block>
<fo:block>Population: 12,281,054</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Rhode Island</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Providence</fo:block>
<fo:block>Area: 1,045 square miles</fo:block>
<fo:block>Population: 1,048,319</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>South Carolina</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Columbia</fo:block>
<fo:block>Area: 32,007 square miles</fo:block>
<fo:block>Population: 4,012,012</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>South Dakota</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Pierre</fo:block>
<fo:block>Area: 77,121 square miles</fo:block>
<fo:block>Population: 754,844</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Tennessee</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Nashville</fo:block>
<fo:block>Area: 42,146 square miles</fo:block>
<fo:block>Population: 5,689,283</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Texas</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Austin</fo:block>
<fo:block>Area: 268,601 square miles</fo:block>
<fo:block>Population: 20,851,820</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Utah</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Salt Lake City</fo:block>
<fo:block>Area: 84,904 square miles</fo:block>
<fo:block>Population: 2,233,169</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Vermont</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Montpelier</fo:block>
<fo:block>Area: 9,615 square miles</fo:block>
<fo:block>Population: 608,827</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Virginia</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Richmond</fo:block>
<fo:block>Area: 42,769 square miles</fo:block>
<fo:block>Population: 7,078,515</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Washington</fo:marker>
<fo:block>Washington</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:marker marker-class-name="state">Washington</fo:marker>
<fo:block>Capital: Olympia</fo:block>
<fo:block>Area: 71,303 square miles</fo:block>
<fo:block>Population: 5,894,121</fo:block>
<fo:block>Time Zone: PST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>West Virginia</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Charleston</fo:block>
<fo:block>Area: 24,231 square miles</fo:block>
<fo:block>Population: 1,808,344</fo:block>
<fo:block>Time Zone: EST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Wisconsin</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Madison</fo:block>
<fo:block>Area: 65,503 square miles</fo:block>
<fo:block>Population: 5,363,675</fo:block>
<fo:block>Time Zone: CST</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Wyoming</fo:block>
</fo:table-cell>
<fo:table-cell padding-before="4pt" padding-after="4pt">
<fo:block>Capital: Cheyenne</fo:block>
<fo:block>Area: 97,818 square miles</fo:block>
<fo:block>Population: 493,782</fo:block>
<fo:block>Time Zone: MST</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:wrapper>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
<eval expected="1" xpath="//lineArea[starts-with(., '[rbwt:t-f][rpwt:f-i-c]( )')]/ancestor::pageViewport/@nr" />
<eval expected="2" xpath="//lineArea[starts-with(., '[rbwt:t][rpwt:f-i-c]( Nevada )')]/ancestor::pageViewport/@nr" />
<eval expected="2" xpath="//lineArea[starts-with(., '[rbwt:p][rpwt:f-i-c]( Nevada )')]/ancestor::pageViewport/@nr" />
<eval expected="3" xpath="//lineArea[starts-with(., '[rbwt:t][rpwt:f-i-c]( Oregon )')]/ancestor::pageViewport/@nr" />
<eval expected="[rbwt:t][rpwt:f-i-c]( California )" xpath="//pageViewport[2]/page/regionViewport[3]//flow[1]/block[1]/block[17]/block[1]/lineArea" />
<eval expected="[rbwt:t][rpwt:f-i-c]( Oregon )" xpath="//pageViewport[3]/page/regionViewport[3]//flow[1]/block[1]/block[15]/block[1]/lineArea" />
<eval expected="[rbwt:t][rpwt:f-i-c]( Washington )" xpath="//pageViewport[4]/page/regionViewport[3]//flow[1]/block[1]/block[5]/block[1]/lineArea" />
</checks>
</testcase>

Loading…
Cancel
Save