git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@429168 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_93
@@ -40,9 +40,9 @@ import org.apache.fop.fo.flow.ListBlock; | |||
import org.apache.fop.fo.flow.ListItem; | |||
import org.apache.fop.fo.flow.PageNumber; | |||
import org.apache.fop.fo.flow.Table; | |||
import org.apache.fop.fo.flow.TableColumn; | |||
import org.apache.fop.fo.flow.TableBody; | |||
import org.apache.fop.fo.flow.TableCell; | |||
import org.apache.fop.fo.flow.TableColumn; | |||
import org.apache.fop.fo.flow.TableRow; | |||
import org.apache.fop.fo.pagination.Flow; | |||
import org.apache.fop.fo.pagination.PageSequence; | |||
@@ -78,7 +78,7 @@ public abstract class FOEventHandler { | |||
*/ | |||
private Set idReferences = new HashSet(); | |||
/* | |||
/** | |||
* The property list maker. | |||
*/ | |||
protected PropertyListMaker propertyListMaker; | |||
@@ -88,6 +88,11 @@ public abstract class FOEventHandler { | |||
*/ | |||
protected XMLWhiteSpaceHandler whiteSpaceHandler = new XMLWhiteSpaceHandler(); | |||
/** | |||
* Indicates whether processing descendants of a marker | |||
*/ | |||
private boolean inMarker = false; | |||
/** | |||
* Main constructor | |||
* @param foUserAgent the apps.FOUserAgent instance for this process | |||
@@ -143,6 +148,23 @@ public abstract class FOEventHandler { | |||
return whiteSpaceHandler; | |||
} | |||
/** | |||
* Switch to or from marker context | |||
* (used by FOTreeBuilder when processing | |||
* a marker) | |||
* | |||
*/ | |||
protected void switchMarkerContext(boolean inMarker) { | |||
this.inMarker = inMarker; | |||
} | |||
/** | |||
* Check whether in marker context | |||
*/ | |||
protected boolean inMarker() { | |||
return this.inMarker; | |||
} | |||
/** | |||
* This method is called to indicate the start of a new document run. | |||
* @throws SAXException In case of a problem | |||
@@ -185,9 +207,10 @@ public abstract class FOEventHandler { | |||
} | |||
/** | |||
* This method is called to indicate the start of a new fo:flow or fo:static-content. | |||
* This method also handles fo:static-content tags, because the StaticContent class | |||
* is derived from the Flow class. | |||
* This method is called to indicate the start of a new fo:flow | |||
* or fo:static-content. | |||
* This method also handles fo:static-content tags, because the | |||
* StaticContent class is derived from the Flow class. | |||
* | |||
* @param fl Flow that is starting. | |||
*/ | |||
@@ -219,15 +242,15 @@ public abstract class FOEventHandler { | |||
* | |||
* @param blc BlockContainer that is starting. | |||
*/ | |||
public void startBlockContainer(BlockContainer blc) { | |||
} | |||
public void startBlockContainer(BlockContainer blc) { | |||
} | |||
/** | |||
/** | |||
* | |||
* @param blc BlockContainer that is ending. | |||
*/ | |||
public void endBlockContainer(BlockContainer blc) { | |||
} | |||
public void endBlockContainer(BlockContainer blc) { | |||
} | |||
/** | |||
* |
@@ -81,7 +81,6 @@ public abstract class FONode implements Cloneable { | |||
throws FOPException { | |||
FONode foNode = (FONode) clone(); | |||
foNode.parent = cloneparent; | |||
cloneparent.addChildNode(foNode); | |||
return foNode; | |||
} | |||
@@ -124,6 +123,10 @@ public abstract class FONode implements Cloneable { | |||
public FOEventHandler getFOEventHandler() { | |||
return parent.getFOEventHandler(); | |||
} | |||
protected boolean inMarker() { | |||
return getFOEventHandler().inMarker(); | |||
} | |||
/** | |||
* Returns the user agent for the node. | |||
@@ -257,7 +260,7 @@ public abstract class FONode implements Cloneable { | |||
/** | |||
* Return an iterator over the object's child nodes starting | |||
* at the pased node. | |||
* at the passed node. | |||
* @param childNode First node in the iterator | |||
* @return A ListIterator or null if child node isn't a child of | |||
* this FObj. | |||
@@ -580,5 +583,35 @@ public abstract class FONode implements Cloneable { | |||
return null; | |||
} | |||
/** | |||
* @return true if markers are valid children | |||
*/ | |||
protected boolean canHaveMarkers() { | |||
int foId = getNameId(); | |||
switch (foId) { | |||
case Constants.FO_BASIC_LINK: | |||
case Constants.FO_BIDI_OVERRIDE: | |||
case Constants.FO_BLOCK: | |||
case Constants.FO_BLOCK_CONTAINER: | |||
case Constants.FO_FLOW: | |||
case Constants.FO_INLINE: | |||
case Constants.FO_INLINE_CONTAINER: | |||
case Constants.FO_LIST_BLOCK: | |||
case Constants.FO_LIST_ITEM: | |||
case Constants.FO_LIST_ITEM_BODY: | |||
case Constants.FO_LIST_ITEM_LABEL: | |||
case Constants.FO_TABLE: | |||
case Constants.FO_TABLE_BODY: | |||
case Constants.FO_TABLE_HEADER: | |||
case Constants.FO_TABLE_FOOTER: | |||
case Constants.FO_TABLE_CELL: | |||
case Constants.FO_TABLE_AND_CAPTION: | |||
case Constants.FO_TABLE_CAPTION: | |||
case Constants.FO_WRAPPER: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
} | |||
@@ -30,7 +30,7 @@ import org.apache.fop.fo.properties.BorderWidthPropertyMaker; | |||
import org.apache.fop.fo.properties.BoxPropShorthandParser; | |||
import org.apache.fop.fo.properties.CharacterProperty; | |||
import org.apache.fop.fo.properties.ColorProperty; | |||
import org.apache.fop.fo.properties.ColumnNumberPropertyMaker; | |||
import org.apache.fop.fo.flow.TableFObj.ColumnNumberPropertyMaker; | |||
import org.apache.fop.fo.properties.CondLengthProperty; | |||
import org.apache.fop.fo.properties.CorrespondingPropertyMaker; | |||
import org.apache.fop.fo.properties.DimensionPropertyMaker; | |||
@@ -326,11 +326,13 @@ public final class FOPropertyMapping implements Constants { | |||
* @return a propId that matches the property name. | |||
*/ | |||
public static int getPropertyId(String name) { | |||
Integer i = (Integer) s_htPropNames.get(name); | |||
if (i == null) { | |||
return -1; | |||
if (name != null) { | |||
Integer i = (Integer) s_htPropNames.get(name); | |||
if (i != null) { | |||
return i.intValue(); | |||
} | |||
} | |||
return i.intValue(); | |||
return -1; | |||
} | |||
/** | |||
@@ -339,11 +341,13 @@ public final class FOPropertyMapping implements Constants { | |||
* @return a subpropId that matches the subproperty name. | |||
*/ | |||
public static int getSubPropertyId(String name) { | |||
Integer i = (Integer) s_htSubPropNames.get(name); | |||
if (i == null) { | |||
return -1; | |||
if (name != null) { | |||
Integer i = (Integer) s_htSubPropNames.get(name); | |||
if (i != null) { | |||
return i.intValue(); | |||
} | |||
} | |||
return i.intValue(); | |||
return -1; | |||
} | |||
// returns a property, compound, or property.compound name | |||
@@ -2263,8 +2267,8 @@ public final class FOPropertyMapping implements Constants { | |||
sub = new LengthProperty.Maker(CP_INLINE_PROGRESSION_DIRECTION); | |||
sub.setDefault("0pt"); | |||
m.addSubpropMaker(sub); | |||
sub.setByShorthand(true); | |||
m.addSubpropMaker(sub); | |||
addPropertyMaker("border-separation", m); | |||
// border-start-precedence |
@@ -150,6 +150,23 @@ public class FOText extends FONode { | |||
this.ca = nca; | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#clone(FONode, boolean) | |||
*/ | |||
public FONode clone(FONode parent, boolean removeChildren) | |||
throws FOPException { | |||
FOText ft = (FOText) super.clone(parent, removeChildren); | |||
if (removeChildren) { | |||
//not really removing, but just make sure the char array | |||
//pointed to is really a different one | |||
if (ca != null) { | |||
ft.ca = new char[ca.length]; | |||
System.arraycopy(ca, 0, ft.ca, 0, ca.length); | |||
} | |||
} | |||
return ft; | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FObj#bind(PropertyList) | |||
*/ | |||
@@ -171,8 +188,8 @@ public class FOText extends FONode { | |||
/** @see org.apache.fop.fo.FONode#endOfNode() */ | |||
protected void endOfNode() throws FOPException { | |||
createBlockPointers(); | |||
textTransform(); | |||
getFOEventHandler().characters(ca, startIndex, endIndex); | |||
} | |||
/** | |||
@@ -210,28 +227,11 @@ public class FOText extends FONode { | |||
} | |||
/** | |||
* This method is run as part of the Constructor, to create xref pointers to | |||
* This method is run as part of the ancestor Block's flushText(), to create xref pointers to | |||
* the previous FOText objects within the same Block | |||
*/ | |||
private void createBlockPointers() { | |||
// build pointers between the FOText objects withing the same Block | |||
// | |||
// find the ancestorBlock of the current node | |||
FONode ancestorFONode = this; | |||
while (this.ancestorBlock == null) { | |||
ancestorFONode = ancestorFONode.parent; | |||
if (ancestorFONode instanceof org.apache.fop.fo.pagination.Title) { | |||
return; | |||
} else if (ancestorFONode instanceof org.apache.fop.fo.flow.Marker) { | |||
return; | |||
} else if (ancestorFONode instanceof Root) { | |||
getLogger().warn("Unexpected: fo:text with no fo:block ancestor. The text is: " | |||
+ new String(ca)); | |||
return; | |||
} else if (ancestorFONode instanceof Block) { | |||
this.ancestorBlock = (Block)ancestorFONode; | |||
} | |||
} | |||
protected void createBlockPointers(Block ancestorBlock) { | |||
this.ancestorBlock = ancestorBlock; | |||
// if the last FOText is a sibling, point to it, and have it point here | |||
if (lastFOTextProcessed != null) { | |||
if (lastFOTextProcessed.ancestorBlock == this.ancestorBlock) { | |||
@@ -252,7 +252,8 @@ public class FOText extends FONode { | |||
* text-transform property. | |||
*/ | |||
private void textTransform() { | |||
if (textTransform == Constants.EN_NONE) { | |||
if (getFOEventHandler().inMarker() | |||
|| textTransform == Constants.EN_NONE) { | |||
return; | |||
} | |||
for (int i = 0; i < endIndex; i++) { |
@@ -246,7 +246,7 @@ public class FOTreeBuilder extends DefaultHandler { | |||
*/ | |||
public FormattingResults getResults() { | |||
if (getEventHandler() instanceof AreaTreeHandler) { | |||
return ((AreaTreeHandler)getEventHandler()).getResults(); | |||
return ((AreaTreeHandler) getEventHandler()).getResults(); | |||
} else { | |||
//No formatting results available for output formats no | |||
//involving the layout engine. | |||
@@ -268,6 +268,11 @@ public class FOTreeBuilder extends DefaultHandler { | |||
* Current propertyList for the node being handled. | |||
*/ | |||
protected PropertyList currentPropertyList; | |||
/** | |||
* Current marker nesting-depth | |||
*/ | |||
private int nestedMarkerDepth = 0; | |||
/** | |||
* SAX Handler for the start of an element | |||
@@ -278,16 +283,17 @@ public class FOTreeBuilder extends DefaultHandler { | |||
/* the node found in the FO document */ | |||
FONode foNode; | |||
PropertyList propertyList; | |||
PropertyList propertyList = null; | |||
// Check to ensure first node encountered is an fo:root | |||
if (rootFObj == null) { | |||
if (!namespaceURI.equals(FOElementMapping.URI) | |||
|| !localName.equals("root")) { | |||
throw new SAXException(new ValidationException( | |||
"Error: First element must be the fo:root formatting object. Found " | |||
+ FONode.getNodeString(namespaceURI, localName) + " instead." | |||
+ " Please make sure you're producing a valid XSL-FO document.")); | |||
throw new ValidationException( | |||
"Error: First element must be the fo:root formatting object. " | |||
+ "Found " + FONode.getNodeString(namespaceURI, localName) | |||
+ " instead." | |||
+ " Please make sure you're producing a valid XSL-FO document."); | |||
} | |||
} else { // check that incoming node is valid for currentFObj | |||
if (namespaceURI.equals(FOElementMapping.URI)) { | |||
@@ -301,7 +307,8 @@ public class FOTreeBuilder extends DefaultHandler { | |||
} | |||
} | |||
ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName); | |||
ElementMapping.Maker fobjMaker = | |||
findFOMaker(namespaceURI, localName); | |||
try { | |||
foNode = fobjMaker.make(currentFObj); | |||
@@ -309,8 +316,17 @@ public class FOTreeBuilder extends DefaultHandler { | |||
rootFObj = (Root) foNode; | |||
rootFObj.setFOEventHandler(foEventHandler); | |||
} | |||
propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler); | |||
foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList); | |||
propertyList = foNode.createPropertyList( | |||
currentPropertyList, foEventHandler); | |||
foNode.processNode(localName, getEffectiveLocator(), | |||
attlist, propertyList); | |||
if (foNode.getNameId() == Constants.FO_MARKER) { | |||
if (foEventHandler.inMarker()) { | |||
nestedMarkerDepth++; | |||
} else { | |||
foEventHandler.switchMarkerContext(true); | |||
} | |||
} | |||
foNode.startOfNode(); | |||
} catch (IllegalArgumentException e) { | |||
throw new SAXException(e); | |||
@@ -321,11 +337,13 @@ public class FOTreeBuilder extends DefaultHandler { | |||
ContentHandler subHandler = chFactory.createContentHandler(); | |||
if (subHandler instanceof ObjectSource | |||
&& foNode instanceof ObjectBuiltListener) { | |||
((ObjectSource)subHandler).setObjectBuiltListener((ObjectBuiltListener)foNode); | |||
((ObjectSource) subHandler).setObjectBuiltListener( | |||
(ObjectBuiltListener) foNode); | |||
} | |||
subHandler.startDocument(); | |||
subHandler.startElement(namespaceURI, localName, rawName, attlist); | |||
subHandler.startElement(namespaceURI, localName, | |||
rawName, attlist); | |||
depth = 1; | |||
delegate = subHandler; | |||
} | |||
@@ -335,7 +353,7 @@ public class FOTreeBuilder extends DefaultHandler { | |||
} | |||
currentFObj = foNode; | |||
if (propertyList != null) { | |||
if (propertyList != null && !foEventHandler.inMarker()) { | |||
currentPropertyList = propertyList; | |||
} | |||
} | |||
@@ -356,11 +374,24 @@ public class FOTreeBuilder extends DefaultHandler { | |||
+ " (" + currentFObj.getNamespaceURI() | |||
+ ") vs. " + localName + " (" + uri + ")"); | |||
} | |||
currentFObj.endOfNode(); | |||
if (currentPropertyList.getFObj() == currentFObj) { | |||
currentPropertyList = currentPropertyList.getParentPropertyList(); | |||
if (currentPropertyList != null | |||
&& currentPropertyList.getFObj() == currentFObj | |||
&& !foEventHandler.inMarker()) { | |||
currentPropertyList = | |||
currentPropertyList.getParentPropertyList(); | |||
} | |||
if (currentFObj.getNameId() == Constants.FO_MARKER) { | |||
if (nestedMarkerDepth == 0) { | |||
foEventHandler.switchMarkerContext(false); | |||
} else { | |||
nestedMarkerDepth--; | |||
} | |||
} | |||
if (currentFObj.getParent() == null) { | |||
log.debug("endElement for top-level " + currentFObj.getName()); | |||
} | |||
@@ -373,19 +404,15 @@ public class FOTreeBuilder extends DefaultHandler { | |||
*/ | |||
public void characters(char[] data, int start, int length) | |||
throws FOPException { | |||
if (currentFObj != null) { | |||
currentFObj.addCharacters(data, start, start + length, | |||
currentPropertyList, getEffectiveLocator()); | |||
} | |||
if (currentFObj != null) { | |||
currentFObj.addCharacters(data, start, start + length, | |||
currentPropertyList, getEffectiveLocator()); | |||
} | |||
} | |||
public void endDocument() throws SAXException { | |||
currentFObj = null; | |||
} | |||
} | |||
} | |||
} | |||
@@ -20,7 +20,6 @@ | |||
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; | |||
@@ -119,11 +118,15 @@ public abstract class FObj extends FONode implements Constants { | |||
* @see org.apache.fop.fo.FONode#processNode | |||
*/ | |||
public void processNode(String elementName, Locator locator, | |||
Attributes attlist, PropertyList pList) throws FOPException { | |||
Attributes attlist, PropertyList pList) | |||
throws FOPException { | |||
setLocator(locator); | |||
pList.addAttributesToList(attlist); | |||
pList.setWritingMode(); | |||
bind(pList); | |||
if (!inMarker() | |||
|| "marker".equals(elementName)) { | |||
pList.setWritingMode(); | |||
bind(pList); | |||
} | |||
} | |||
/** | |||
@@ -154,7 +157,7 @@ public abstract class FObj extends FONode implements Constants { | |||
* @throws ValidationException if the ID is already defined elsewhere | |||
*/ | |||
protected void checkId(String id) throws ValidationException { | |||
if (!id.equals("")) { | |||
if (!inMarker() && !id.equals("")) { | |||
Set idrefs = getFOEventHandler().getIDReferences(); | |||
if (!idrefs.contains(id)) { | |||
idrefs.add(id); | |||
@@ -178,13 +181,15 @@ public abstract class FObj extends FONode implements Constants { | |||
* @see org.apache.fop.fo.FONode#addChildNode(FONode) | |||
*/ | |||
protected void addChildNode(FONode child) throws FOPException { | |||
if (PropertySets.canHaveMarkers(getNameId()) && child.getNameId() == FO_MARKER) { | |||
addMarker((Marker)child); | |||
if (canHaveMarkers() && child.getNameId() == FO_MARKER) { | |||
addMarker((Marker) child); | |||
} else { | |||
ExtensionAttachment attachment = child.getExtensionAttachment(); | |||
if (attachment != null) { | |||
//This removes the element from the normal children, so no layout manager | |||
//is being created for them as they are only additional information. | |||
/* This removes the element from the normal children, | |||
* so no layout manager is being created for them | |||
* as they are only additional information. | |||
*/ | |||
addExtensionAttachment(attachment); | |||
} else { | |||
if (childNodes == null) { | |||
@@ -195,6 +200,10 @@ public abstract class FObj extends FONode implements Constants { | |||
} | |||
} | |||
protected static void addChildTo(FONode child, FObj parent) throws FOPException { | |||
parent.addChildNode(child); | |||
} | |||
/** @see org.apache.fop.fo.FONode#removeChild(org.apache.fop.fo.FONode) */ | |||
public void removeChild(FONode child) { | |||
if (childNodes != null) { | |||
@@ -250,6 +259,20 @@ public abstract class FObj extends FONode implements Constants { | |||
return null; | |||
} | |||
/** | |||
* Return a FONode based on the index in the list of childNodes. | |||
* @param nodeIndex index of the node to return | |||
* @return the node or null if the index is invalid | |||
*/ | |||
public FONode getChildNodeAt(int nodeIndex) { | |||
if (childNodes != null) { | |||
if (nodeIndex >= 0 && nodeIndex < childNodes.size()) { | |||
return (FONode) childNodes.get(nodeIndex); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Notifies a FObj that one of it's children is removed. | |||
* This method is subclassed by Block to clear the firstInlineChild variable. |
@@ -42,7 +42,7 @@ public abstract class FObjMixed extends FObj { | |||
protected FObjMixed(FONode parent) { | |||
super(parent); | |||
} | |||
/** @see org.apache.fop.fo.FONode */ | |||
protected void addCharacters(char[] data, int start, int end, | |||
PropertyList pList, | |||
@@ -50,7 +50,9 @@ public abstract class FObjMixed extends FObj { | |||
if (ft == null) { | |||
ft = new FOText(this); | |||
ft.setLocator(locator); | |||
ft.bind(pList); | |||
if (!inMarker()) { | |||
ft.bind(pList); | |||
} | |||
} | |||
ft.addCharacters(data, start, end, null, null); | |||
} | |||
@@ -58,11 +60,26 @@ public abstract class FObjMixed extends FObj { | |||
/** @see org.apache.fop.fo.FONode#endOfNode() */ | |||
protected void endOfNode() throws FOPException { | |||
flushText(); | |||
getFOEventHandler().whiteSpaceHandler | |||
.handleWhiteSpace(this, currentTextNode); | |||
if (!inMarker() | |||
|| getNameId() == FO_MARKER) { | |||
getFOEventHandler().whiteSpaceHandler | |||
.handleWhiteSpace(this, currentTextNode); | |||
} | |||
super.endOfNode(); | |||
} | |||
/** | |||
* Handles white-space for the node that is passed in, | |||
* starting at its current text-node | |||
* (used by RetrieveMarker to trigger 'end-of-node' white-space | |||
* handling) | |||
* @param fobj the node for which to handle white-space | |||
*/ | |||
protected static void handleWhiteSpaceFor(FObjMixed fobj) { | |||
fobj.getFOEventHandler().getXMLWhiteSpaceHandler() | |||
.handleWhiteSpace(fobj, fobj.currentTextNode); | |||
} | |||
/** | |||
* Adds accumulated text as one FOText instance. | |||
* Makes sure that nested calls to itself do nothing. | |||
@@ -72,8 +89,29 @@ public abstract class FObjMixed extends FObj { | |||
if (ft != null) { | |||
FOText lft = ft; | |||
ft = null; | |||
if (getNameId() == FO_BLOCK) { | |||
lft.createBlockPointers((org.apache.fop.fo.flow.Block) this); | |||
} else if (getNameId() != FO_MARKER | |||
&& getNameId() != FO_TITLE | |||
&& getNameId() != FO_BOOKMARK_TITLE) { | |||
FONode fo = parent; | |||
int foNameId = fo.getNameId(); | |||
while (foNameId != FO_BLOCK | |||
&& foNameId != FO_MARKER | |||
&& foNameId != FO_TITLE | |||
&& foNameId != FO_BOOKMARK_TITLE | |||
&& foNameId != FO_PAGE_SEQUENCE) { | |||
fo = fo.getParent(); | |||
foNameId = fo.getNameId(); | |||
} | |||
if (foNameId == FO_BLOCK) { | |||
lft.createBlockPointers((org.apache.fop.fo.flow.Block) fo); | |||
} else if (foNameId == FO_PAGE_SEQUENCE) { | |||
log.error("Could not create block pointers." | |||
+ " FOText w/o Block ancestor."); | |||
} | |||
} | |||
lft.endOfNode(); | |||
getFOEventHandler().characters(lft.ca, lft.startIndex, lft.endIndex); | |||
addChildNode(lft); | |||
} | |||
} | |||
@@ -83,15 +121,18 @@ public abstract class FObjMixed extends FObj { | |||
*/ | |||
protected void addChildNode(FONode child) throws FOPException { | |||
flushText(); | |||
if (child instanceof FOText || child.getNameId() == FO_CHARACTER) { | |||
if (currentTextNode == null) { | |||
currentTextNode = child; | |||
if (!inMarker() | |||
|| getNameId() == FO_MARKER) { | |||
if (child instanceof FOText || child.getNameId() == FO_CHARACTER) { | |||
if (currentTextNode == null) { | |||
currentTextNode = child; | |||
} | |||
} else { | |||
// handle white-space for all text up to here | |||
getFOEventHandler().whiteSpaceHandler | |||
.handleWhiteSpace(this, currentTextNode, child); | |||
currentTextNode = null; | |||
} | |||
} else { | |||
// handle white-space for all text up to here | |||
getFOEventHandler().whiteSpaceHandler | |||
.handleWhiteSpace(this, currentTextNode, child); | |||
currentTextNode = null; | |||
} | |||
super.addChildNode(child); | |||
} |
@@ -169,8 +169,11 @@ public abstract class PropertyList { | |||
boolean bTryDefault) throws PropertyException { | |||
PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK); | |||
return propertyMaker.get(propId & Constants.COMPOUND_MASK, this, | |||
bTryInherit, bTryDefault); | |||
if (propertyMaker != null) { | |||
return propertyMaker.get(propId & Constants.COMPOUND_MASK, this, | |||
bTryInherit, bTryDefault); | |||
} | |||
return null; | |||
} | |||
/** | |||
@@ -260,8 +263,11 @@ public abstract class PropertyList { | |||
* Adds the attributes, passed in by the parser to the PropertyList | |||
* | |||
* @param attributes Collection of attributes passed to us from the parser. | |||
* @throws ValidationException if there is an attribute that does not | |||
* map to a property id (strict validation only) | |||
*/ | |||
public void addAttributesToList(Attributes attributes) { | |||
public void addAttributesToList(Attributes attributes) | |||
throws ValidationException { | |||
/* | |||
* If column-number/number-columns-spanned are specified, then we | |||
* need them before all others (possible from-table-column() on any | |||
@@ -308,69 +314,104 @@ public abstract class PropertyList { | |||
if (factory.getElementMappingRegistry().isKnownNamespace(attributeNS)) { | |||
getFObj().addForeignAttribute(attributeNS, attributeName, attributeValue); | |||
} else { | |||
handleInvalidProperty(attributeName); | |||
handleInvalidProperty( | |||
"Error processing foreign attribute: " | |||
+ attributeNS + "/@" + attributeName, attributeName); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Validates a property name. | |||
* @param propertyName the property name to check | |||
* @return true if the base property name and the subproperty name (if any) | |||
* can be correctly mapped to an id | |||
* @throws ValidationException in case the property name | |||
* is invalid for the FO namespace | |||
*/ | |||
protected boolean isValidPropertyName(String propertyName) | |||
throws ValidationException { | |||
int propId = FOPropertyMapping.getPropertyId( | |||
findBasePropertyName(propertyName)); | |||
int subpropId = FOPropertyMapping.getSubPropertyId( | |||
findSubPropertyName(propertyName)); | |||
if (propId == -1 | |||
|| (subpropId == -1 | |||
&& findSubPropertyName(propertyName) != null)) { | |||
StringBuffer errorMessage = new StringBuffer().append( | |||
"Invalid property name \'").append(propertyName); | |||
handleInvalidProperty(errorMessage.toString(), propertyName); | |||
return false; | |||
} | |||
return true; | |||
} | |||
/** | |||
* | |||
* @param attributes Collection of attributes | |||
* @param attributeName Attribute name to convert | |||
* @param attributeValue Attribute value to assign to property | |||
* @throws ValidationException in case the property name is invalid | |||
* for the FO namespace | |||
*/ | |||
private void convertAttributeToProperty(Attributes attributes, | |||
String attributeName, | |||
String attributeValue) { | |||
PropertyMaker propertyMaker = null; | |||
FObj parentFO = fobj.findNearestAncestorFObj(); | |||
String attributeValue) | |||
throws ValidationException { | |||
/* Handle "compound" properties, ex. space-before.minimum */ | |||
String basePropertyName = findBasePropertyName(attributeName); | |||
String subPropertyName = findSubPropertyName(attributeName); | |||
if (attributeValue != null) { | |||
if (!isValidPropertyName(attributeName)) { | |||
//will log an error or throw an exception | |||
return; | |||
} | |||
FObj parentFO = fobj.findNearestAncestorFObj(); | |||
/* Handle "compound" properties, ex. space-before.minimum */ | |||
String basePropertyName = findBasePropertyName(attributeName); | |||
String subPropertyName = findSubPropertyName(attributeName); | |||
int propId = FOPropertyMapping.getPropertyId(basePropertyName); | |||
int propId = FOPropertyMapping.getPropertyId(basePropertyName); | |||
int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName); | |||
PropertyMaker propertyMaker = findMaker(propId); | |||
if (propertyMaker == null) { | |||
log.warn("No PropertyMaker registered for " + attributeName | |||
+ ". Ignoring property."); | |||
return; | |||
} | |||
propertyMaker = findMaker(propId); | |||
if (propertyMaker == null) { | |||
handleInvalidProperty(attributeName); | |||
return; | |||
} | |||
if (attributeValue == null) { | |||
return; | |||
} | |||
try { | |||
Property prop = null; | |||
if (subPropertyName == null) { // base attribute only found | |||
/* Do nothing if the base property has already been created. | |||
* This is e.g. the case when a compound attribute was | |||
* specified before the base attribute; in these cases | |||
* the base attribute was already created in | |||
* findBaseProperty() | |||
*/ | |||
if (getExplicit(propId) != null) { | |||
return; | |||
try { | |||
Property prop = null; | |||
if (subPropertyName == null) { // base attribute only found | |||
/* Do nothing if the base property has already been created. | |||
* This is e.g. the case when a compound attribute was | |||
* specified before the base attribute; in these cases | |||
* the base attribute was already created in | |||
* findBaseProperty() | |||
*/ | |||
if (getExplicit(propId) != null) { | |||
return; | |||
} | |||
prop = propertyMaker.make(this, attributeValue, parentFO); | |||
} else { // e.g. "leader-length.maximum" | |||
Property baseProperty = | |||
findBaseProperty(attributes, parentFO, propId, | |||
basePropertyName, propertyMaker); | |||
prop = propertyMaker.make(baseProperty, subpropId, | |||
this, attributeValue, parentFO); | |||
} | |||
prop = propertyMaker.make(this, attributeValue, parentFO); | |||
} else { // e.g. "leader-length.maximum" | |||
Property baseProperty = findBaseProperty(attributes, | |||
parentFO, propId, basePropertyName, propertyMaker); | |||
int subpropertyId = FOPropertyMapping.getSubPropertyId(subPropertyName); | |||
if (subpropertyId == -1) { | |||
handleInvalidProperty(attributeName); | |||
return; | |||
if (prop != null) { | |||
putExplicit(propId, prop); | |||
} | |||
prop = propertyMaker.make(baseProperty, subpropertyId, | |||
this, attributeValue, parentFO); | |||
} | |||
if (prop != null) { | |||
putExplicit(propId, prop); | |||
} catch (PropertyException e) { | |||
log.error("Ignoring property: " | |||
+ attributeName + "=\"" + attributeValue + "\""); | |||
} | |||
} catch (PropertyException e) { | |||
// TODO: Add strict validation. | |||
log.error(e.getMessage()); | |||
} | |||
} | |||
@@ -405,9 +446,19 @@ public abstract class PropertyList { | |||
return null; // could not find base property | |||
} | |||
private void handleInvalidProperty(String attributeName) { | |||
if (!attributeName.startsWith("xmlns")) { | |||
log.error("property '" + attributeName + "' ignored"); | |||
/** | |||
* @param message ... | |||
* @param propName ... | |||
* @throws ValidationException ... | |||
*/ | |||
protected void handleInvalidProperty(String message, String propName) | |||
throws ValidationException { | |||
if (!propName.startsWith("xmlns")) { | |||
if (fobj.getUserAgent().validateStrictly()) { | |||
fobj.attributeError(message); | |||
} else { | |||
log.error(message + " Property ignored."); | |||
} | |||
} | |||
} | |||
@@ -418,7 +469,7 @@ public abstract class PropertyList { | |||
* @param attributeName String to be atomized | |||
* @return the base portion of the attribute | |||
*/ | |||
private static String findBasePropertyName(String attributeName) { | |||
protected static String findBasePropertyName(String attributeName) { | |||
int separatorCharIndex = attributeName.indexOf('.'); | |||
String basePropertyName = attributeName; | |||
if (separatorCharIndex > -1) { | |||
@@ -434,7 +485,7 @@ public abstract class PropertyList { | |||
* @param attributeName String to be atomized | |||
* @return the sub portion of the attribute | |||
*/ | |||
private static String findSubPropertyName(String attributeName) { | |||
protected static String findSubPropertyName(String attributeName) { | |||
int separatorCharIndex = attributeName.indexOf('.'); | |||
String subpropertyName = null; | |||
if (separatorCharIndex > -1) { |
@@ -21,7 +21,7 @@ import org.apache.fop.fo.properties.Property; | |||
/** | |||
* A very fast implementation of PropertyList that uses arrays to store | |||
* the explit set properties and another array to store cached values. | |||
* the explicit set properties and another array to store cached values. | |||
*/ | |||
public class StaticPropertyList extends PropertyList { | |||
private Property[] explicit; |
@@ -72,11 +72,34 @@ public class XMLWhiteSpaceHandler { | |||
* @param firstTextNode the node at which to start | |||
*/ | |||
public void handleWhiteSpace(FObjMixed fo, FONode firstTextNode) { | |||
if (fo.getNameId() == Constants.FO_BLOCK) { | |||
this.currentBlock = (Block) fo; | |||
this.linefeedTreatment = currentBlock.getLinefeedTreatment(); | |||
this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse(); | |||
this.whiteSpaceTreatment = currentBlock.getWhitespaceTreatment(); | |||
if (fo.getNameId() == Constants.FO_BLOCK | |||
|| fo.getNameId() == Constants.FO_RETRIEVE_MARKER) { | |||
if (fo.getNameId() == Constants.FO_BLOCK) { | |||
this.currentBlock = (Block) fo; | |||
} else { | |||
FONode ancestor = fo.parent; | |||
while (ancestor.getNameId() != Constants.FO_BLOCK | |||
&& ancestor.getNameId() != Constants.FO_STATIC_CONTENT) { | |||
ancestor = ancestor.getParent(); | |||
} | |||
if (ancestor.getNameId() == Constants.FO_BLOCK) { | |||
this.currentBlock = (Block) ancestor; | |||
} | |||
} | |||
if (currentBlock != null) { | |||
this.linefeedTreatment = currentBlock.getLinefeedTreatment(); | |||
this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse(); | |||
this.whiteSpaceTreatment = | |||
currentBlock.getWhitespaceTreatment(); | |||
} else { | |||
/* fo:retrieve-marker as direct child of static-content | |||
* set properties to their initial values | |||
*/ | |||
this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE; | |||
this.whiteSpaceCollapse = Constants.EN_TRUE; | |||
this.whiteSpaceTreatment = | |||
Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; | |||
} | |||
} else if (fo.getNameId() == Constants.FO_TITLE | |||
|| fo.getNameId() == Constants.FO_BOOKMARK_TITLE) { | |||
/* Two special types of FO that can contain #PCDATA | |||
@@ -84,7 +107,8 @@ public class XMLWhiteSpaceHandler { | |||
*/ | |||
this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE; | |||
this.whiteSpaceCollapse = Constants.EN_TRUE; | |||
this.whiteSpaceTreatment = Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; | |||
this.whiteSpaceTreatment = | |||
Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; | |||
} | |||
currentFO = fo; | |||
if (firstTextNode == null) { | |||
@@ -94,7 +118,10 @@ public class XMLWhiteSpaceHandler { | |||
charIter = new RecursiveCharIterator(fo, firstTextNode); | |||
inWhiteSpace = false; | |||
int textNodeIndex = -1; | |||
if (currentFO == currentBlock) { | |||
if (currentFO == currentBlock | |||
|| currentBlock == null | |||
|| (currentFO.getNameId() == Constants.FO_RETRIEVE_MARKER | |||
&& currentFO.getParent() == currentBlock)) { | |||
textNodeIndex = fo.childNodes.indexOf(firstTextNode); | |||
afterLinefeed = ((textNodeIndex == 0) | |||
|| (textNodeIndex > 0 | |||
@@ -104,7 +131,8 @@ public class XMLWhiteSpaceHandler { | |||
endOfBlock = (nextChild == null && currentFO == currentBlock); | |||
if (nextChild != null) { | |||
int nextChildId = nextChild.getNameId(); | |||
nextChildIsBlockLevel = (nextChildId == Constants.FO_BLOCK | |||
nextChildIsBlockLevel = ( | |||
nextChildId == Constants.FO_BLOCK | |||
|| nextChildId == Constants.FO_TABLE_AND_CAPTION | |||
|| nextChildId == Constants.FO_TABLE | |||
|| nextChildId == Constants.FO_LIST_BLOCK | |||
@@ -149,6 +177,11 @@ public class XMLWhiteSpaceHandler { | |||
addPendingInline(fo); | |||
} | |||
} | |||
if (currentFO == currentBlock && nextChild == null) { | |||
/* end of block: clear the reference */ | |||
currentBlock = null; | |||
} | |||
} | |||
/** |
@@ -24,9 +24,9 @@ import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.FOPropertyMapping; | |||
import org.apache.fop.fo.flow.Table; | |||
import org.apache.fop.fo.flow.TableFObj; | |||
import org.apache.fop.fo.flow.TableCell; | |||
import org.apache.fop.fo.flow.TableColumn; | |||
import org.apache.fop.fo.flow.TableFObj; | |||
import org.apache.fop.fo.properties.Property; | |||
/** |
@@ -42,20 +42,6 @@ import org.apache.fop.fo.properties.CommonRelativePosition; | |||
import org.apache.fop.fo.properties.KeepProperty; | |||
import org.apache.fop.fo.properties.SpaceProperty; | |||
/* | |||
Modified by Mark Lillywhite mark-fop@inomial.com. The changes | |||
here are based on memory profiling and do not change functionality. | |||
Essentially, the Block object had a pointer to a BlockArea object | |||
that it created. The BlockArea was not referenced after the Block | |||
was finished except to determine the size of the BlockArea, however | |||
a reference to the BlockArea was maintained and this caused a lot of | |||
GC problems, and was a major reason for FOP memory leaks. So, | |||
the reference to BlockArea was made local, the required information | |||
is now stored (instead of a reference to the complex BlockArea object) | |||
and it appears that there are a lot of changes in this file, in fact | |||
there are only a few sematic changes; mostly I just got rid of | |||
"this." from blockArea since BlockArea is now local. | |||
*/ | |||
/** | |||
* Class modelling the fo:block object. | |||
*/ | |||
@@ -361,6 +347,48 @@ public class Block extends FObjMixed { | |||
return whiteSpaceCollapse; | |||
} | |||
/** | |||
* @return Returns the commonAccessibility. | |||
*/ | |||
public CommonAccessibility getCommonAccessibility() { | |||
return this.commonAccessibility; | |||
} | |||
/** | |||
* @return Returns the commonAural. | |||
*/ | |||
public CommonAural getCommonAural() { | |||
return this.commonAural; | |||
} | |||
/** | |||
* @return Returns the commonRelativePosition. | |||
*/ | |||
public CommonRelativePosition getCommonRelativePosition() { | |||
return this.commonRelativePosition; | |||
} | |||
/** | |||
* @return Returns the hyphenationKeep. | |||
*/ | |||
public int getHyphenationKeep() { | |||
return this.hyphenationKeep; | |||
} | |||
/** | |||
* @return Returns the intrusionDisplace. | |||
*/ | |||
public int getIntrusionDisplace() { | |||
return this.intrusionDisplace; | |||
} | |||
/** | |||
* @return Returns the lineHeightShiftAdjustment. | |||
*/ | |||
public int getLineHeightShiftAdjustment() { | |||
return this.lineHeightShiftAdjustment; | |||
} | |||
/** @see org.apache.fop.fo.FONode#charIterator() */ | |||
public CharIterator charIterator() { | |||
return NullCharIterator.getInstance(); | |||
@@ -377,4 +405,5 @@ public class Block extends FObjMixed { | |||
public int getNameId() { | |||
return FO_BLOCK; | |||
} | |||
} |
@@ -20,9 +20,8 @@ | |||
package org.apache.fop.fo.flow; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -30,6 +29,7 @@ import org.apache.fop.fo.FOEventHandler; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.FObjMixed; | |||
import org.apache.fop.fo.FOPropertyMapping; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.PropertyListMaker; | |||
import org.apache.fop.fo.ValidationException; | |||
@@ -44,7 +44,7 @@ public class Marker extends FObjMixed { | |||
// End of property values | |||
private PropertyListMaker savePropertyListMaker; | |||
private HashMap descPLists = new HashMap(); | |||
private HashMap descendantPropertyLists = new HashMap(); | |||
/** | |||
* Create a marker fo. | |||
@@ -76,14 +76,11 @@ public class Marker extends FObjMixed { | |||
* @param foNode the FO node whose property list is requested | |||
* @return the MarkerPropertyList of foNode | |||
*/ | |||
protected MarkerPropertyList getPList(FONode foNode) { | |||
return (MarkerPropertyList) descPLists.get(foNode); | |||
} | |||
protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) throws FOPException { | |||
return new MarkerPropertyList(this, parent); | |||
protected MarkerPropertyList getPropertyListFor(FONode foNode) { | |||
return (MarkerPropertyList) | |||
descendantPropertyLists.get(foNode); | |||
} | |||
/** @see org.apache.fop.fo.FONode#startOfNode() */ | |||
protected void startOfNode() { | |||
FOEventHandler foEventHandler = getFOEventHandler(); | |||
@@ -92,30 +89,18 @@ public class Marker extends FObjMixed { | |||
foEventHandler.setPropertyListMaker(new PropertyListMaker() { | |||
public PropertyList make(FObj fobj, PropertyList parentPropertyList) { | |||
PropertyList pList = new MarkerPropertyList(fobj, parentPropertyList); | |||
descPLists.put(fobj, pList); | |||
descendantPropertyLists.put(fobj, pList); | |||
return pList; | |||
} | |||
}); | |||
} | |||
/** @see org.apache.fop.fo.FONode#endOfNode() */ | |||
protected void endOfNode() throws FOPException { | |||
super.endOfNode(); | |||
// Pop the MarkerPropertyList maker. | |||
getFOEventHandler().setPropertyListMaker(savePropertyListMaker); | |||
savePropertyListMaker = null; | |||
// unparent the child property lists | |||
Iterator iter = getChildNodes(); | |||
if (iter != null) { | |||
while (iter.hasNext()) { | |||
FONode child = (FONode) iter.next(); | |||
MarkerPropertyList pList | |||
= (MarkerPropertyList) descPLists.get(child); | |||
if (pList != null) { | |||
pList.setParentPropertyList(null); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
@@ -132,7 +117,11 @@ public class Marker extends FObjMixed { | |||
invalidChildError(loc, nsURI, localName); | |||
} | |||
} | |||
protected boolean inMarker() { | |||
return true; | |||
} | |||
/** | |||
* Return the "marker-class-name" property. | |||
*/ | |||
@@ -160,30 +149,252 @@ public class Marker extends FObjMixed { | |||
} | |||
/** | |||
* An implementation of PropertyList which only stores the explicit | |||
* assigned properties. It is memory efficient but slow. | |||
* An implementation of PropertyList which only stores the explicitly | |||
* specified properties/attributes as bundles of name-value-namespace | |||
* strings | |||
*/ | |||
public class MarkerPropertyList extends PropertyList { | |||
HashMap explicit = new HashMap(); | |||
public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) { | |||
super(fobj, parentPropertyList); | |||
protected class MarkerPropertyList extends PropertyList | |||
implements Attributes { | |||
protected class MarkerAttribute { | |||
protected String namespace; | |||
protected String qname; | |||
protected String name; | |||
protected String value; | |||
/** | |||
* Main constructor | |||
* @param namespace the namespace URI | |||
* @param qname the qualified name | |||
* @param name the name | |||
* @param value the value | |||
*/ | |||
public MarkerAttribute(String namespace, String qname, | |||
String name, String value) { | |||
this.namespace = namespace; | |||
this.qname = qname; | |||
this.name = (name == null ? qname : name); | |||
this.value = value; | |||
} | |||
/** | |||
* Convenience constructor for FO attributes | |||
* @param name the attribute name | |||
* @param value the attribute value | |||
*/ | |||
public MarkerAttribute(String name, String value) { | |||
this.namespace = null; | |||
this.qname = name; | |||
this.name = name; | |||
this.value = value; | |||
} | |||
} | |||
/** the array of attributes **/ | |||
private MarkerAttribute[] attribs; | |||
/** | |||
* Set the parent property list. Used to assign a new parent | |||
* before re-binding all the child elements. | |||
* Overriding default constructor | |||
* | |||
* @param fobj the FObj to attach | |||
* @param parentPropertyList ignored | |||
*/ | |||
public void setParentPropertyList(PropertyList parentPropertyList) { | |||
this.parentPropertyList = parentPropertyList; | |||
public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) { | |||
/* ignore parentPropertyList | |||
* won't be used because the attributes will be stored | |||
* without resolving | |||
*/ | |||
super(fobj, null); | |||
} | |||
/** | |||
* Override that doesn't convert the attributes to Property instances, | |||
* but simply stores the attributes for later processing; | |||
* | |||
* @see org.apache.fop.fo.PropertyList#addAttributesToList(Attributes) | |||
*/ | |||
public void addAttributesToList(Attributes attributes) | |||
throws ValidationException { | |||
this.attribs = new MarkerAttribute[attributes.getLength()]; | |||
String name; | |||
String value; | |||
String namespace; | |||
String qname; | |||
for (int i = attributes.getLength(); --i >= 0;) { | |||
namespace = attributes.getURI(i); | |||
qname = attributes.getQName(i); | |||
name = attributes.getLocalName(i); | |||
value = attributes.getValue(i); | |||
this.attribs[i] = | |||
new MarkerAttribute(namespace, qname, name, value); | |||
} | |||
} | |||
/** | |||
* Null implementation; not used by this type of PropertyList | |||
* @see org.apache.fop.fo.PropertyList#putExplicit(int, Property) | |||
*/ | |||
public void putExplicit(int propId, Property value) { | |||
explicit.put(new Integer(propId), value); | |||
//nop | |||
} | |||
/** | |||
* Null implementation; not used by this type of PropertyList | |||
* @see org.apache.fop.fo.PropertyList#getExplicit(int) | |||
*/ | |||
public Property getExplicit(int propId) { | |||
return (Property) explicit.get(new Integer(propId)); | |||
return null; | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getLength() | |||
*/ | |||
public int getLength() { | |||
if (attribs == null) { | |||
return 0; | |||
} else { | |||
return attribs.length; | |||
} | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getURI() | |||
*/ | |||
public String getURI(int index) { | |||
if (attribs != null | |||
&& index < attribs.length | |||
&& index >= 0 | |||
&& attribs[index] != null) { | |||
return attribs[index].namespace; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getLocalName() | |||
*/ | |||
public String getLocalName(int index) { | |||
if (attribs != null | |||
&& index < attribs.length | |||
&& index >= 0 | |||
&& attribs[index] != null) { | |||
return attribs[index].name; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getQName() | |||
*/ | |||
public String getQName(int index) { | |||
if (attribs != null | |||
&& index < attribs.length | |||
&& index >= 0 | |||
&& attribs[index] != null) { | |||
return attribs[index].qname; | |||
} else { | |||
return null; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Default implementation; not used | |||
* @see org.xml.sax.Attributes#getType() | |||
*/ | |||
public String getType(int index) { | |||
return "CDATA"; | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getValue() | |||
*/ | |||
public String getValue(int index) { | |||
if (attribs != null | |||
&& index < attribs.length | |||
&& index >= 0 | |||
&& attribs[index] != null) { | |||
return attribs[index].value; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getIndex() | |||
*/ | |||
public int getIndex(String name, String namespace) { | |||
int index = -1; | |||
if (attribs != null && name != null && namespace != null) { | |||
for (int i = attribs.length; --i >= 0;) { | |||
if (attribs[i] != null | |||
&& namespace.equals(attribs[i].namespace) | |||
&& name.equals(attribs[i].name)) { | |||
break; | |||
} | |||
} | |||
} | |||
return index; | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getIndex() | |||
*/ | |||
public int getIndex(String qname) { | |||
int index = -1; | |||
if (attribs != null && qname != null) { | |||
for (int i = attribs.length; --i >= 0;) { | |||
if (attribs[i] != null | |||
&& qname.equals(attribs[i].qname)) { | |||
break; | |||
} | |||
} | |||
} | |||
return index; | |||
} | |||
/** | |||
* Default implementation; not used | |||
* @see org.xml.sax.Attributes#getType() | |||
*/ | |||
public String getType(String name, String namespace) { | |||
return "CDATA"; | |||
} | |||
/** | |||
* Default implementation; not used | |||
* @see org.xml.sax.Attributes#getType() | |||
*/ | |||
public String getType(String qname) { | |||
return "CDATA"; | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getValue() | |||
*/ | |||
public String getValue(String name, String namespace) { | |||
int index = getIndex(name, namespace); | |||
if (index > 0) { | |||
return getValue(index); | |||
} | |||
return null; | |||
} | |||
/** | |||
* @see org.xml.sax.Attributes#getValue() | |||
*/ | |||
public String getValue(String qname) { | |||
int index = getIndex(qname); | |||
if (index > 0) { | |||
return getValue(index); | |||
} | |||
return null; | |||
} | |||
} | |||
} |
@@ -19,24 +19,24 @@ | |||
package org.apache.fop.fo.flow; | |||
import java.util.Map; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.ArrayList; | |||
import org.xml.sax.Locator; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.fo.FOEventHandler; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FOText; | |||
import org.apache.fop.fo.FOPropertyMapping; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.FObjMixed; | |||
import org.apache.fop.fo.FOText; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.StaticPropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.properties.Property; | |||
import org.apache.fop.fo.properties.PropertyMaker; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.Locator; | |||
/** | |||
@@ -44,7 +44,7 @@ import org.apache.fop.fo.ValidationException; | |||
* This will create a layout manager that will retrieve | |||
* a marker based on the information. | |||
*/ | |||
public class RetrieveMarker extends FObj { | |||
public class RetrieveMarker extends FObjMixed { | |||
// The value of properties relevant for fo:retrieve-marker. | |||
private String retrieveClassName; | |||
private int retrievePosition; | |||
@@ -78,7 +78,9 @@ public class RetrieveMarker extends FObj { | |||
if (retrieveClassName == null || retrieveClassName.equals("")) { | |||
missingPropertyError("retrieve-class-name"); | |||
} | |||
} | |||
propertyList = pList.getParentPropertyList(); | |||
} | |||
/** | |||
@@ -90,40 +92,70 @@ public class RetrieveMarker extends FObj { | |||
invalidChildError(loc, nsURI, localName); | |||
} | |||
protected PropertyList createPropertyList(PropertyList parent, | |||
FOEventHandler foEventHandler) throws FOPException { | |||
// TODO: A special RetrieveMarkerPropertyList would be more memory | |||
// efficient. Storing a StaticPropertyList like this will keep all | |||
// the parent PropertyLists alive. | |||
propertyList = new StaticPropertyList(this, parent); | |||
return propertyList; | |||
} | |||
public PropertyList getPropertyList() { | |||
return propertyList; | |||
} | |||
/** | |||
* Return the "retrieve-class-name" property. | |||
* @return the "retrieve-class-name" property. | |||
*/ | |||
public String getRetrieveClassName() { | |||
return retrieveClassName; | |||
} | |||
/** | |||
* Return the "retrieve-position" property. | |||
* @return the "retrieve-position" property (enum value). | |||
*/ | |||
public int getRetrievePosition() { | |||
return retrievePosition; | |||
} | |||
/** | |||
* Return the "retrieve-boundry" property. | |||
* @return the "retrieve-boundary" property (enum value). | |||
*/ | |||
public int getRetrieveBoundary() { | |||
return retrieveBoundary; | |||
} | |||
private PropertyList createPropertyListFor(FObj fo, PropertyList parent) { | |||
return getFOEventHandler().getPropertyListMaker().make(fo, parent); | |||
} | |||
private void cloneSingleNode(FONode child, FONode newParent, | |||
Marker marker, PropertyList parentPropertyList) | |||
throws FOPException { | |||
if (child != null) { | |||
FONode newChild = child.clone(newParent, true); | |||
if (child instanceof FObj) { | |||
Marker.MarkerPropertyList pList; | |||
PropertyList newPropertyList = createPropertyListFor( | |||
(FObj) newChild, parentPropertyList); | |||
pList = marker.getPropertyListFor(child); | |||
newChild.processNode( | |||
child.getLocalName(), | |||
getLocator(), | |||
pList, | |||
newPropertyList); | |||
if (newChild.getNameId() == FO_TABLE) { | |||
Table t = (Table) child; | |||
cloneSubtree(t.getColumns().listIterator(), | |||
newChild, marker, newPropertyList); | |||
cloneSingleNode(t.getTableHeader(), | |||
newChild, marker, newPropertyList); | |||
cloneSingleNode(t.getTableFooter(), | |||
newChild, marker, newPropertyList); | |||
} | |||
cloneSubtree(child.getChildNodes(), newChild, | |||
marker, newPropertyList); | |||
if (newChild instanceof FObjMixed) { | |||
handleWhiteSpaceFor((FObjMixed) newChild); | |||
} | |||
} else if (child instanceof FOText) { | |||
FOText ft = (FOText) newChild; | |||
ft.bind(parentPropertyList); | |||
} | |||
addChildTo(newChild, (FObj) newParent); | |||
} | |||
} | |||
/** | |||
* Clone the FO nodes in the parent iterator, | |||
* attach the new nodes to the new parent, | |||
@@ -136,94 +168,48 @@ public class RetrieveMarker extends FObj { | |||
* @param descPLists the map of the new nodes to property lists | |||
*/ | |||
private void cloneSubtree(Iterator parentIter, FONode newParent, | |||
Marker marker, Map descPLists) | |||
throws FOPException { | |||
if (parentIter == null) return; | |||
while (parentIter.hasNext()) { | |||
FONode child = (FONode) parentIter.next(); | |||
FONode newChild = child.clone(newParent, true); | |||
descPLists.put(newChild, marker.getPList(child)); | |||
cloneSubtree(child.getChildNodes(), newChild, marker, descPLists); | |||
} | |||
} | |||
/** | |||
* Clone the subtree of marker, | |||
* and attach the new subtree to this node. | |||
* The property lists are not cloned; | |||
* the existing property lists of the direct children | |||
* are reparented to the property list of this node. | |||
* @param marker the marker that is to be cloned | |||
* @param descPLists the map of the new nodes to property lists | |||
*/ | |||
private void cloneFromMarker(Marker marker, Map descPLists) | |||
Marker marker, PropertyList parentPropertyList) | |||
throws FOPException { | |||
// release child nodes from a possible earlier layout | |||
childNodes = new ArrayList(); | |||
Iterator markerIter = marker.getChildNodes(); | |||
cloneSubtree(markerIter, this, marker, descPLists); | |||
// reparent the property lists of the direct children | |||
for (Iterator iter = getChildNodes(); iter.hasNext(); ) { | |||
FONode child = (FONode) iter.next(); | |||
Marker.MarkerPropertyList pList | |||
= (Marker.MarkerPropertyList) descPLists.get(child); | |||
if (pList != null) { | |||
pList.setParentPropertyList(propertyList); | |||
if (parentIter != null) { | |||
FONode child; | |||
while (parentIter.hasNext()) { | |||
child = (FONode) parentIter.next(); | |||
cloneSingleNode(child, newParent, | |||
marker, parentPropertyList); | |||
} | |||
} | |||
} | |||
/** | |||
* Bind the new nodes to the property values in this context | |||
* @param descPLists the map of the new nodes to property lists | |||
*/ | |||
private void bindChildren(Map descPLists) throws FOPException { | |||
for (Iterator i = descPLists.keySet().iterator(); i.hasNext(); ) { | |||
FONode desc = (FONode) i.next(); | |||
PropertyList descPList; | |||
if (desc instanceof FObj) { | |||
descPList = (PropertyList) descPLists.get(desc); | |||
((FObj) desc).bind(descPList); | |||
} else if (desc instanceof FOText) { | |||
descPList = (PropertyList) descPLists.get(desc.getParent()); | |||
if (descPList == null) { | |||
descPList = propertyList; | |||
} | |||
((FOText) desc).bind(descPList); | |||
} | |||
private void cloneFromMarker(Marker marker) | |||
throws FOPException { | |||
// clean up remnants from a possible earlier layout | |||
if (childNodes != null) { | |||
currentTextNode = null; | |||
childNodes.removeAll(childNodes); | |||
} | |||
cloneSubtree(marker.getChildNodes(), this, | |||
marker, propertyList); | |||
} | |||
/** | |||
* Clone the subtree of marker | |||
* and bind the nodes to the property values in this context. | |||
* The property lists are not cloned, | |||
* but the subtree is attached to the property list of this node. | |||
* This is only needed for the binding of the FO nodes. | |||
* After that a subsequent retrieve-marker | |||
* may reparent the property lists. | |||
* Clone the subtree of the given marker | |||
* | |||
* @param marker the marker that is to be cloned | |||
*/ | |||
public void bindMarker(Marker marker) { | |||
// assert(marker != null); | |||
// catch empty marker | |||
if (marker.getChildNodes() == null) { | |||
return; | |||
} | |||
HashMap descPLists = new HashMap(); | |||
try { | |||
cloneFromMarker(marker, descPLists); | |||
} catch (FOPException exc) { | |||
Log log = getLogger(); | |||
log.error("fo:retrieve-marker unable to clone subtree of fo:marker", exc); | |||
return; | |||
} | |||
try { | |||
bindChildren(descPLists); | |||
} catch (FOPException exc) { | |||
Log log = getLogger(); | |||
log.error("fo:retrieve-marker unable to rebind property values", exc); | |||
if (marker.getChildNodes() != null) { | |||
try { | |||
cloneFromMarker(marker); | |||
} catch (FOPException exc) { | |||
log.error("fo:retrieve-marker unable to clone " | |||
+ "subtree of fo:marker (marker-class-name=" | |||
+ marker.getMarkerClassName() + ")", exc); | |||
return; | |||
} | |||
} else if (log.isInfoEnabled()) { | |||
log.info("Empty marker retrieved..."); | |||
} | |||
return; | |||
} | |||
/** @see org.apache.fop.fo.FONode#getLocalName() */ | |||
@@ -236,5 +222,5 @@ public class RetrieveMarker extends FObj { | |||
*/ | |||
public int getNameId() { | |||
return FO_RETRIEVE_MARKER; | |||
} | |||
} | |||
} | |||
} |
@@ -27,6 +27,7 @@ import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.datatypes.ValidationPercentBaseContext; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.StaticPropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
@@ -209,15 +210,19 @@ public class Table extends TableFObj { | |||
* @see org.apache.fop.fo.FONode#endOfNode | |||
*/ | |||
protected void endOfNode() throws FOPException { | |||
if (!tableBodyFound) { | |||
missingChildElementError( | |||
"(marker*,table-column*,table-header?,table-footer?" | |||
+ ",table-body+)"); | |||
} | |||
if (columns != null && !columns.isEmpty()) { | |||
for (int i = columns.size(); --i >= 0;) { | |||
if (isColumnNumberUsed(i + 1)) { | |||
((TableColumn) columns.get(i)).releasePropertyList(); | |||
if (!inMarker()) { | |||
if (columns != null && !columns.isEmpty()) { | |||
for (int i = columns.size(); --i >= 0;) { | |||
TableColumn col = (TableColumn) columns.get(i); | |||
if (col != null) { | |||
col.releasePropertyList(); | |||
} | |||
} | |||
} | |||
} | |||
@@ -229,7 +234,14 @@ public class Table extends TableFObj { | |||
*/ | |||
protected void addChildNode(FONode child) throws FOPException { | |||
if ("fo:table-column".equals(child.getName())) { | |||
addColumnNode((TableColumn) child); | |||
if (columns == null) { | |||
columns = new java.util.ArrayList(); | |||
} | |||
if (!inMarker()) { | |||
addColumnNode((TableColumn) child); | |||
} else { | |||
columns.add((TableColumn) child); | |||
} | |||
} else { | |||
if ("fo:table-footer".equals(child.getName())) { | |||
tableFooter = (TableBody) child; | |||
@@ -252,9 +264,6 @@ public class Table extends TableFObj { | |||
private void addColumnNode(TableColumn col) { | |||
int colNumber = col.getColumnNumber(); | |||
int colRepeat = col.getNumberColumnsRepeated(); | |||
if (columns == null) { | |||
columns = new java.util.ArrayList(); | |||
} | |||
if (columns.size() < colNumber) { | |||
//add nulls for non-occupied indices between | |||
//the last column up to and including the current one | |||
@@ -273,6 +282,10 @@ public class Table extends TableFObj { | |||
columns.add(col); | |||
} | |||
} | |||
//flag column indices used by this column | |||
int startIndex = columnIndex - 1; | |||
int endIndex = startIndex + colRepeat; | |||
flagColumnIndices(startIndex, endIndex); | |||
} | |||
/** @return true of table-layout="auto" */ | |||
@@ -458,9 +471,17 @@ public class Table extends TableFObj { | |||
} | |||
/** | |||
* @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() | |||
* @see org.apache.fop.fo.FONode#clone(FONode, boolean) | |||
*/ | |||
protected boolean existsUsedColumnIndices() { | |||
return (usedColumnIndices != null); | |||
public FONode clone(FONode parent, boolean removeChildren) | |||
throws FOPException { | |||
FObj fobj = (FObj) super.clone(parent, removeChildren); | |||
if (removeChildren) { | |||
Table t = (Table) fobj; | |||
t.columns = null; | |||
t.tableHeader = null; | |||
t.tableFooter = null; | |||
} | |||
return fobj; | |||
} | |||
} |
@@ -25,6 +25,7 @@ import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -33,6 +34,7 @@ import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.StaticPropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
import org.apache.fop.fo.flow.TableFObj.PendingSpan; | |||
import org.apache.fop.fo.properties.CommonAccessibility; | |||
import org.apache.fop.fo.properties.CommonAural; | |||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | |||
@@ -62,7 +64,7 @@ public class TableBody extends TableFObj { | |||
* used for initial values of column-number property | |||
*/ | |||
protected List pendingSpans; | |||
protected BitSet usedColumnIndices = new BitSet(); | |||
protected BitSet usedColumnIndices; | |||
private int columnIndex = 1; | |||
protected boolean firstRow = true; | |||
@@ -87,11 +89,30 @@ public class TableBody extends TableFObj { | |||
savedPropertyList = pList; | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#processNode() | |||
*/ | |||
public void processNode(String elementName, Locator locator, | |||
Attributes attlist, PropertyList pList) | |||
throws FOPException { | |||
if (!inMarker()) { | |||
if (getTable().columns != null) { | |||
int cap = getTable().columns.size(); | |||
pendingSpans = new java.util.ArrayList(cap); | |||
usedColumnIndices = new java.util.BitSet(cap); | |||
} else { | |||
pendingSpans = new java.util.ArrayList(); | |||
usedColumnIndices = new java.util.BitSet(); | |||
} | |||
setNextColumnIndex(); | |||
} | |||
super.processNode(elementName, locator, attlist, pList); | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#startOfNode | |||
*/ | |||
protected void startOfNode() throws FOPException { | |||
initPendingSpans(); | |||
getFOEventHandler().startBody(this); | |||
} | |||
@@ -99,7 +120,16 @@ public class TableBody extends TableFObj { | |||
* @see org.apache.fop.fo.FONode#endOfNode | |||
*/ | |||
protected void endOfNode() throws FOPException { | |||
if (!inMarker()) { | |||
// clean up | |||
savedPropertyList = null; | |||
pendingSpans = null; | |||
usedColumnIndices = null; | |||
} | |||
getFOEventHandler().endBody(this); | |||
if (!(tableRowsFound || tableCellsFound)) { | |||
if (getUserAgent().validateStrictly()) { | |||
missingChildElementError("marker* (table-row+|table-cell+)"); | |||
@@ -110,18 +140,11 @@ public class TableBody extends TableFObj { | |||
} | |||
} | |||
/* | |||
if (tableCellsFound) { | |||
convertCellsToRows(); | |||
} | |||
//reset column index (so that it would be | |||
//correct if the table is cloned during | |||
//marker retrieval) | |||
resetColumnIndex(); | |||
//release references | |||
savedPropertyList = null; | |||
pendingSpans = null; | |||
usedColumnIndices = null; | |||
*/ | |||
} | |||
/** | |||
@@ -158,6 +181,18 @@ public class TableBody extends TableFObj { | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#addChildNode(FONode) | |||
*/ | |||
protected void addChildNode(FONode child) throws FOPException { | |||
if (!inMarker()) { | |||
if (firstRow && child.getNameId() == FO_TABLE_ROW) { | |||
firstRow = false; | |||
} | |||
} | |||
super.addChildNode(child); | |||
} | |||
/** | |||
* If table-cells are used as direct children of a table-body|header|footer | |||
* they are replaced in this method by proper table-rows. | |||
@@ -243,15 +278,17 @@ public class TableBody extends TableFObj { | |||
* column-number of 2, since the first column is already | |||
* occupied...) | |||
*/ | |||
protected void initPendingSpans() { | |||
if (getTable().columns != null) { | |||
List tableCols = getTable().columns; | |||
pendingSpans = new java.util.ArrayList(tableCols.size()); | |||
for (int i = tableCols.size(); --i >= 0;) { | |||
pendingSpans.add(null); | |||
} | |||
} else { | |||
if (firstRow && pendingSpans == null) { | |||
protected void initPendingSpans(FONode child) { | |||
if (child.getNameId() == FO_TABLE_ROW) { | |||
pendingSpans = ((TableRow) child).pendingSpans; | |||
} else if (pendingSpans == null) { | |||
if (getTable().columns != null) { | |||
List tableCols = getTable().columns; | |||
pendingSpans = new java.util.ArrayList(tableCols.size()); | |||
for (int i = tableCols.size(); --i >= 0;) { | |||
pendingSpans.add(null); | |||
} | |||
} else { | |||
pendingSpans = new java.util.ArrayList(); | |||
} | |||
} | |||
@@ -262,7 +299,7 @@ public class TableBody extends TableFObj { | |||
* | |||
* @return the next column number to use | |||
*/ | |||
public int getCurrentColumnIndex() { | |||
protected int getCurrentColumnIndex() { | |||
return columnIndex; | |||
} | |||
@@ -273,7 +310,7 @@ public class TableBody extends TableFObj { | |||
* | |||
* @param newIndex the new column index | |||
*/ | |||
public void setCurrentColumnIndex(int newIndex) { | |||
protected void setCurrentColumnIndex(int newIndex) { | |||
columnIndex = newIndex; | |||
} | |||
@@ -281,11 +318,12 @@ public class TableBody extends TableFObj { | |||
* Resets the current column index for the TableBody | |||
* | |||
*/ | |||
public void resetColumnIndex() { | |||
protected void resetColumnIndex() { | |||
columnIndex = 1; | |||
for (int i = 0; i < usedColumnIndices.size(); i++) { | |||
for (int i = usedColumnIndices.length(); --i >= 0;) { | |||
usedColumnIndices.clear(i); | |||
} | |||
PendingSpan pSpan; | |||
for (int i = pendingSpans.size(); --i >= 0;) { | |||
pSpan = (PendingSpan) pendingSpans.get(i); | |||
@@ -293,13 +331,10 @@ public class TableBody extends TableFObj { | |||
pSpan.rowsLeft--; | |||
if (pSpan.rowsLeft == 0) { | |||
pendingSpans.set(i, null); | |||
} else { | |||
usedColumnIndices.set(i); | |||
} | |||
} | |||
if (pendingSpans.get(i) != null) { | |||
usedColumnIndices.set(i); | |||
} else { | |||
usedColumnIndices.clear(i); | |||
} | |||
} | |||
if (!firstRow) { | |||
setNextColumnIndex(); | |||
@@ -310,19 +345,19 @@ public class TableBody extends TableFObj { | |||
* Increases columnIndex to the next available value | |||
* | |||
*/ | |||
private void setNextColumnIndex() { | |||
protected void setNextColumnIndex() { | |||
while (usedColumnIndices.get(columnIndex - 1)) { | |||
//increment columnIndex | |||
columnIndex++; | |||
//if the table has explicit columns, and | |||
//the updated index is not assigned to any | |||
//column, increment further until the next | |||
//index occupied by a column... | |||
if (getTable().columns != null) { | |||
while (columnIndex <= getTable().columns.size() | |||
&& !getTable().isColumnNumberUsed(columnIndex) ) { | |||
columnIndex++; | |||
} | |||
} | |||
//if the table has explicit columns, and | |||
//the index is not assigned to any | |||
//column, increment further until the next | |||
//index occupied by a column... | |||
if (getTable().columns != null) { | |||
while (columnIndex <= getTable().columns.size() | |||
&& !getTable().isColumnNumberUsed(columnIndex) ) { | |||
columnIndex++; | |||
} | |||
} | |||
} | |||
@@ -337,11 +372,10 @@ public class TableBody extends TableFObj { | |||
* b) there is no previous cell (implicit | |||
* start of row) | |||
*/ | |||
protected boolean lastCellEndedRow(TableCell currentCell) { | |||
if (childNodes != null && childNodes.indexOf(currentCell) > 0) { | |||
FONode prevNode = (FONode) childNodes.get( | |||
childNodes.indexOf(currentCell) - 1); | |||
if (prevNode != null && prevNode.getNameId() == FO_TABLE_CELL) { | |||
protected boolean previousCellEndedRow() { | |||
if (childNodes != null) { | |||
FONode prevNode = (FONode) childNodes.get(childNodes.size() - 1); | |||
if (prevNode.getNameId() == FO_TABLE_CELL) { | |||
return ((TableCell) prevNode).endsRow(); | |||
} | |||
} | |||
@@ -368,11 +402,4 @@ public class TableBody extends TableFObj { | |||
} | |||
setNextColumnIndex(); | |||
} | |||
/** | |||
* @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() | |||
*/ | |||
protected boolean existsUsedColumnIndices() { | |||
return (usedColumnIndices != null); | |||
} | |||
} |
@@ -22,6 +22,7 @@ package org.apache.fop.fo.flow; | |||
import java.util.BitSet; | |||
import java.util.List; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -30,10 +31,12 @@ import org.apache.fop.datatypes.Numeric; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.properties.CommonAccessibility; | |||
import org.apache.fop.fo.properties.CommonAural; | |||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | |||
import org.apache.fop.fo.properties.CommonRelativePosition; | |||
import org.apache.fop.fo.properties.KeepProperty; | |||
import org.apache.fop.fo.properties.LengthRangeProperty; | |||
/** | |||
@@ -59,6 +62,9 @@ public class TableCell extends TableFObj { | |||
private Numeric numberRowsSpanned; | |||
private int startsRow; | |||
private Length width; | |||
private KeepProperty keepTogether; | |||
private KeepProperty keepWithNext; | |||
private KeepProperty keepWithPrevious; | |||
// End of property values | |||
/** used for FO validation */ | |||
@@ -94,11 +100,6 @@ public class TableCell extends TableFObj { | |||
/** Ypos of cell ??? */ | |||
protected int top; | |||
/** | |||
* Set to true if all content completely laid out. | |||
*/ | |||
private boolean bDone = false; | |||
/** | |||
* @param parent FONode that is the parent of this object | |||
*/ | |||
@@ -122,16 +123,14 @@ public class TableCell extends TableFObj { | |||
height = pList.get(PR_HEIGHT).getLength(); | |||
id = pList.get(PR_ID).getString(); | |||
inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); | |||
columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric(); | |||
numberColumnsSpanned = pList.get(PR_NUMBER_COLUMNS_SPANNED).getNumeric(); | |||
numberRowsSpanned = pList.get(PR_NUMBER_ROWS_SPANNED).getNumeric(); | |||
startsRow = pList.get(PR_STARTS_ROW).getEnum(); | |||
width = pList.get(PR_WIDTH).getLength(); | |||
//Check to make sure we're not in retrieve-marker context | |||
//TODO: Can this be generalized/extended to other FOs/Properties? | |||
if (((TableFObj) parent).existsUsedColumnIndices()) { | |||
columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric(); | |||
} | |||
keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); | |||
keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); | |||
keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); | |||
super.bind(pList); | |||
} | |||
@@ -163,112 +162,9 @@ public class TableCell extends TableFObj { | |||
getLogger().warn("starts-row/ends-row for fo:table-cells " | |||
+ "non-applicable for children of an fo:table-row."); | |||
} | |||
updateParentColumnIndex(); | |||
getFOEventHandler().endCell(this); | |||
} | |||
private void updateParentColumnIndex() { | |||
int rowSpan = getNumberRowsSpanned(); | |||
int colSpan = getNumberColumnsSpanned(); | |||
int columnIndex = ((TableFObj) parent).getCurrentColumnIndex(); | |||
int i = -1; | |||
while (++i < colSpan) { | |||
//if table has explicit columns and the column-number isn't | |||
//assigned to any column, increment further until the next | |||
//column is encountered | |||
if (getTable().getColumns() != null) { | |||
while (columnIndex <= getTable().getColumns().size() | |||
&& !getTable().isColumnNumberUsed(columnIndex)) { | |||
columnIndex++; | |||
} | |||
} | |||
//if column-number is already in use by another cell | |||
//in the current row => error! | |||
if (((TableFObj) parent).isColumnNumberUsed(columnIndex + i)) { | |||
log.error("fo:table-cell overlaps in column " | |||
+ (columnIndex + i)); | |||
} | |||
} | |||
if (parent.getNameId() == FO_TABLE_ROW) { | |||
/* parent is a fo:table-row */ | |||
TableRow row = (TableRow) parent; | |||
TableBody body = (TableBody) parent.getParent(); | |||
if (body.isFirst(row) && getTable().columns == null ) { | |||
row.pendingSpans.add(null); | |||
if (row.usedColumnIndices == null) { | |||
row.usedColumnIndices = new BitSet(); | |||
} | |||
} | |||
//if the current cell spans more than one row, | |||
//update pending span list for the next row | |||
if (rowSpan > 1) { | |||
for (i = colSpan; --i >= 0;) { | |||
row.pendingSpans.set(columnIndex - 1 + i, | |||
new PendingSpan(rowSpan)); | |||
} | |||
} | |||
} else { | |||
/* parent is (should be) a fo:table-body/-header/-footer */ | |||
TableBody body = (TableBody) parent; | |||
/* if body.firstRow is still true, and : | |||
* a) the cell starts a row, | |||
* b) there was a previous cell | |||
* c) that previous cell didn't explicitly end the previous row | |||
* => set firstRow flag to false | |||
*/ | |||
if (startsRow() && body.firstRow) { | |||
if (!body.lastCellEndedRow(this)) { | |||
body.firstRow = false; | |||
} | |||
} | |||
/* if there were no explicit columns, pendingSpans | |||
* will not be properly initialized for the first row... | |||
*/ | |||
if (body.firstRow && getTable().columns == null) { | |||
for (i = colSpan; --i >= 0;) { | |||
body.pendingSpans.add(null); | |||
} | |||
} | |||
/* if the current cell spans more than one row, | |||
* update pending span list for the next row | |||
*/ | |||
if (rowSpan > 1) { | |||
for (i = colSpan; --i >= 0;) { | |||
body.pendingSpans.set(columnIndex - 1 + i, | |||
new PendingSpan(rowSpan)); | |||
} | |||
} | |||
} | |||
//flag column indices used by this cell, | |||
//take into account that possibly not all column-numbers | |||
//are used by columns in the parent table (if any), | |||
//so a cell spanning three columns, might actually | |||
//take up more than three columnIndices... | |||
int startIndex = columnIndex - 1; | |||
int endIndex = startIndex + colSpan; | |||
if (getTable().columns != null) { | |||
List cols = getTable().columns; | |||
int tmpIndex = endIndex; | |||
for (i = startIndex; i <= tmpIndex; ++i) { | |||
if (i < cols.size() && cols.get(i) == null) { | |||
endIndex++; | |||
} | |||
} | |||
} | |||
((TableFObj) parent).flagColumnIndices(startIndex, endIndex); | |||
if (endsRow() && parent.getNameId() != FO_TABLE_ROW) { | |||
((TableBody) parent).firstRow = false; | |||
((TableBody) parent).resetColumnIndex(); | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String) | |||
* XSL Content Model: marker* (%block;)+ | |||
@@ -304,7 +200,7 @@ public class TableCell extends TableFObj { | |||
* @return the Common Border, Padding, and Background Properties. | |||
*/ | |||
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { | |||
return commonBorderPaddingBackground; | |||
return this.commonBorderPaddingBackground; | |||
} | |||
/** |
@@ -78,10 +78,6 @@ public class TableColumn extends TableFObj { | |||
visibility = pList.get(PR_VISIBILITY).getEnum(); | |||
super.bind(pList); | |||
if (getTable().isColumnNumberUsed(columnNumber.getValue())) { | |||
throw new PropertyException("column-number \"" + columnNumber | |||
+ "\" has already been assigned to a previous column"); | |||
} | |||
if (numberColumnsRepeated.getValue() <= 0) { | |||
throw new PropertyException("number-columns-repeated must be 1 or bigger, " | |||
+ "but got " + numberColumnsRepeated.getValue()); | |||
@@ -104,10 +100,6 @@ public class TableColumn extends TableFObj { | |||
* @see org.apache.fop.fo.FONode#endOfNode | |||
*/ | |||
protected void endOfNode() throws FOPException { | |||
//flag column indices used by this column | |||
int startIndex = getColumnNumber() - 1; | |||
int endIndex = startIndex + getNumberColumnsRepeated(); | |||
getTable().flagColumnIndices(startIndex, endIndex); | |||
getFOEventHandler().endColumn(this); | |||
} | |||
@@ -211,6 +203,4 @@ public class TableColumn extends TableFObj { | |||
protected void releasePropertyList() { | |||
this.pList = null; | |||
} | |||
} | |||
} |
@@ -19,13 +19,23 @@ | |||
package org.apache.fop.fo.flow; | |||
import java.util.BitSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.datatypes.Numeric; | |||
import org.apache.fop.datatypes.ValidationPercentBaseContext; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | |||
import org.apache.fop.fo.properties.NumberProperty; | |||
import org.apache.fop.fo.properties.Property; | |||
import org.apache.fop.fo.properties.PropertyMaker; | |||
/** | |||
* Superclass for table-related FOs | |||
@@ -89,12 +99,128 @@ public abstract class TableFObj extends FObj { | |||
if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java | |||
&& getNameId() != FO_TABLE_CELL | |||
&& getCommonBorderPaddingBackground().hasPadding( | |||
ValidationPercentBaseContext.getPseudoContextForValidationPurposes())) { | |||
attributeWarning("padding-* properties are not applicable to " + getName() | |||
ValidationPercentBaseContext | |||
.getPseudoContextForValidationPurposes())) { | |||
attributeWarning( | |||
"padding-* properties are not applicable to " + getName() | |||
+ ", but a non-zero value for padding was found."); | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#addChildNode(FONode) | |||
*/ | |||
protected void addChildNode(FONode child) throws FOPException { | |||
if (!inMarker() | |||
&& child.getNameId() == FO_TABLE_CELL) { | |||
/* update current column index for the table-body/table-row */ | |||
updateColumnIndex((TableCell) child); | |||
} | |||
super.addChildNode(child); | |||
} | |||
private void updateColumnIndex(TableCell cell) | |||
throws ValidationException { | |||
int rowSpan = cell.getNumberRowsSpanned(); | |||
int colSpan = cell.getNumberColumnsSpanned(); | |||
int columnIndex = getCurrentColumnIndex(); | |||
int i = -1; | |||
while (++i < colSpan) { | |||
if (isColumnNumberUsed(columnIndex + i)) { | |||
/* if column-number is already in use by another cell | |||
* in the current row => error! | |||
*/ | |||
StringBuffer errorMessage = new StringBuffer(); | |||
errorMessage.append("fo:table-cell overlaps in column ") | |||
.append(columnIndex + i); | |||
if (locator.getLineNumber() != -1) { | |||
errorMessage.append(" (line #") | |||
.append(locator.getLineNumber()).append(", column #") | |||
.append(locator.getColumnNumber()).append(")"); | |||
} | |||
throw new ValidationException(errorMessage.toString()); | |||
} | |||
} | |||
if (getNameId() == FO_TABLE_ROW) { | |||
TableRow row = (TableRow) this; | |||
TableBody body = (TableBody) parent; | |||
for (i = colSpan; --i >= 0;) { | |||
row.pendingSpans.add(null); | |||
} | |||
/* if the current cell spans more than one row, | |||
* update pending span list for the next row | |||
*/ | |||
if (rowSpan > 1) { | |||
for (i = colSpan; --i >= 0;) { | |||
row.pendingSpans.set(columnIndex - 1 + i, | |||
new PendingSpan(rowSpan)); | |||
} | |||
} | |||
} else { | |||
TableBody body = (TableBody) this; | |||
/* if body.firstRow is still true, and : | |||
* a) the cell starts a row, | |||
* b) there was a previous cell | |||
* c) that previous cell didn't explicitly end the previous row | |||
* => set firstRow flag to false | |||
*/ | |||
if (body.firstRow && cell.startsRow()) { | |||
if (!body.previousCellEndedRow()) { | |||
body.firstRow = false; | |||
} | |||
} | |||
/* pendingSpans not initialized for the first row... | |||
*/ | |||
if (body.firstRow) { | |||
for (i = colSpan; --i >= 0;) { | |||
body.pendingSpans.add(null); | |||
} | |||
} | |||
/* if the current cell spans more than one row, | |||
* update pending span list for the next row | |||
*/ | |||
if (rowSpan > 1) { | |||
for (i = colSpan; --i >= 0;) { | |||
body.pendingSpans.set(columnIndex - 1 + i, | |||
new PendingSpan(rowSpan)); | |||
} | |||
} | |||
} | |||
/* flag column indices used by this cell, | |||
* take into account that possibly not all column-numbers | |||
* are used by columns in the parent table (if any), | |||
* so a cell spanning three columns, might actually | |||
* take up more than three columnIndices... | |||
*/ | |||
int startIndex = columnIndex - 1; | |||
int endIndex = startIndex + colSpan; | |||
if (getTable().columns != null) { | |||
List cols = getTable().columns; | |||
int tmpIndex = endIndex; | |||
for (i = startIndex; i <= tmpIndex; ++i) { | |||
if (i < cols.size() && cols.get(i) == null) { | |||
endIndex++; | |||
} | |||
} | |||
} | |||
flagColumnIndices(startIndex, endIndex); | |||
if (getNameId() != FO_TABLE_ROW && cell.endsRow()) { | |||
((TableBody) this).firstRow = false; | |||
((TableBody) this).resetColumnIndex(); | |||
} | |||
} | |||
/** | |||
* | |||
* @param side the side for which to return the border precedence | |||
@@ -121,7 +247,7 @@ public abstract class TableFObj extends FObj { | |||
* | |||
* @return the next column number to use | |||
*/ | |||
public int getCurrentColumnIndex() { | |||
protected int getCurrentColumnIndex() { | |||
return 0; | |||
} | |||
@@ -133,10 +259,10 @@ public abstract class TableFObj extends FObj { | |||
* | |||
* @param newIndex new value for column index | |||
*/ | |||
public void setCurrentColumnIndex(int newIndex) { | |||
protected void setCurrentColumnIndex(int newIndex) { | |||
//do nothing by default | |||
} | |||
/** | |||
* Checks if a certain column-number is already occupied | |||
* (overridden for Table, TableBody, TableRow) | |||
@@ -184,10 +310,83 @@ public abstract class TableFObj extends FObj { | |||
} | |||
/** | |||
* Overridden for Table, TableBody, TableRow | |||
* @return true if the usedColumnIndices BitSet exists, and is initialized | |||
* PropertyMaker subclass for the column-number property | |||
* | |||
*/ | |||
protected boolean existsUsedColumnIndices() { | |||
return false; | |||
public static class ColumnNumberPropertyMaker extends NumberProperty.Maker { | |||
/** | |||
* Constructor | |||
* @param propId the id of the property for which the maker should | |||
* be created | |||
*/ | |||
public ColumnNumberPropertyMaker(int propId) { | |||
super(propId); | |||
} | |||
/** | |||
* @see PropertyMaker#make(PropertyList) | |||
*/ | |||
public Property make(PropertyList propertyList) | |||
throws PropertyException { | |||
FObj fo = propertyList.getFObj(); | |||
if (fo.getNameId() == Constants.FO_TABLE_CELL | |||
|| fo.getNameId() == Constants.FO_TABLE_COLUMN) { | |||
if (fo.getNameId() == Constants.FO_TABLE_CELL | |||
&& fo.getParent().getNameId() != Constants.FO_TABLE_ROW | |||
&& (propertyList.get(Constants.PR_STARTS_ROW).getEnum() | |||
== Constants.EN_TRUE)) { | |||
TableBody parent = (TableBody) fo.getParent(); | |||
if (!parent.previousCellEndedRow()) { | |||
parent.resetColumnIndex(); | |||
} | |||
} | |||
return new NumberProperty(((TableFObj) fo.getParent()) | |||
.getCurrentColumnIndex()); | |||
} else { | |||
throw new PropertyException( | |||
"column-number property is only allowed" | |||
+ " on fo:table-cell or fo:table-column, not on " | |||
+ fo.getName()); | |||
} | |||
} | |||
/** | |||
* Check the value of the column-number property. | |||
* Return the parent's column index (initial value) in case | |||
* of a negative or zero value | |||
* | |||
* @see org.apache.fop.fo.properties.PropertyMaker#get( | |||
* int, PropertyList, boolean, boolean) | |||
*/ | |||
public Property get(int subpropId, PropertyList propertyList, | |||
boolean tryInherit, boolean tryDefault) | |||
throws PropertyException { | |||
Property p = super.get(0, propertyList, tryInherit, tryDefault); | |||
TableFObj fo = (TableFObj) propertyList.getFObj(); | |||
TableFObj parent = (TableFObj) propertyList.getParentFObj(); | |||
int columnIndex = p.getNumeric().getValue(); | |||
if (columnIndex <= 0) { | |||
fo.getLogger().warn("Specified negative or zero value for " | |||
+ "column-number on " + fo.getName() + ": " | |||
+ columnIndex + " forced to " | |||
+ parent.getCurrentColumnIndex()); | |||
return new NumberProperty(parent.getCurrentColumnIndex()); | |||
} | |||
//TODO: check for non-integer value and round | |||
/* if column-number was explicitly specified, force the | |||
* parent's current column index to the specified value, | |||
* so that the updated index will be the correct initial | |||
* value for the next cell/column (see Rec 7.26.8) | |||
*/ | |||
if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) { | |||
parent.setCurrentColumnIndex(p.getNumeric().getValue()); | |||
} | |||
return p; | |||
} | |||
} | |||
} |
@@ -40,7 +40,6 @@ public class TableFooter extends TableBody { | |||
* @see org.apache.fop.fo.FONode#startOfNode | |||
*/ | |||
protected void startOfNode() throws FOPException { | |||
initPendingSpans(); | |||
//getFOEventHandler().startBody(this); | |||
} | |||
@@ -40,7 +40,6 @@ public class TableHeader extends TableBody { | |||
* @see org.apache.fop.fo.FONode#startOfNode | |||
*/ | |||
protected void startOfNode() throws FOPException { | |||
initPendingSpans(); | |||
//getFOEventHandler().startHeader(this); | |||
} | |||
@@ -22,6 +22,7 @@ package org.apache.fop.fo.flow; | |||
import java.util.BitSet; | |||
import java.util.List; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -104,24 +105,30 @@ public class TableRow extends TableFObj { | |||
childNodes.add(cell); | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#processNode(String, Locator, | |||
* Attributes, PropertyList) | |||
*/ | |||
public void processNode(String elementName, Locator locator, | |||
Attributes attlist, PropertyList pList) throws FOPException { | |||
if (!inMarker()) { | |||
TableBody body = (TableBody) parent; | |||
body.resetColumnIndex(); | |||
pendingSpans = body.pendingSpans; | |||
usedColumnIndices = body.usedColumnIndices; | |||
while (usedColumnIndices.get(columnIndex - 1)) { | |||
columnIndex++; | |||
} | |||
} | |||
super.processNode(elementName, locator, attlist, pList); | |||
} | |||
/** | |||
* @see org.apache.fop.fo.FONode#startOfNode | |||
*/ | |||
protected void startOfNode() throws FOPException { | |||
pendingSpans = ((TableBody) parent).pendingSpans; | |||
usedColumnIndices = ((TableBody) parent).usedColumnIndices; | |||
while (usedColumnIndices.get(columnIndex - 1)) { | |||
columnIndex++; | |||
} | |||
checkId(id); | |||
getFOEventHandler().startRow(this); | |||
if (((TableBody) parent).isFirst(this) | |||
&& getTable().columns == null ) { | |||
if (pendingSpans == null) { | |||
pendingSpans = new java.util.ArrayList(); | |||
} | |||
} | |||
} | |||
/** | |||
@@ -131,17 +138,10 @@ public class TableRow extends TableFObj { | |||
if (childNodes == null) { | |||
missingChildElementError("(table-cell+)"); | |||
} | |||
if (((TableBody) parent).isFirst(this) | |||
&& getTable().columns == null ) { | |||
//force parent body's pendingSpans | |||
//to the one accumulated after processing this row | |||
((TableBody) parent).pendingSpans = pendingSpans; | |||
if (!inMarker()) { | |||
pendingSpans = null; | |||
usedColumnIndices = null; | |||
} | |||
((TableBody) parent).resetColumnIndex(); | |||
columnIndex = 1; | |||
//release references | |||
pendingSpans = null; | |||
usedColumnIndices = null; | |||
getFOEventHandler().endRow(this); | |||
} | |||
@@ -155,7 +155,7 @@ public class TableRow extends TableFObj { | |||
if (!(FO_URI.equals(nsURI) && localName.equals("table-cell"))) { | |||
invalidChildError(loc, nsURI, localName); | |||
} | |||
} | |||
} | |||
/** | |||
* @return the "id" property. | |||
@@ -292,12 +292,5 @@ public class TableRow extends TableFObj { | |||
while (usedColumnIndices.get(columnIndex - 1)) { | |||
columnIndex++; | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() | |||
*/ | |||
protected boolean existsUsedColumnIndices() { | |||
return (usedColumnIndices != null); | |||
} | |||
} | |||
} |
@@ -1,112 +0,0 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.fo.properties; | |||
import java.util.Iterator; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.flow.TableBody; | |||
import org.apache.fop.fo.flow.TableCell; | |||
import org.apache.fop.fo.flow.TableFObj; | |||
/** | |||
* Maker class for the column-number property on table-cells and | |||
* table-columns | |||
* | |||
*/ | |||
public class ColumnNumberPropertyMaker extends NumberProperty.Maker { | |||
/** | |||
* Constructor | |||
* @param propId the id of the property for which the maker should be created | |||
*/ | |||
public ColumnNumberPropertyMaker(int propId) { | |||
super(propId); | |||
} | |||
/** | |||
* @see PropertyMaker#make(PropertyList) | |||
*/ | |||
public Property make(PropertyList propertyList) throws PropertyException { | |||
FObj fo = propertyList.getFObj(); | |||
if (fo.getNameId() == Constants.FO_TABLE_CELL | |||
|| fo.getNameId() == Constants.FO_TABLE_COLUMN) { | |||
if (fo.getNameId() == Constants.FO_TABLE_CELL | |||
&& fo.getParent().getNameId() != Constants.FO_TABLE_ROW | |||
&& (propertyList.get(Constants.PR_STARTS_ROW).getEnum() | |||
== Constants.EN_TRUE)) { | |||
TableBody parent = (TableBody) fo.getParent(); | |||
TableCell prevCell = null; | |||
for (Iterator i = parent.getChildNodes(); | |||
(i != null && i.hasNext());) { | |||
prevCell = (TableCell) i.next(); | |||
} | |||
if (prevCell != null && !prevCell.endsRow()) { | |||
parent.resetColumnIndex(); | |||
} | |||
} | |||
return new NumberProperty(((TableFObj) fo.getParent()) | |||
.getCurrentColumnIndex()); | |||
} else { | |||
throw new PropertyException("column-number property is only allowed" | |||
+ " on fo:table-cell or fo:table-column, not on " | |||
+ fo.getName()); | |||
} | |||
} | |||
/** | |||
* Check the value of the column-number property. | |||
* Return the parent's column index (initial value) in case | |||
* of a negative or zero value | |||
* | |||
* @see org.apache.fop.fo.properties.PropertyMaker#get( | |||
* int, PropertyList, boolean, boolean) | |||
*/ | |||
public Property get(int subpropId, PropertyList propertyList, | |||
boolean tryInherit, boolean tryDefault) | |||
throws PropertyException { | |||
Property p = super.get(0, propertyList, tryInherit, tryDefault); | |||
TableFObj fo = (TableFObj) propertyList.getFObj(); | |||
TableFObj parent = (TableFObj) propertyList.getParentFObj(); | |||
int columnIndex = p.getNumeric().getValue(); | |||
if (columnIndex <= 0) { | |||
fo.getLogger().warn("Specified negative or zero value for " | |||
+ "column-number on " + fo.getName() + ": " | |||
+ columnIndex + " forced to " | |||
+ parent.getCurrentColumnIndex()); | |||
return new NumberProperty(parent.getCurrentColumnIndex()); | |||
} | |||
//TODO: check for non-integer value and round | |||
//if column-number was explicitly specified, force the parent's current | |||
//column index to the specified value, so that the updated index will | |||
//be the correct initial value for the next cell/column (see Rec 7.26.8) | |||
if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) { | |||
parent.setCurrentColumnIndex(p.getNumeric().getValue()); | |||
} | |||
return p; | |||
} | |||
} |
@@ -53,136 +53,142 @@ public class FontShorthandProperty extends ListProperty { | |||
public Property make(PropertyList propertyList, | |||
String value, FObj fo) throws PropertyException { | |||
FontShorthandProperty newProp = new FontShorthandProperty(); | |||
newProp.setSpecifiedValue(value); | |||
String specVal = value; | |||
Property prop = null; | |||
if ("inherit".equals(specVal)) { | |||
for (int i = PROP_IDS.length; --i >= 0;) { | |||
prop = propertyList.getFromParent(PROP_IDS[i]); | |||
newProp.addProperty(prop, i); | |||
} | |||
} else { | |||
/* initialize list with nulls */ | |||
for (int pos = 6; --pos >= 0;) { | |||
newProp.addProperty(null, pos); | |||
} | |||
prop = checkEnumValues(specVal); | |||
if (prop == null) { | |||
/* not an enum: | |||
* value should consist at least of font-size and font-family | |||
* separated by a space | |||
* mind the possible spaces from quoted font-family names | |||
*/ | |||
int spaceIndex = value.indexOf(' '); | |||
int quoteIndex = (value.indexOf('\'') == -1) | |||
? value.indexOf('\"') : value.indexOf('\''); | |||
if (spaceIndex == -1 | |||
|| (quoteIndex != -1 && spaceIndex > quoteIndex)) { | |||
/* no spaces or first space appears after the first | |||
* single/double quote, so malformed value string | |||
try { | |||
FontShorthandProperty newProp = new FontShorthandProperty(); | |||
newProp.setSpecifiedValue(value); | |||
String specVal = value; | |||
Property prop = null; | |||
if ("inherit".equals(specVal)) { | |||
/* fill the list with the individual properties from the parent */ | |||
for (int i = PROP_IDS.length; --i >= 0;) { | |||
prop = propertyList.getFromParent(PROP_IDS[i]); | |||
newProp.addProperty(prop, i); | |||
} | |||
} else { | |||
/* initialize list with nulls */ | |||
for (int pos = PROP_IDS.length; --pos >= 0;) { | |||
newProp.addProperty(null, pos); | |||
} | |||
prop = checkEnumValues(specVal); | |||
if (prop == null) { | |||
/* not an enum: | |||
* value should consist at least of font-size and font-family | |||
* separated by a space | |||
* mind the possible spaces from quoted font-family names | |||
*/ | |||
throw new PropertyException("Invalid property value: " | |||
+ "font=\"" + value + "\""); | |||
} | |||
PropertyMaker m = null; | |||
int fromIndex = spaceIndex + 1; | |||
int toIndex = specVal.length(); | |||
/* at least one space that appears before the first | |||
* single/double quote, so extract the individual properties | |||
*/ | |||
boolean fontFamilyParsed = false; | |||
int commaIndex = value.indexOf(','); | |||
while (!fontFamilyParsed) { | |||
/* value contains a (list of) possibly quoted | |||
* font-family name(s) | |||
int spaceIndex = value.indexOf(' '); | |||
int quoteIndex = (value.indexOf('\'') == -1) | |||
? value.indexOf('\"') : value.indexOf('\''); | |||
if (spaceIndex == -1 | |||
|| (quoteIndex != -1 && spaceIndex > quoteIndex)) { | |||
/* no spaces or first space appears after the first | |||
* single/double quote, so malformed value string | |||
*/ | |||
throw new PropertyException("Invalid property value: " | |||
+ "font=\"" + value + "\""); | |||
} | |||
PropertyMaker m = null; | |||
int fromIndex = spaceIndex + 1; | |||
int toIndex = specVal.length(); | |||
/* at least one space that appears before the first | |||
* single/double quote, so extract the individual properties | |||
*/ | |||
if (commaIndex == -1) { | |||
/* no list, just a single name | |||
* (or first name in the list) | |||
boolean fontFamilyParsed = false; | |||
int commaIndex = value.indexOf(','); | |||
while (!fontFamilyParsed) { | |||
/* value contains a (list of) possibly quoted | |||
* font-family name(s) | |||
*/ | |||
if (quoteIndex != -1) { | |||
/* a single name, quoted | |||
if (commaIndex == -1) { | |||
/* no list, just a single name | |||
* (or first name in the list) | |||
*/ | |||
fromIndex = quoteIndex; | |||
} | |||
m = FObj.getPropertyMakerFor(PROP_IDS[1]); | |||
prop = m.make(propertyList, specVal.substring(fromIndex), fo); | |||
newProp.addProperty(prop, 1); | |||
fontFamilyParsed = true; | |||
} else { | |||
if (quoteIndex != -1 && quoteIndex < commaIndex) { | |||
/* a quoted font-family name as first name | |||
* in the comma-separated list | |||
* fromIndex = index of the first quote | |||
*/ | |||
fromIndex = quoteIndex; | |||
quoteIndex = -1; | |||
if (quoteIndex != -1) { | |||
/* a single name, quoted | |||
*/ | |||
fromIndex = quoteIndex; | |||
} | |||
m = FObj.getPropertyMakerFor(PROP_IDS[1]); | |||
prop = m.make(propertyList, specVal.substring(fromIndex), fo); | |||
newProp.addProperty(prop, 1); | |||
fontFamilyParsed = true; | |||
} else { | |||
fromIndex = value.lastIndexOf(' ', commaIndex) + 1; | |||
if (quoteIndex != -1 && quoteIndex < commaIndex) { | |||
/* a quoted font-family name as first name | |||
* in the comma-separated list | |||
* fromIndex = index of the first quote | |||
*/ | |||
fromIndex = quoteIndex; | |||
quoteIndex = -1; | |||
} else { | |||
fromIndex = value.lastIndexOf(' ', commaIndex) + 1; | |||
} | |||
commaIndex = -1; | |||
} | |||
commaIndex = -1; | |||
} | |||
} | |||
toIndex = fromIndex - 1; | |||
fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1; | |||
value = specVal.substring(fromIndex, toIndex); | |||
int slashIndex = value.indexOf('/'); | |||
String fontSize = value.substring(0, | |||
(slashIndex == -1) ? value.length() : slashIndex); | |||
m = FObj.getPropertyMakerFor(PROP_IDS[0]); | |||
prop = m.make(propertyList, fontSize, fo); | |||
/* need to make sure subsequent call to LineHeightPropertyMaker.make() | |||
* doesn't generate the default font-size property... | |||
*/ | |||
propertyList.putExplicit(PROP_IDS[0], prop); | |||
newProp.addProperty(prop, 0); | |||
if (slashIndex != -1) { | |||
/* line-height */ | |||
String lineHeight = value.substring(slashIndex + 1); | |||
m = FObj.getPropertyMakerFor(PROP_IDS[2]); | |||
prop = m.make(propertyList, lineHeight, fo); | |||
newProp.addProperty(prop, 2); | |||
} | |||
if (fromIndex != 0) { | |||
toIndex = fromIndex - 1; | |||
value = specVal.substring(0, toIndex); | |||
fromIndex = 0; | |||
spaceIndex = value.indexOf(' '); | |||
do { | |||
toIndex = (spaceIndex == -1) ? value.length() : spaceIndex; | |||
String val = value.substring(fromIndex, toIndex); | |||
for (int i = 6; --i >= 3;) { | |||
if (newProp.list.get(i) == null) { | |||
/* not set */ | |||
m = FObj.getPropertyMakerFor(PROP_IDS[i]); | |||
val = m.checkValueKeywords(val); | |||
prop = m.checkEnumValues(val); | |||
if (prop != null) { | |||
newProp.addProperty(prop, i); | |||
fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1; | |||
value = specVal.substring(fromIndex, toIndex); | |||
int slashIndex = value.indexOf('/'); | |||
String fontSize = value.substring(0, | |||
(slashIndex == -1) ? value.length() : slashIndex); | |||
m = FObj.getPropertyMakerFor(PROP_IDS[0]); | |||
prop = m.make(propertyList, fontSize, fo); | |||
/* need to make sure subsequent call to LineHeightPropertyMaker.make() | |||
* doesn't generate the default font-size property... | |||
*/ | |||
propertyList.putExplicit(PROP_IDS[0], prop); | |||
newProp.addProperty(prop, 0); | |||
if (slashIndex != -1) { | |||
/* line-height */ | |||
String lineHeight = value.substring(slashIndex + 1); | |||
m = FObj.getPropertyMakerFor(PROP_IDS[2]); | |||
prop = m.make(propertyList, lineHeight, fo); | |||
newProp.addProperty(prop, 2); | |||
} | |||
if (fromIndex != 0) { | |||
toIndex = fromIndex - 1; | |||
value = specVal.substring(0, toIndex); | |||
fromIndex = 0; | |||
spaceIndex = value.indexOf(' '); | |||
do { | |||
toIndex = (spaceIndex == -1) ? value.length() : spaceIndex; | |||
String val = value.substring(fromIndex, toIndex); | |||
for (int i = 6; --i >= 3;) { | |||
if (newProp.list.get(i) == null) { | |||
/* not set */ | |||
m = FObj.getPropertyMakerFor(PROP_IDS[i]); | |||
val = m.checkValueKeywords(val); | |||
prop = m.checkEnumValues(val); | |||
if (prop != null) { | |||
newProp.addProperty(prop, i); | |||
} | |||
} | |||
} | |||
} | |||
fromIndex = toIndex + 1; | |||
spaceIndex = value.indexOf(' ', fromIndex); | |||
} while (toIndex != value.length()); | |||
fromIndex = toIndex + 1; | |||
spaceIndex = value.indexOf(' ', fromIndex); | |||
} while (toIndex != value.length()); | |||
} | |||
} else { | |||
//TODO: implement enum values | |||
log.warn("Enum values other than \"inherit\"" | |||
+ " not yet supported for the font shorthand."); | |||
return null; | |||
} | |||
} else { | |||
//TODO: implement enum values | |||
log.warn("Enum values other than \"inherit\"" | |||
+ " not yet supported for the font shorthand."); | |||
return null; | |||
} | |||
} | |||
if (newProp.list.get(0) == null || newProp.list.get(1) == null) { | |||
throw new PropertyException("Invalid property value: " | |||
+ "font-size and font-family are required for the font shorthand" | |||
+ "\nfont=" + value); | |||
} | |||
return newProp; | |||
if (newProp.list.get(0) == null || newProp.list.get(1) == null) { | |||
throw new PropertyException("Invalid property value: " | |||
+ "font-size and font-family are required for the font shorthand" | |||
+ "\nfont=\"" + value + "\""); | |||
} | |||
return newProp; | |||
} catch (PropertyException pe) { | |||
pe.setLocator(propertyList.getFObj().getLocator()); | |||
pe.setPropertyName(getName()); | |||
throw pe; | |||
} | |||
} | |||
} | |||
private void addProperty(Property prop, int pos) { |
@@ -28,6 +28,11 @@ | |||
<changes> | |||
<release version="FOP Trunk"> | |||
<action context="Code" dev="AD" type="fix"> | |||
Deferred property resolution for markers until they are actually retrieved, | |||
which leads to percentages and relative font-sizes now getting the correct | |||
values. Also deferred white-space-handling for markers. | |||
</action> | |||
<action context="Code" dev="JM" type="update"> | |||
Changed the way overflowing pages are handled. The overflow property on region-body | |||
is now used to define the behaviour. |
@@ -0,0 +1,98 @@ | |||
<?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$ --> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns:test="http://xmlgraphics.apache.org/fop/test"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="normal" page-width="5in" page-height="5in"> | |||
<fo:region-body/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="normal" white-space-collapse="true"> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:table table-layout="fixed" width="100%" border-collapse="separate"> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(2)"/> | |||
<fo:table-column column-width="proportional-column-width(2)"/> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell number-rows-spanned="3" number-columns-spanned="2" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="1" /> | |||
<fo:block>cell1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="3" /> | |||
<fo:block>cell2</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell3</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row background-color="yellow"> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell4</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="3" /> | |||
<fo:block>cell5</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell6</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row background-color="yellow"> | |||
<fo:table-cell number-rows-spanned="3" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="1" /> | |||
<fo:block>cell7</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell number-rows-spanned="3" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="2" /> | |||
<fo:block>cell8</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell9</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="3" /> | |||
<fo:block>cell10</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell11</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row background-color="yellow"> | |||
<fo:table-cell border="solid 0.5pt"> | |||
<test:assert property="column-number" expected="4" /> | |||
<fo:block>cell12</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |
@@ -194,20 +194,6 @@ | |||
<description>Keep-with-previous doesn't work inside tables and | |||
lists, yet.</description> | |||
</testcase> | |||
<testcase> | |||
<name>Whitespace around markers is not handled correctly</name> | |||
<file>marker_white-space-collapse.xml</file> | |||
<description>Whitespace within markers is handled according to the value of | |||
the white-space-collapse property in the context of the fo:marker and not according to | |||
the value of the property in the fo:retrieve-marker context.</description> | |||
</testcase> | |||
<testcase> | |||
<name>Relative font sizes within markers are not handled correctly</name> | |||
<file>marker_font-size.xml</file> | |||
<description>Relative font sizes within markers are evaluated according to the | |||
font size in the fo:marker context and not the font size in the fo:retrieve-marker | |||
context.</description> | |||
</testcase> | |||
<testcase> | |||
<name>Page breaking doesn't deal with IPD changes</name> | |||
<file>page-breaking_4.xml</file> | |||
@@ -334,7 +320,8 @@ | |||
<testcase> | |||
<name>table-cell empty area with marker.xml</name> | |||
<file>table-cell_empty_area_with_marker.xml</file> | |||
<description>A table-cell producing an empty area does currently not add any markers to a page. See TODO entry in AreaAdditionUtil.</description> | |||
<description>A table-cell producing an empty area does currently not add any markers to a page. | |||
See TODO entry in AreaAdditionUtil.</description> | |||
</testcase> | |||
<testcase> | |||
<name>Border conditionality on table</name> |
@@ -37,7 +37,9 @@ | |||
<fo:page-sequence master-reference="normal"> | |||
<fo:static-content flow-name="xsl-region-before"> | |||
<fo:block background-color="yellow" font-size="16pt"> | |||
1. Marker <fo:retrieve-marker retrieve-class-name="m1" /> | |||
1. Marker <fo:retrieve-marker retrieve-class-name="m1" | |||
retrieve-boundary="page" | |||
retrieve-position="first-starting-within-page" /> | |||
</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="xsl-region-after"> | |||
@@ -46,7 +48,7 @@ | |||
</fo:block> | |||
</fo:static-content> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block background-color="red" white-space-collapse="false"> | |||
<fo:block background-color="red"> | |||
<fo:marker marker-class-name="m1"> | |||
<fo:block font-size=".5em"> | |||
First marker with small font | |||
@@ -65,8 +67,9 @@ | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<!-- font-size relative to the retrieve-marker context? --> | |||
<eval expected="8000" xpath="//regionBefore/block[1]/block/lineArea/text/@font-size"/> | |||
<eval expected="20000" xpath="//regionBefore/block[1]/block/lineArea/text/@font-size"/> | |||
<eval expected="20000" xpath="//regionAfter/block[1]/block/lineArea/text/@font-size"/> | |||
</checks> | |||
</testcase> |
@@ -0,0 +1,106 @@ | |||
<?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 and percentage resolution: | |||
Percentages should be evaluated in the context in which the marker is | |||
retrieved. | |||
</p> | |||
</info> | |||
<fo> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<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-after extent="0.5in"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="normal"> | |||
<fo:static-content flow-name="xsl-region-after"> | |||
<fo:block text-align="end" background-color="yellow"> | |||
<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 table-layout="fixed" width="100%"> | |||
<fo:table-column number-columns-repeated="2"/> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell> | |||
<fo:marker marker-class-name="test"> | |||
<fo:table table-layout="fixed" width="100%"> | |||
<fo:table-column number-columns-repeated="2"/> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell> | |||
<fo:block>Subtotal</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell text-align="end"> | |||
<fo:block>29.95</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:marker> | |||
<fo:block>MemoryStick 32MB</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell text-align="end"> | |||
<fo:block>29.95</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell> | |||
<fo:marker marker-class-name="test"> | |||
<fo:block-container inline-progression-dimension="75%"> | |||
<fo:block>Test</fo:block> | |||
</fo:block-container> | |||
<fo:table table-layout="fixed" width="100%"> | |||
<fo:table-column number-columns-repeated="2"/> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell> | |||
<fo:block>Subtotal</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell text-align="end"> | |||
<fo:block>49.95</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:marker> | |||
<fo:block>Geek-Tool</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell text-align="end"> | |||
<fo:block>20.00</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<eval expected="270000" xpath="//regionAfter/block[1]/block[1]/@ipd"/> | |||
<eval expected="360000" xpath="//regionAfter/block[1]/block[2]/@ipd"/> | |||
</checks> | |||
</testcase> |
@@ -52,7 +52,7 @@ | |||
</fo:block> | |||
</fo:static-content> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block background-color="red" white-space-collapse="false"> | |||
<fo:block background-color="red"> | |||
<fo:marker marker-class-name="m1"> | |||
<fo:block> | |||
First marker with whitespace around | |||
@@ -71,11 +71,12 @@ | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<!-- These checks do not have the correct values --> | |||
<eval expected="1" xpath="count(//regionBefore/block[1]/block/lineArea/text/space)"/> | |||
<eval expected="4" xpath="count(//regionBefore/block[2]/block/lineArea/text/space)"/> | |||
<!-- preserve inter-word whitespace in the first block, | |||
collapse them in the second --> | |||
<eval expected="9" xpath="count(//regionBefore/block[1]/block[1]/lineArea[1]/text/space)"/> | |||
<eval expected="4" xpath="count(//regionBefore/block[2]/block[1]/lineArea[1]/text/space)"/> | |||
<eval expected="1" xpath="count(//regionAfter/block[1]/block/lineArea/text/space)"/> | |||
<eval expected="4" xpath="count(//regionAfter/block[2]/block/lineArea/text/space)"/> | |||
<eval expected="8" xpath="count(//regionAfter/block[1]/block[1]/lineArea[1]/text/space)"/> | |||
<eval expected="4" xpath="count(//regionAfter/block[2]/block[1]/lineArea[1]/text/space)"/> | |||
</checks> | |||
</testcase> |