<fonts....
</renderer>]]></source>
+ <p>
+ Another (optional) setting specific to the PDF Renderer is an output color profile, an ICC
+ color profile which indicates the target color space the PDF file is generated for. This
+ setting is mainly used in conjunction with the <a href="pdfx.html">PDF/X</a> feature.
+ An example:
+ </p>
+ <source><![CDATA[
+ <renderer mime="application/pdf">
+ <filterList...
+
+ <output-profile>C:\FOP\Color\EuropeISOCoatedFOGRA27.icc</output-profile>
+
+ <fonts....
+ </renderer>]]></source>
</section>
<section id="ps-renderer">
<title>Special Settings for the PostScript Renderer</title>
<li>
Don't use CMYK images without an ICC color profile. PDF/X doesn't allow mixing
color spaces and FOP currently only properly supports the sRGB color space. However,
- you will need to specify an output device profile (usually a CMYK profile) in the
- configuration. sRGB won't work here since it's a display device profile, not an
- output device profile.
+ you will need to specify an
+ <a href="configuration.html#pdf-renderer">output device profile</a>
+ (usually a CMYK profile) in the configuration. sRGB won't work here since it's a
+ display device profile, not an output device profile.
</li>
<li>
Don't use non-RGB colors in SVG images. Same issue as with CMYK images.
<xsd:attribute name="version" use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
- <xsd:enumeration value="1.0"></xsd:enumeration>
+ <xsd:enumeration value="1.0"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
-
<xsd:complexType name="rendererType">
- <xsd:annotation>
- <xsd:documentation>This type is stricter than required by FOP, in that it imposes an
+ <xsd:annotation>
+ <xsd:documentation>This type is stricter than required by FOP, in that it imposes an
order of the elements, which is not required by FOP.</xsd:documentation>
- </xsd:annotation>
- <xsd:sequence>
+ </xsd:annotation>
+ <xsd:sequence>
<xsd:choice minOccurs="0">
- <xsd:element name="filterList" maxOccurs="2">
- <xsd:annotation>
- <xsd:documentation>filterLists are used by the PDF renderer, MIME type
- application/pdf.</xsd:documentation>
- </xsd:annotation>
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string"
- maxOccurs="unbounded">
- </xsd:element>
- </xsd:sequence>
- <xsd:attribute name="type" use="optional">
- <xsd:simpleType>
- <xsd:restriction base="xsd:string">
- <xsd:enumeration value="image"></xsd:enumeration>
- </xsd:restriction>
- </xsd:simpleType>
- </xsd:attribute>
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="auto-rotate-landscape">
- <xsd:annotation>
- <xsd:documentation>auto-rotate-landscape is used by the PostScript renderer,
- MIME type application/postscript.</xsd:documentation>
- </xsd:annotation>
- <xsd:simpleType>
- <xsd:restriction base="xsd:string">
- <xsd:enumeration value="false"></xsd:enumeration>
- <xsd:enumeration value="true"></xsd:enumeration>
- </xsd:restriction>
- </xsd:simpleType>
- </xsd:element>
<xsd:sequence>
- <xsd:annotation>
- <xsd:documentation>rendering and text-rendering are used by the PCL renderer,
+ <xsd:annotation>
+ <xsd:documentation>Configuration elements used by the PDF renderer,
+ MIME type application/pdf</xsd:documentation>
+ </xsd:annotation>
+ <xsd:element name="filterList" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation>filterLists are used by the PDF renderer, MIME type
+ application/pdf.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="type" use="optional">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="default"/>
+ <xsd:enumeration value="content"/>
+ <xsd:enumeration value="image"/>
+ <xsd:enumeration value="jpeg"/>
+ <xsd:enumeration value="tiff"/>
+ <xsd:enumeration value="font"/>
+ <xsd:enumeration value="metadata"/>
+ <!-- Available values: see PDFFilterList.java -->
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="output-profile" type="xsd:string" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation>Output color profile used by the PDF renderer. Specifies a
+ filename to an ICC file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:sequence>
+ <xsd:annotation>
+ <xsd:documentation>Configuration elements used by the PostScript renderer,
+ MIME type application/postscript</xsd:documentation>
+ </xsd:annotation>
+ <xsd:element name="auto-rotate-landscape">
+ <xsd:annotation>
+ <xsd:documentation>auto-rotate-landscape is used by the PostScript renderer,
+ MIME type application/postscript.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="false"/>
+ <xsd:enumeration value="true"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:sequence>
+ <xsd:annotation>
+ <xsd:documentation>rendering and text-rendering are used by the PCL renderer,
MIME type application/vnd.hp-PCL</xsd:documentation>
- </xsd:annotation>
- <xsd:element name="rendering" minOccurs="0">
+ </xsd:annotation>
+ <xsd:element name="rendering" minOccurs="0">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
- <xsd:enumeration value="speed"></xsd:enumeration>
- <xsd:enumeration value="quality"></xsd:enumeration>
+ <xsd:enumeration value="speed"/>
+ <xsd:enumeration value="quality"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="text-rendering" minOccurs="0">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
- <xsd:enumeration value="auto"></xsd:enumeration>
- <xsd:enumeration value="bitmap"></xsd:enumeration>
+ <xsd:enumeration value="auto"/>
+ <xsd:enumeration value="bitmap"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
+ <xsd:sequence>
+ <xsd:annotation>
+ <xsd:documentation>The elements in this sequence apply only to the text renderer,
+ MIME type text/plain.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:element name="encoding" type="xsd:string" default="UTF-8" minOccurs="0"/>
+ </xsd:sequence>
</xsd:choice>
<xsd:element name="fonts" minOccurs="0">
<xsd:complexType>
<xsd:sequence>
- <xsd:element name="font" type="fontType"
- maxOccurs="unbounded">
- </xsd:element>
+ <xsd:element name="font" type="fontType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
+ <xsd:element name="xml-handler" minOccurs="0" maxOccurs="unbounded">
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="xsd:anyType"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
</xsd:sequence>
- <xsd:attribute name="mime" type="MimeConstants" use="required"></xsd:attribute>
+ <xsd:attribute name="mime" type="MimeConstants" use="required"/>
</xsd:complexType>
-
<xsd:complexType name="fontType">
<xsd:sequence>
- <xsd:element name="font-triplet" type="fontTripletType" maxOccurs="unbounded"></xsd:element>
+ <xsd:element name="font-triplet" type="fontTripletType" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="metrics-url" type="xsd:anyURI" use="required"></xsd:attribute>
- <xsd:attribute name="embed-url" type="xsd:anyURI" use="optional"></xsd:attribute>
- <xsd:attribute name="kerning" use="optional">
+ <xsd:attribute name="metrics-url" type="xsd:anyURI" use="required"/>
+ <xsd:attribute name="embed-url" type="xsd:anyURI" use="optional"/>
+ <xsd:attribute name="kerning" use="optional" default="no">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
- <xsd:enumeration value="yes"></xsd:enumeration>
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
-
<xsd:complexType name="fontTripletType">
- <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
- <xsd:attribute name="style" type="xsd:string" use="required"></xsd:attribute>
- <xsd:attribute name="weight" type="xsd:string" use="required"></xsd:attribute>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="style" type="xsd:string" use="required"/>
+ <xsd:attribute name="weight" type="xsd:string" use="required"/>
</xsd:complexType>
-
<xsd:simpleType name="MimeConstants">
<xsd:restriction base="xsd:string">
- <xsd:enumeration value="application/pdf"></xsd:enumeration>
- <xsd:enumeration value="application/postscript"></xsd:enumeration>
- <xsd:enumeration value="application/x-pcl"></xsd:enumeration>
- <xsd:enumeration value="application/vnd.hp-PCL"></xsd:enumeration>
- <xsd:enumeration value="application/x-afp"></xsd:enumeration>
- <xsd:enumeration value="application/vnd.ibm.modcap"></xsd:enumeration>
- <xsd:enumeration value="text/plain"></xsd:enumeration>
- <xsd:enumeration value="application/rtf"></xsd:enumeration>
- <xsd:enumeration value="text/richtext"></xsd:enumeration>
- <xsd:enumeration value="text/rtf"></xsd:enumeration>
- <xsd:enumeration value="application/mif"></xsd:enumeration>
- <xsd:enumeration value="application/X-fop-awt-preview"></xsd:enumeration>
- <xsd:enumeration value="application/X-fop-print"></xsd:enumeration>
- <xsd:enumeration value="application/X-fop-areatree"></xsd:enumeration>
+ <xsd:enumeration value="application/pdf"/>
+ <xsd:enumeration value="application/postscript"/>
+ <xsd:enumeration value="application/x-pcl"/>
+ <xsd:enumeration value="application/vnd.hp-PCL"/>
+ <xsd:enumeration value="application/x-afp"/>
+ <xsd:enumeration value="application/vnd.ibm.modcap"/>
+ <xsd:enumeration value="text/plain"/>
+ <xsd:enumeration value="application/rtf"/>
+ <xsd:enumeration value="text/richtext"/>
+ <xsd:enumeration value="text/rtf"/>
+ <xsd:enumeration value="application/mif"/>
+ <xsd:enumeration value="application/X-fop-awt-preview"/>
+ <xsd:enumeration value="application/X-fop-print"/>
+ <xsd:enumeration value="application/X-fop-areatree"/>
+ <xsd:enumeration value="image/png"/>
+ <xsd:enumeration value="image/tiff"/>
</xsd:restriction>
</xsd:simpleType>
-
</xsd:schema>
// AWT
import java.awt.Color;
+import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.BufferedImage;
ColorModel cm = imageData.getColorModel();
this.bitsPerPixel = cm.getComponentSize(0); //only use first, we assume all are equal
- this.colorSpace = cm.getColorSpace();
+ //this.colorSpace = cm.getColorSpace();
+ //We currently force the image to sRGB
+ this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] tmpMap = imageData.getRGB(0, 0, this.width,
this.height, null, 0, this.width);
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;
*/
private Set idReferences = new HashSet();
- /*
+ /**
* The property list maker.
*/
protected PropertyListMaker propertyListMaker;
*/
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
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
}
/**
- * 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.
*/
*
* @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) {
+ }
/**
*
throws FOPException {
FONode foNode = (FONode) clone();
foNode.parent = cloneparent;
- cloneparent.addChildNode(foNode);
return foNode;
}
public FOEventHandler getFOEventHandler() {
return parent.getFOEventHandler();
}
+
+ protected boolean inMarker() {
+ return getFOEventHandler().inMarker();
+ }
/**
* Returns the user agent for the node.
/**
* 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.
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;
+ }
+ }
}
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;
* @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;
}
/**
* @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
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
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)
*/
/** @see org.apache.fop.fo.FONode#endOfNode() */
protected void endOfNode() throws FOPException {
- createBlockPointers();
textTransform();
+ getFOEventHandler().characters(ca, startIndex, endIndex);
}
/**
}
/**
- * 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) {
* 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++) {
*/
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.
* 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
/* 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)) {
}
}
- ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
+ ElementMapping.Maker fobjMaker =
+ findFOMaker(namespaceURI, localName);
try {
foNode = fobjMaker.make(currentFObj);
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);
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;
}
}
currentFObj = foNode;
- if (propertyList != null) {
+ if (propertyList != null && !foEventHandler.inMarker()) {
currentPropertyList = propertyList;
}
}
+ " (" + 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());
}
*/
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;
- }
-
-
-
+ }
}
-
}
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;
* @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);
+ }
}
/**
* @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);
* @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) {
}
}
+ 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) {
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.
protected FObjMixed(FONode parent) {
super(parent);
}
-
+
/** @see org.apache.fop.fo.FONode */
protected void addCharacters(char[] data, int start, int end,
PropertyList pList,
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);
}
/** @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.
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);
}
}
*/
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);
}
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;
}
/**
* 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
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());
}
}
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.");
+ }
}
}
* @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) {
* @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) {
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.fo;
-
-import org.apache.fop.fo.Constants;
-import java.util.BitSet;
-import java.util.ArrayList;
-
-public class PropertySets {
- private static short[][] mapping = null;
- private static BitSet can_have_markers = null;
- private static BitSet no_inline_areas = null;
-
- private Element[] elements = new Element[Constants.FRM_OBJ_COUNT + 1];
- private BitSet block_elems = new BitSet();
- private BitSet inline_elems = new BitSet();
-
- BitSet CommonAccessibilityProperties = new BitSet();
- BitSet CommonAbsolutePositionProperties = new BitSet();
- BitSet CommonAuralProperties = new BitSet();
- BitSet CommonBorderPaddingBackgroundProperties = new BitSet();
- BitSet CommonFontProperties = new BitSet();
- BitSet CommonHyphenationProperties = new BitSet();
- BitSet CommonMarginPropertiesBlock = new BitSet();
- BitSet CommonMarginPropertiesInline = new BitSet();
- BitSet CommonRelativePositionProperties = new BitSet();
-
- public void initializeElements() {
- block_elems.set(Constants.FO_BLOCK);
- block_elems.set(Constants.FO_BLOCK_CONTAINER);
- block_elems.set(Constants.FO_TABLE_AND_CAPTION);
- block_elems.set(Constants.FO_TABLE);
- block_elems.set(Constants.FO_LIST_BLOCK);
-
- inline_elems.set(Constants.FO_BIDI_OVERRIDE);
- inline_elems.set(Constants.FO_CHARACTER);
- inline_elems.set(Constants.FO_EXTERNAL_GRAPHIC);
- inline_elems.set(Constants.FO_INSTREAM_FOREIGN_OBJECT);
- inline_elems.set(Constants.FO_INLINE);
- inline_elems.set(Constants.FO_INLINE_CONTAINER);
- inline_elems.set(Constants.FO_LEADER);
- inline_elems.set(Constants.FO_PAGE_NUMBER);
- inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION);
- inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION_LAST);
- inline_elems.set(Constants.FO_BASIC_LINK);
- inline_elems.set(Constants.FO_MULTI_TOGGLE);
- }
-
- public void initializeCommon() {
- CommonAccessibilityProperties.set(Constants.PR_SOURCE_DOCUMENT);
- CommonAccessibilityProperties.set(Constants.PR_ROLE);
-
- CommonAbsolutePositionProperties.set(Constants.PR_ABSOLUTE_POSITION);
- CommonAbsolutePositionProperties.set(Constants.PR_POSITION);
- CommonAbsolutePositionProperties.set(Constants.PR_TOP);
- CommonAbsolutePositionProperties.set(Constants.PR_RIGHT);
- CommonAbsolutePositionProperties.set(Constants.PR_BOTTOM);
- CommonAbsolutePositionProperties.set(Constants.PR_LEFT);
-
- CommonAuralProperties.set(Constants.PR_AZIMUTH);
- CommonAuralProperties.set(Constants.PR_CUE_AFTER);
- CommonAuralProperties.set(Constants.PR_CUE_BEFORE);
- CommonAuralProperties.set(Constants.PR_CUE);
- CommonAuralProperties.set(Constants.PR_ELEVATION);
- CommonAuralProperties.set(Constants.PR_PAUSE_AFTER);
- CommonAuralProperties.set(Constants.PR_PAUSE_BEFORE);
- CommonAuralProperties.set(Constants.PR_PAUSE);
- CommonAuralProperties.set(Constants.PR_PITCH);
- CommonAuralProperties.set(Constants.PR_PITCH_RANGE);
- CommonAuralProperties.set(Constants.PR_PLAY_DURING);
- CommonAuralProperties.set(Constants.PR_RICHNESS);
- CommonAuralProperties.set(Constants.PR_SPEAK);
- CommonAuralProperties.set(Constants.PR_SPEAK_HEADER);
- CommonAuralProperties.set(Constants.PR_SPEAK_NUMERAL);
- CommonAuralProperties.set(Constants.PR_SPEAK_PUNCTUATION);
- CommonAuralProperties.set(Constants.PR_SPEECH_RATE);
- CommonAuralProperties.set(Constants.PR_STRESS);
- CommonAuralProperties.set(Constants.PR_VOICE_FAMILY);
- CommonAuralProperties.set(Constants.PR_VOLUME);
-
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_ATTACHMENT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_IMAGE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_REPEAT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_HORIZONTAL);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_VERTICAL);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BEFORE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_AFTER);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_START);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_END);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_TOP);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BOTTOM);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_LEFT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_RIGHT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_STYLE);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_WIDTH);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_COLOR);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT);
- CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING);
-
- CommonFontProperties.set(Constants.PR_FONT);
- CommonFontProperties.set(Constants.PR_FONT_FAMILY);
- CommonFontProperties.set(Constants.PR_FONT_SELECTION_STRATEGY);
- CommonFontProperties.set(Constants.PR_FONT_SIZE);
- CommonFontProperties.set(Constants.PR_FONT_STRETCH);
- CommonFontProperties.set(Constants.PR_FONT_SIZE_ADJUST);
- CommonFontProperties.set(Constants.PR_FONT_STYLE);
- CommonFontProperties.set(Constants.PR_FONT_VARIANT);
- CommonFontProperties.set(Constants.PR_FONT_WEIGHT);
-
- CommonHyphenationProperties.set(Constants.PR_COUNTRY);
- CommonHyphenationProperties.set(Constants.PR_LANGUAGE);
- CommonHyphenationProperties.set(Constants.PR_SCRIPT);
- CommonHyphenationProperties.set(Constants.PR_HYPHENATE);
- CommonHyphenationProperties.set(Constants.PR_HYPHENATION_CHARACTER);
- CommonHyphenationProperties.set(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT);
- CommonHyphenationProperties.set(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT);
-
- CommonMarginPropertiesBlock.set(Constants.PR_MARGIN);
- CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_TOP);
- CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_BOTTOM);
- CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_LEFT);
- CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_RIGHT);
- CommonMarginPropertiesBlock.set(Constants.PR_SPACE_BEFORE);
- CommonMarginPropertiesBlock.set(Constants.PR_SPACE_AFTER);
- CommonMarginPropertiesBlock.set(Constants.PR_START_INDENT);
- CommonMarginPropertiesBlock.set(Constants.PR_END_INDENT);
-
- CommonMarginPropertiesInline.set(Constants.PR_SPACE_END);
- CommonMarginPropertiesInline.set(Constants.PR_SPACE_START);
-
- CommonRelativePositionProperties.set(Constants.PR_RELATIVE_POSITION);
-
-
- }
-
- public void initialize() {
- // define the fo: elements
- for (int i = 1; i < elements.length; i++) {
- elements[i] = new Element(i);
- }
-
- // populate the elements with properties and content elements.
- Element elem;
- elem = elements[Constants.FO_ROOT];
- elem.addProperty(Constants.PR_MEDIA_USAGE);
- elem.addContent(Constants.FO_LAYOUT_MASTER_SET);
- elem.addContent(Constants.FO_DECLARATIONS);
- elem.addContent(Constants.FO_PAGE_SEQUENCE);
-
- elem = elements[Constants.FO_DECLARATIONS];
- elem.addContent(Constants.FO_COLOR_PROFILE);
-
- elem = elements[Constants.FO_COLOR_PROFILE];
- elem.addProperty(Constants.PR_SRC);
- elem.addProperty(Constants.PR_COLOR_PROFILE_NAME);
- elem.addProperty(Constants.PR_RENDERING_INTENT);
-
- elem = elements[Constants.FO_BOOKMARK_TREE];
- elem.addContent(Constants.FO_BOOKMARK);
-
- elem = elements[Constants.FO_BOOKMARK];
- elem.addContent(Constants.FO_BOOKMARK_TITLE);
- elem.addContent(Constants.FO_BOOKMARK);
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_EXTERNAL_DESTINATION);
- elem.addProperty(Constants.PR_INTERNAL_DESTINATION);
- elem.addProperty(Constants.PR_STARTING_STATE);
-
- elem = elements[Constants.FO_BOOKMARK_TITLE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_FONT_STYLE);
- elem.addProperty(Constants.PR_FONT_WEIGHT);
-
- elem = elements[Constants.FO_PAGE_SEQUENCE_WRAPPER];
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INDEX_CLASS);
- elem.addProperty(Constants.PR_INDEX_KEY);
-
- elem = elements[Constants.FO_PAGE_SEQUENCE];
- elem.addProperty(Constants.PR_COUNTRY);
- elem.addProperty(Constants.PR_FORMAT);
- elem.addProperty(Constants.PR_LANGUAGE);
- elem.addProperty(Constants.PR_LETTER_VALUE);
- elem.addProperty(Constants.PR_GROUPING_SEPARATOR);
- elem.addProperty(Constants.PR_GROUPING_SIZE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INITIAL_PAGE_NUMBER);
- elem.addProperty(Constants.PR_FORCE_PAGE_COUNT);
- elem.addProperty(Constants.PR_MASTER_REFERENCE);
- elem.addContent(Constants.FO_TITLE);
- elem.addContent(Constants.FO_STATIC_CONTENT);
- elem.addContent(Constants.FO_FLOW);
-
- elem = elements[Constants.FO_LAYOUT_MASTER_SET];
- elem.addProperty(Constants.PR_MASTER_NAME);
- elem.addContent(Constants.FO_SIMPLE_PAGE_MASTER);
- elem.addContent(Constants.FO_PAGE_SEQUENCE_MASTER);
-
- elem = elements[Constants.FO_PAGE_SEQUENCE_MASTER];
- elem.addProperty(Constants.PR_MASTER_NAME);
- elem.addContent(Constants.FO_SINGLE_PAGE_MASTER_REFERENCE);
- elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE);
- elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES);
-
- elem = elements[Constants.FO_SINGLE_PAGE_MASTER_REFERENCE];
- elem.addProperty(Constants.PR_MASTER_REFERENCE);
-
- elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE];
- elem.addProperty(Constants.PR_MASTER_REFERENCE);
- elem.addProperty(Constants.PR_MAXIMUM_REPEATS);
-
- elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES];
- elem.addProperty(Constants.PR_MAXIMUM_REPEATS);
- elem.addContent(Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE);
-
- elem = elements[Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE];
- elem.addProperty(Constants.PR_MASTER_REFERENCE);
- elem.addProperty(Constants.PR_PAGE_POSITION);
- elem.addProperty(Constants.PR_ODD_OR_EVEN);
- elem.addProperty(Constants.PR_BLANK_OR_NOT_BLANK);
-
- elem = elements[Constants.FO_SIMPLE_PAGE_MASTER];
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperty(Constants.PR_MASTER_NAME);
- elem.addProperty(Constants.PR_PAGE_HEIGHT);
- elem.addProperty(Constants.PR_PAGE_WIDTH);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
- elem.addContent(Constants.FO_REGION_BODY);
- elem.addContent(Constants.FO_REGION_BEFORE);
- elem.addContent(Constants.FO_REGION_AFTER);
- elem.addContent(Constants.FO_REGION_START);
- elem.addContent(Constants.FO_REGION_END);
-
- elem = elements[Constants.FO_REGION_BODY];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_COLUMN_COUNT);
- elem.addProperty(Constants.PR_COLUMN_GAP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_REGION_NAME);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
-
- elem = elements[Constants.FO_REGION_BEFORE];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_EXTENT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_PRECEDENCE);
- elem.addProperty(Constants.PR_REGION_NAME);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
-
- elem = elements[Constants.FO_REGION_AFTER];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_EXTENT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_PRECEDENCE);
- elem.addProperty(Constants.PR_REGION_NAME);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
-
- elem = elements[Constants.FO_REGION_START];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_EXTENT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_REGION_NAME);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
-
- elem = elements[Constants.FO_REGION_END];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_EXTENT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_REGION_NAME);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WRITING_MODE);
-
- elem = elements[Constants.FO_FLOW];
- elem.addProperty(Constants.PR_FLOW_NAME);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_STATIC_CONTENT];
- elem.addProperty(Constants.PR_FLOW_NAME);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_TITLE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addContent(inline_elems);
-
- elem = elements[Constants.FO_BLOCK];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonHyphenationProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_HYPHENATION_KEEP);
- elem.addProperty(Constants.PR_HYPHENATION_LADDER_COUNT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LAST_LINE_END_INDENT);
- elem.addProperty(Constants.PR_LINEFEED_TREATMENT);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_LINE_HEIGHT_SHIFT_ADJUSTMENT);
- elem.addProperty(Constants.PR_LINE_STACKING_STRATEGY);
- elem.addProperty(Constants.PR_ORPHANS);
- elem.addProperty(Constants.PR_WHITE_SPACE_TREATMENT);
- elem.addProperty(Constants.PR_SPAN);
- elem.addProperty(Constants.PR_TEXT_ALIGN);
- elem.addProperty(Constants.PR_TEXT_ALIGN_LAST);
- elem.addProperty(Constants.PR_TEXT_INDENT);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WHITE_SPACE_COLLAPSE);
- elem.addProperty(Constants.PR_WIDOWS);
- elem.addProperty(Constants.PR_WRAP_OPTION);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_BLOCK_CONTAINER];
- elem.addProperties(CommonAbsolutePositionProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_SPAN);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addProperty(Constants.PR_WRITING_MODE);
- elem.addProperty(Constants.PR_Z_INDEX);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_BIDI_OVERRIDE];
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_DIRECTION);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_UNICODE_BIDI);
- elem.addProperty(Constants.PR_WORD_SPACING);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_CHARACTER];
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonHyphenationProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_TREAT_AS_WORD_SPACE);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_CHARACTER);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_GLYPH_ORIENTATION_HORIZONTAL);
- elem.addProperty(Constants.PR_GLYPH_ORIENTATION_VERTICAL);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_SUPPRESS_AT_LINE_BREAK);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_TEXT_TRANSFORM);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WORD_SPACING);
-
- elem = elements[Constants.FO_INITIAL_PROPERTY_SET];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_TEXT_TRANSFORM);
- elem.addProperty(Constants.PR_WORD_SPACING);
-
- elem = elements[Constants.FO_EXTERNAL_GRAPHIC];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_CONTENT_HEIGHT);
- elem.addProperty(Constants.PR_CONTENT_TYPE);
- elem.addProperty(Constants.PR_CONTENT_WIDTH);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_SCALING);
- elem.addProperty(Constants.PR_SCALING_METHOD);
- elem.addProperty(Constants.PR_SRC);
- elem.addProperty(Constants.PR_TEXT_ALIGN);
- elem.addProperty(Constants.PR_WIDTH);
-
- elem = elements[Constants.FO_INSTREAM_FOREIGN_OBJECT];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_CONTENT_HEIGHT);
- elem.addProperty(Constants.PR_CONTENT_TYPE);
- elem.addProperty(Constants.PR_CONTENT_WIDTH);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_SCALING);
- elem.addProperty(Constants.PR_SCALING_METHOD);
- elem.addProperty(Constants.PR_TEXT_ALIGN);
- elem.addProperty(Constants.PR_WIDTH);
-
- elem = elements[Constants.FO_INLINE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addProperty(Constants.PR_WRAP_OPTION);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_INLINE_CONTAINER];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_CLIP);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_OVERFLOW);
- elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addProperty(Constants.PR_WRITING_MODE);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_LEADER];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_COLOR);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LEADER_ALIGNMENT);
- elem.addProperty(Constants.PR_LEADER_LENGTH);
- elem.addProperty(Constants.PR_LEADER_PATTERN);
- elem.addProperty(Constants.PR_LEADER_PATTERN_WIDTH);
- elem.addProperty(Constants.PR_RULE_STYLE);
- elem.addProperty(Constants.PR_RULE_THICKNESS);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WORD_SPACING);
- elem.addContent(inline_elems);
-
- elem = elements[Constants.FO_PAGE_NUMBER];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_TEXT_TRANSFORM);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WORD_SPACING);
- elem.addProperty(Constants.PR_WRAP_OPTION);
-
- elem = elements[Constants.FO_PAGE_NUMBER_CITATION];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_REF_ID);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_TEXT_TRANSFORM);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WORD_SPACING);
- elem.addProperty(Constants.PR_WRAP_OPTION);
-
- elem = elements[Constants.FO_PAGE_NUMBER_CITATION_LAST];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonFontProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LETTER_SPACING);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_REF_ID);
- elem.addProperty(Constants.PR_SCORE_SPACES);
- elem.addProperty(Constants.PR_TEXT_ALTITUDE);
- elem.addProperty(Constants.PR_TEXT_DECORATION);
- elem.addProperty(Constants.PR_TEXT_DEPTH);
- elem.addProperty(Constants.PR_TEXT_SHADOW);
- elem.addProperty(Constants.PR_TEXT_TRANSFORM);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addProperty(Constants.PR_WORD_SPACING);
- elem.addProperty(Constants.PR_WRAP_OPTION);
-
- elem = elements[Constants.FO_TABLE_AND_CAPTION];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_CAPTION_SIDE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_TEXT_ALIGN);
- elem.addContent(Constants.FO_TABLE_CAPTION);
- elem.addContent(Constants.FO_TABLE);
-
- elem = elements[Constants.FO_TABLE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_SPACING);
- elem.addProperty(Constants.PR_BORDER_COLLAPSE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_SEPARATION);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_TABLE_LAYOUT);
- elem.addProperty(Constants.PR_TABLE_OMIT_FOOTER_AT_BREAK);
- elem.addProperty(Constants.PR_TABLE_OMIT_HEADER_AT_BREAK);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addProperty(Constants.PR_WRITING_MODE);
- elem.addContent(Constants.FO_TABLE_COLUMN);
- elem.addContent(Constants.FO_TABLE_HEADER);
- elem.addContent(Constants.FO_TABLE_FOOTER);
- elem.addContent(Constants.FO_TABLE_BODY);
-
- elem = elements[Constants.FO_TABLE_COLUMN];
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_COLUMN_NUMBER);
- elem.addProperty(Constants.PR_COLUMN_WIDTH);
- elem.addProperty(Constants.PR_NUMBER_COLUMNS_REPEATED);
- elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED);
- elem.addProperty(Constants.PR_VISIBILITY);
-
- elem = elements[Constants.FO_TABLE_CAPTION];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_TABLE_HEADER];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addContent(Constants.FO_TABLE_ROW);
- elem.addContent(Constants.FO_TABLE_CELL);
-
- elem = elements[Constants.FO_TABLE_FOOTER];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addContent(Constants.FO_TABLE_ROW);
- elem.addContent(Constants.FO_TABLE_CELL);
-
- elem = elements[Constants.FO_TABLE_BODY];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addContent(Constants.FO_TABLE_ROW);
- elem.addContent(Constants.FO_TABLE_CELL);
-
- elem = elements[Constants.FO_TABLE_ROW];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_VISIBILITY);
- elem.addContent(Constants.FO_TABLE_CELL);
-
- elem = elements[Constants.FO_TABLE_CELL];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
- elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
- elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_COLUMN_NUMBER);
- elem.addProperty(Constants.PR_DISPLAY_ALIGN);
- elem.addProperty(Constants.PR_RELATIVE_ALIGN);
- elem.addProperty(Constants.PR_EMPTY_CELLS);
- elem.addProperty(Constants.PR_ENDS_ROW);
- elem.addProperty(Constants.PR_HEIGHT);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
- elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED);
- elem.addProperty(Constants.PR_NUMBER_ROWS_SPANNED);
- elem.addProperty(Constants.PR_STARTS_ROW);
- elem.addProperty(Constants.PR_WIDTH);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_LIST_BLOCK];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_PROVISIONAL_DISTANCE_BETWEEN_STARTS);
- elem.addProperty(Constants.PR_PROVISIONAL_LABEL_SEPARATION);
- elem.addContent(Constants.FO_LIST_ITEM);
-
- elem = elements[Constants.FO_LIST_ITEM];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesBlock);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
- elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
- elem.addProperty(Constants.PR_BREAK_AFTER);
- elem.addProperty(Constants.PR_BREAK_BEFORE);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_RELATIVE_ALIGN);
- elem.addContent(Constants.FO_LIST_ITEM_LABEL);
- elem.addContent(Constants.FO_LIST_ITEM_BODY);
-
- elem = elements[Constants.FO_LIST_ITEM_BODY];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_LIST_ITEM_LABEL];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_BASIC_LINK];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperties(CommonAuralProperties);
- elem.addProperties(CommonBorderPaddingBackgroundProperties);
- elem.addProperties(CommonMarginPropertiesInline);
- elem.addProperties(CommonRelativePositionProperties);
- elem.addProperty(Constants.PR_VERTICAL_ALIGN);
- elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
- elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
- elem.addProperty(Constants.PR_BASELINE_SHIFT);
- elem.addProperty(Constants.PR_DESTINATION_PLACEMENT_OFFSET);
- elem.addProperty(Constants.PR_DOMINANT_BASELINE);
- elem.addProperty(Constants.PR_EXTERNAL_DESTINATION);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_INDICATE_DESTINATION);
- elem.addProperty(Constants.PR_INTERNAL_DESTINATION);
- elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
- elem.addProperty(Constants.PR_KEEP_TOGETHER);
- elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
- elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
- elem.addProperty(Constants.PR_LINE_HEIGHT);
- elem.addProperty(Constants.PR_SHOW_DESTINATION);
- elem.addProperty(Constants.PR_TARGET_PROCESSING_CONTEXT);
- elem.addProperty(Constants.PR_TARGET_PRESENTATION_CONTEXT);
- elem.addProperty(Constants.PR_TARGET_STYLESHEET);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_MULTI_SWITCH];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_AUTO_RESTORE);
- elem.addProperty(Constants.PR_ID);
- elem.addContent(Constants.FO_MULTI_CASE);
-
- elem = elements[Constants.FO_MULTI_CASE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_STARTING_STATE);
- elem.addProperty(Constants.PR_CASE_NAME);
- elem.addProperty(Constants.PR_CASE_TITLE);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_MULTI_TOGGLE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_SWITCH_TO);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_MULTI_PROPERTIES];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addProperty(Constants.PR_ID);
- elem.addContent(Constants.FO_MULTI_PROPERTY_SET);
- elem.addContent(Constants.FO_WRAPPER);
-
- elem = elements[Constants.FO_MULTI_PROPERTY_SET];
- elem.addProperty(Constants.PR_ID);
- elem.addProperty(Constants.PR_ACTIVE_STATE);
-
- elem = elements[Constants.FO_FLOAT];
- elem.addProperty(Constants.PR_FLOAT);
- elem.addProperty(Constants.PR_CLEAR);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_FOOTNOTE];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addContent(Constants.FO_INLINE);
- elem.addContent(Constants.FO_FOOTNOTE_BODY);
-
- elem = elements[Constants.FO_FOOTNOTE_BODY];
- elem.addProperties(CommonAccessibilityProperties);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_WRAPPER];
- elem.addProperty(Constants.PR_ID);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_MARKER];
- elem.addProperty(Constants.PR_MARKER_CLASS_NAME);
- elem.addContent(inline_elems);
- elem.addContent(block_elems);
-
- elem = elements[Constants.FO_RETRIEVE_MARKER];
- elem.addProperty(Constants.PR_RETRIEVE_CLASS_NAME);
- elem.addProperty(Constants.PR_RETRIEVE_POSITION);
- elem.addProperty(Constants.PR_RETRIEVE_BOUNDARY);
-
- // Merge the attributes from the children into the parent.
- for (boolean dirty = true; dirty;) {
- dirty = false;
- for (int i = 1; i < elements.length; i++) {
- dirty = dirty || elements[i].merge();
- }
- }
- // Calculate the sparse indices for each element.
- for (int i = 1; i < elements.length; i++) {
- mapping[i] = makeSparseIndices(elements[i].valid);
- }
- }
-
- /**
- * Turn a BitSet into an array of shorts with the first element
- * on the array the number of set bits in the BitSet.
- */
- private static short[] makeSparseIndices(BitSet set) {
- short[] indices = new short[Constants.PROPERTY_COUNT + 1];
- int j = 1;
- for (int i = 0; i < Constants.PROPERTY_COUNT + 1; i++) {
- if (set.get(i)) {
- indices[i] = (short) j++;
- }
- }
- indices[0] = (short)j;
- return indices;
- }
-
- public static short[] getPropertySet(int elementId) {
- if (mapping == null) {
- mapping = new short[Constants.FRM_OBJ_COUNT + 1][];
- PropertySets ps = new PropertySets();
- ps.initializeElements();
- ps.initializeCommon();
- ps.initialize();
- }
- return mapping[elementId];
- }
-
- /**
- * Determines if fo:markers are allowed as children for the given FO
- * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT)
- * @return true if fo:markers allowed, false otherwise
- * @todo check if still needed after validateChildNode() fully implemented
- */
- public static boolean canHaveMarkers(int elementId) {
- if (can_have_markers == null) {
- can_have_markers = new BitSet();
- can_have_markers.set(Constants.FO_BASIC_LINK);
- can_have_markers.set(Constants.FO_BIDI_OVERRIDE);
- can_have_markers.set(Constants.FO_BLOCK);
- can_have_markers.set(Constants.FO_BLOCK_CONTAINER);
- can_have_markers.set(Constants.FO_FLOW);
- can_have_markers.set(Constants.FO_INLINE);
- can_have_markers.set(Constants.FO_INLINE_CONTAINER);
- can_have_markers.set(Constants.FO_LIST_BLOCK);
- can_have_markers.set(Constants.FO_LIST_ITEM);
- can_have_markers.set(Constants.FO_LIST_ITEM_BODY);
- can_have_markers.set(Constants.FO_LIST_ITEM_LABEL);
- can_have_markers.set(Constants.FO_TABLE);
- can_have_markers.set(Constants.FO_TABLE_BODY);
- can_have_markers.set(Constants.FO_TABLE_HEADER);
- can_have_markers.set(Constants.FO_TABLE_FOOTER);
- can_have_markers.set(Constants.FO_TABLE_CELL);
- can_have_markers.set(Constants.FO_TABLE_AND_CAPTION);
- can_have_markers.set(Constants.FO_TABLE_CAPTION);
- can_have_markers.set(Constants.FO_WRAPPER);
- }
- return can_have_markers.get(elementId);
- }
-
- /**
- * Determines if the FO generates inline areas. Used only within flow.Block
- * for whitespace handling
- * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT)
- * @return true if id property is applicable, false otherwise
- * @todo see if more values need to be entered here (copied values over
- * from legacy code, list of FO's below probably incomplete)
- * @todo see if still needed (LM has a similar generatesInlineAreas()
- * method)
- */
- public static boolean generatesInlineAreas(int elementId) {
- if (no_inline_areas == null) {
- no_inline_areas = new BitSet();
- no_inline_areas.set(Constants.FO_UNKNOWN_NODE);
- no_inline_areas.set(Constants.FO_BLOCK);
- no_inline_areas.set(Constants.FO_BLOCK_CONTAINER);
- no_inline_areas.set(Constants.FO_LIST_BLOCK);
- no_inline_areas.set(Constants.FO_LIST_ITEM);
- no_inline_areas.set(Constants.FO_TABLE);
- no_inline_areas.set(Constants.FO_TABLE_AND_CAPTION);
- }
- return !(no_inline_areas.get(elementId));
- }
-
- /**
- * An object that represent the properties and contents of a fo element
- */
- class Element {
- BitSet relevant = new BitSet();
- BitSet valid = new BitSet();
- int elementId;
- ArrayList childFOs;
-
- Element(int elementId) {
- this.elementId = elementId;
- }
-
- /**
- * Add a single property to the element.
- */
- public void addProperty(int propId) {
- relevant.set(propId);
- valid.set(propId);
- }
-
- /**
- * Add a set of properties to the element.
- */
- public void addProperties(BitSet properties) {
- relevant.or(properties);
- valid.or(properties);
- }
-
- /**
- * Add a single fo element as a content child.
- */
- public void addContent(int elementId) {
- if (childFOs == null) {
- childFOs = new ArrayList();
- }
- childFOs.add(elements[elementId]);
- }
-
- /**
- * Add a set of fo elements as content childFOs.
- */
- public void addContent(BitSet elements) {
- for (int i = 0; i < elements.size(); i++) {
- if (elements.get(i)) {
- addContent(i);
- }
- }
- }
-
- /**
- * Merge the properties from the child FO's into the set of valid
- * properties. Return true if at least one property could be added.
- */
- public boolean merge() {
- if (childFOs == null) {
- return false;
- }
- boolean dirty = false;
- for (int i = 0; i < childFOs.size(); i++) {
- Element child = (Element) childFOs.get(i);
- BitSet childValid = child.valid;
- int n = childValid.length();
- for (int j = 0; j < n; j++) {
- if (childValid.get(j) && !valid.get(j)) {
- dirty = true;
- valid.set(j);
- }
- }
- }
- return dirty;
- }
- }
-}
/**
* 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;
* @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
*/
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) {
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
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
addPendingInline(fo);
}
}
+
+ if (currentFO == currentBlock && nextChild == null) {
+ /* end of block: clear the reference */
+ currentBlock = null;
+ }
}
/**
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.fo.expr;
-
-import org.apache.fop.fo.FOPropertyMapping;
-import org.apache.fop.fo.properties.Property;
-
-
-/**
- * This appears to be an artificial function, which handles the specified
- * or initial value of the property on this object.
- */
-public class FopPropValFunction extends FunctionBase {
-
- /**
- * @return 1 (the maximum number of arguments)
- */
- public int nbArgs() {
- return 1;
- }
-
- /**
- *
- * @param args array of arguments, which should either be empty, or the
- * first of which should be an NCName corresponding to a property name
- * @param pInfo PropertyInfo object to be evaluated
- * @return the Property corresponding to the input
- * @throws PropertyException for incorrect parameters
- */
- public Property eval(Property[] args,
- PropertyInfo pInfo) throws PropertyException {
- String propName = args[0].getString();
- if (propName == null) {
- throw new PropertyException("Incorrect parameter to _int-property-value function");
- }
-
- int propId = FOPropertyMapping.getPropertyId(propName);
- return pInfo.getPropertyList().get(propId);
- }
-
-}
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;
/**
package org.apache.fop.fo.expr;
-
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.flow.Table;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.TableColLength;
Number d = args[0].getNumber();
if (d == null) {
throw new PropertyException("Non numeric operand to "
- + "proportional-column-width function");
+ + "proportional-column-width() function.");
+ }
+ PropertyList pList = pInfo.getPropertyList();
+ if (!"fo:table-column".equals(pList.getFObj().getName())) {
+ throw new PropertyException("proportional-column-width() function "
+ + "may only be used on fo:table-column.");
}
- if (!pInfo.getPropertyList().getFObj().getName().equals("fo:table-column")) {
- throw new PropertyException("proportional-column-width function "
- + "may only be used on table-column FO");
+
+ Table t = (Table) pList.getParentFObj();
+ if (t.isAutoLayout()) {
+ throw new PropertyException("proportional-column-width() function "
+ + "may only be used when fo:table has "
+ + "table-layout=\"fixed\".");
}
- // Check if table-layout is "fixed"...
return new TableColLength(d.doubleValue(), pInfo.getFO());
}
new PPColWidthFunction());
FUNCTION_TABLE.put("label-end", new LabelEndFunction());
FUNCTION_TABLE.put("body-start", new BodyStartFunction());
- // NOTE: used from code generated for corresponding properties
- FUNCTION_TABLE.put("_fop-property-value", new FopPropValFunction());
/**
* * NOT YET IMPLEMENTED!!!
* @see org.apache.fop.fo.FONode#endOfNode
*/
protected void endOfNode() throws FOPException {
- if (!blockItemFound) {
- missingChildElementError("marker* (%block;)+");
+ if (!this.blockItemFound) {
+ String contentModel = "marker* (%block;)+";
+ if (getUserAgent().validateStrictly()) {
+ missingChildElementError(contentModel);
+ } else {
+ StringBuffer message = new StringBuffer(
+ errorText(getLocator()));
+ message.append(getName())
+ .append(" is missing child elements. ")
+ .append("Required Content Model: ")
+ .append(contentModel);
+ getLogger().warn(message.toString());
+ }
}
}
public String getId() {
return id;
}
-
+
}
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.
*/
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();
public int getNameId() {
return FO_BLOCK;
}
+
}
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;
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;
// End of property values
private PropertyListMaker savePropertyListMaker;
- private HashMap descPLists = new HashMap();
+ private HashMap descendantPropertyLists = new HashMap();
/**
* Create a marker fo.
* @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();
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);
- }
- }
- }
}
/**
invalidChildError(loc, nsURI, localName);
}
}
-
+
+ protected boolean inMarker() {
+ return true;
+ }
+
/**
* Return the "marker-class-name" property.
*/
}
/**
- * 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;
+ }
+ }
+}
\ No newline at end of file
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;
+
/**
* 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;
if (retrieveClassName == null || retrieveClassName.equals("")) {
missingPropertyError("retrieve-class-name");
- }
+ }
+
+ propertyList = pList.getParentPropertyList();
}
/**
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,
* @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() */
*/
public int getNameId() {
return FO_RETRIEVE_MARKER;
- }
-}
+ }
+}
\ No newline at end of file
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;
* @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();
+ }
}
}
}
*/
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;
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
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" */
}
/**
- * @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;
}
}
import java.util.List;
import java.util.ListIterator;
+import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
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;
* 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;
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);
}
* @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+)");
}
}
+ /*
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;
+ */
}
/**
}
}
+ /**
+ * @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.
* 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();
}
}
*
* @return the next column number to use
*/
- public int getCurrentColumnIndex() {
+ protected int getCurrentColumnIndex() {
return columnIndex;
}
*
* @param newIndex the new column index
*/
- public void setCurrentColumnIndex(int newIndex) {
+ protected void setCurrentColumnIndex(int newIndex) {
columnIndex = newIndex;
}
* 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);
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();
* 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++;
}
}
}
* 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();
}
}
}
setNextColumnIndex();
}
-
- /**
- * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices()
- */
- protected boolean existsUsedColumnIndices() {
- return (usedColumnIndices != null);
- }
}
import java.util.BitSet;
import java.util.List;
+import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
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;
/**
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 */
/** 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
*/
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);
}
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;)+
* @return the Common Border, Padding, and Background Properties.
*/
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
- return commonBorderPaddingBackground;
+ return this.commonBorderPaddingBackground;
}
/**
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());
* @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);
}
protected void releasePropertyList() {
this.pList = null;
}
-
-}
-
+}
\ No newline at end of file
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
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
*
* @return the next column number to use
*/
- public int getCurrentColumnIndex() {
+ protected int getCurrentColumnIndex() {
return 0;
}
*
* @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)
}
/**
- * 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;
+ }
}
}
* @see org.apache.fop.fo.FONode#startOfNode
*/
protected void startOfNode() throws FOPException {
- initPendingSpans();
//getFOEventHandler().startBody(this);
}
* @see org.apache.fop.fo.FONode#startOfNode
*/
protected void startOfNode() throws FOPException {
- initPendingSpans();
//getFOEventHandler().startHeader(this);
}
import java.util.BitSet;
import java.util.List;
+import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
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();
- }
- }
}
/**
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);
}
if (!(FO_URI.equals(nsURI) && localName.equals("table-cell"))) {
invalidChildError(loc, nsURI, localName);
}
- }
+ }
/**
* @return the "id" property.
while (usedColumnIndices.get(columnIndex - 1)) {
columnIndex++;
}
- }
-
- /**
- * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices()
- */
- protected boolean existsUsedColumnIndices() {
- return (usedColumnIndices != null);
- }
+ }
}
return this.language;
}
+ /**
+ * Releases a page-sequence's children after the page-sequence has been fully processed.
+ */
+ public void releasePageSequence() {
+ this.mainFlow = null;
+ this.flowMap.clear();
+ }
+
}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.fo.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;
- }
-}
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) {
// Java
import java.util.List;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
/**
* Default fonts for FOP application; currently this uses PDF's fonts
* by default.
return; //No fonts to process
}
+ if (resolver == null) {
+ //Ensure that we have minimal font resolution capabilities
+ resolver = createMinimalFontResolver();
+ }
+
String internalName = null;
//FontReader reader = null;
}
}
+ /** @return a new FontResolver to be used by the font subsystem */
+ private static FontResolver createMinimalFontResolver() {
+ return new FontResolver() {
+
+ /** @see org.apache.fop.fonts.FontResolver#resolve(java.lang.String) */
+ public Source resolve(String href) {
+ //Minimal functionality here
+ return new StreamSource(href);
+ }
+
+ };
+ }
/**
* Builds a list of EmbedFontInfo objects for use with the setup() method.
* @param cfg Configuration object
return null;
}
+ /**
+ * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout
+ * problems.
+ * @return the listener instance or null if no notifications are needed
+ */
+ protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() {
+ return null;
+ }
+
/*
* This method is to contain the logic to determine the LM's
* getNextKnuthElements() implementation(s) that are to be called.
log.debug("PLM> start of algorithm (" + this.getClass().getName()
+ "), flow BPD =" + flowBPD);
PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
- getPageProvider(),
+ getPageProvider(), getLayoutListener(),
alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
int iOptPageCount;
\r
import org.apache.commons.logging.Log;\r
import org.apache.commons.logging.LogFactory;\r
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;\r
import org.apache.fop.traits.MinOptMax;\r
\r
/**\r
\r
public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM,\r
PageSequenceLayoutManager.PageProvider pageProvider,\r
+ PageBreakingLayoutListener layoutListener,\r
int alignment, int alignmentLast,\r
MinOptMax footnoteSeparatorLength,\r
MinOptMax floatSeparatorLength,\r
boolean partOverflowRecovery,\r
int columnCount) {\r
- super(topLevelLM, pageProvider, alignment, alignmentLast, \r
+ super(topLevelLM, pageProvider, layoutListener,\r
+ alignment, alignmentLast, \r
footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false);\r
this.columnCount = columnCount;\r
this.considerTooShort = true; //This is important!\r
/** Maximum adjustment ration */
protected static final int INFINITE_RATIO = 1000;
- private static final int MAX_RECOVERY_ATTEMPTS = 50;
+ private static final int MAX_RECOVERY_ATTEMPTS = 5;
// constants identifying a subset of the feasible breaks
/** All feasible breaks are ok. */
* The threshold for considering breaks to be acceptable. The adjustment ratio must be
* inferior to this threshold.
*/
- private double threshold;
+ protected double threshold;
/**
* The paragraph of KnuthElements.
/** Force the algorithm to find a set of breakpoints, even if no feasible breakpoints
* exist.
*/
- private boolean force = false;
+ boolean force = false;
/** If set to true, doesn't ignore break possibilities which are definitely too short. */
protected boolean considerTooShort = false;
/** When in forced mode, the best node leading to a too long line. The line will be
* too long anyway, but this one will lead to a paragraph with fewest demerits.
*/
- private KnuthNode lastTooLong;
+ protected KnuthNode lastTooLong;
/** When in forced mode, the best node leading to a too short line. The line will be
* too short anyway, but this one will lead to a paragraph with fewest demerits.
*/
- private KnuthNode lastTooShort;
+ protected KnuthNode lastTooShort;
/** The node to be reactivated if no set of feasible breakpoints can be found for this
* paragraph.
*/
- private KnuthNode lastDeactivated;
+ protected KnuthNode lastDeactivated;
/** Alignment of the paragraph/page. One of EN_START, EN_JUSTIFY, etc. */
protected int alignment;
*/
protected int totalShrink = 0;
+ protected SumsAfter sumsAfter = new SumsAfter();
+
protected BestRecords best;
/** @see #isPartOverflowRecoveryActivated() */
private boolean partOverflowRecoveryActivated = true;
+ private KnuthNode lastRecovered;
/**
* Create a new instance.
alignmentLast = alignLast;
bFirst = first;
this.partOverflowRecoveryActivated = partOverflowRecovery;
- this.best = new BestRecords();
+ this.best = new BestRecords(log);
maxFlaggedPenaltiesCount = maxFlagCount;
}
+ protected static class SumsAfter {
+ private KnuthSequence par;
+ private int widthAfter;
+ private int stretchAfter;
+ private int shrinkAfter;
+ private int currentElementIndex = -1;
+
+ void compute(int elementIndex, int totalWidth, int totalStretch, int totalShrink) {
+ if (currentElementIndex == elementIndex) {
+ return;
+ }
+ currentElementIndex = elementIndex;
+ widthAfter = totalWidth;
+ stretchAfter = totalStretch;
+ shrinkAfter = totalShrink;
+ for (int i = elementIndex; i < par.size(); i++) {
+ KnuthElement element = (KnuthElement) par.get(i);
+ if (element.isBox()) {
+ break;
+ } else if (element.isGlue()) {
+ widthAfter += element.getW();
+ stretchAfter += element.getY();
+ shrinkAfter += element.getZ();
+ } else if (element.isForcedBreak() && i != elementIndex) {
+ break;
+ }
+ }
+ }
+
+ void initialize(KnuthSequence par) {
+ this.par = par;
+ currentElementIndex = -1;
+ }
+
+ int getWidthAfter() {
+ return widthAfter;
+ }
+
+ int getStretchAfter() {
+ return stretchAfter;
+ }
+
+ int getShrinkAfter() {
+ return shrinkAfter;
+ }
+ }
+
/**
* Class recording all the informations of a feasible breaking point.
*/
- public class KnuthNode {
+ public static class KnuthNode {
/** index of the breakpoint represented by this node */
public int position;
/** Class that stores, for each fitness class, the best active node that could start
* a line of the corresponding fitness ending at the current element.
*/
- protected class BestRecords {
+ protected static class BestRecords {
private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
//private static final double INFINITE_DEMERITS = 1E11;
+ private Log log;
private double[] bestDemerits = new double[4];
private KnuthNode[] bestNode = new KnuthNode[4];
private double[] bestAdjust = new double[4];
/** Points to the fitness class which currently leads to the best demerits. */
private int bestIndex = -1;
- public BestRecords() {
+ public BestRecords(Log log) {
+ this.log = log;
reset();
}
// create an active node representing the starting point
activeLines = new KnuthNode[20];
- addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+ registerActiveNode(createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
if (log.isTraceEnabled()) {
log.trace("Looping over " + (par.size() - startIndex) + " elements");
}
if (lastTooShort == null || lastForced.position == lastTooShort.position) {
if (isPartOverflowRecoveryActivated()) {
+ if (this.lastRecovered == null) {
+ this.lastRecovered = lastTooLong;
+ if (log.isDebugEnabled()) {
+ log.debug("Recovery point: " + lastRecovered);
+ }
+ }
+ if (lastTooLong == null) {
+ log.debug("lastTooLong null! lastTooShort: "
+ + new Boolean(lastTooShort != null));
+ }
// content would overflow, insert empty line/page and try again
KnuthNode node = createNode(
lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
0, 0, lastTooLong.previous);
lastForced = node;
node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1;
- log.debug("first part doesn't fit into line, recovering: "
- + node.fitRecoveryCounter);
+ if (log.isDebugEnabled()) {
+ log.debug("first part doesn't fit into line, recovering: "
+ + node.fitRecoveryCounter);
+ }
if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) {
- FONode contextFO = findContextFO(par, node.position + 1);
- throw new RuntimeException(FONode.decorateWithContextInfo(
- "Some content could not fit "
- + "into a line/page after " + getMaxRecoveryAttempts()
- + " attempts. Giving up to avoid an endless loop.", contextFO));
+ while (lastForced.fitRecoveryCounter > 0) {
+ lastForced = lastForced.previous;
+ lastDeactivated = lastForced.previous;
+ startLine--;
+ endLine--;
+ }
+ lastForced = this.lastRecovered;
+ this.lastRecovered = null;
+ startLine = lastForced.line;
+ endLine = lastForced.line;
+ log.debug("rolled back...");
}
} else {
lastForced = lastTooLong;
}
} else {
lastForced = lastTooShort;
+ this.lastRecovered = null;
}
- log.debug("Restarting at node " + lastForced);
+ if (log.isDebugEnabled()) {
+ log.debug("Restarting at node " + lastForced);
+ }
i = restartFrom(lastForced, i);
}
}
this.totalWidth = 0;
this.totalStretch = 0;
this.totalShrink = 0;
+ sumsAfter.initialize(par);
}
/** Creates a new active node for a feasible breakpoint at the given position. Only
protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
restartingNode.totalDemerits = 0;
- addNode(restartingNode.line, restartingNode);
+ registerActiveNode(restartingNode);
startLine = restartingNode.line;
endLine = startLine + 1;
totalWidth = restartingNode.totalWidth;
if (log.isTraceEnabled()) {
log.trace("Removing " + node);
}
- removeNode(line, node);
+ deactivateNode(node);
lastDeactivated = compareNodes(lastDeactivated, node);
}
if (force && (r <= -1 || r > threshold)) {
int fitnessClass = computeFitness(r);
double demerits = computeDemerits(node, element, fitnessClass, r);
- int newWidth = totalWidth;
- int newStretch = totalStretch;
- int newShrink = totalShrink;
-
- // add the width, stretch and shrink of glue elements after
- // the break
- // this does not affect the dimension of the line / page, only
- // the values stored in the node; these would be as if the break
- // was just before the next box element, thus ignoring glues and
- // penalties between the "real" break and the following box
- for (int i = elementIdx; i < par.size(); i++) {
- KnuthElement tempElement = getElement(i);
- if (tempElement.isBox()) {
- break;
- } else if (tempElement.isGlue()) {
- newWidth += tempElement.getW();
- newStretch += tempElement.getY();
- newShrink += tempElement.getZ();
- } else if (tempElement.isForcedBreak() && i != elementIdx) {
- break;
- }
- }
+ sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink);
+// int newWidth = totalWidth;
+// int newStretch = totalStretch;
+// int newShrink = totalShrink;
+//
+// // add the width, stretch and shrink of glue elements after
+// // the break
+// // this does not affect the dimension of the line / page, only
+// // the values stored in the node; these would be as if the break
+// // was just before the next box element, thus ignoring glues and
+// // penalties between the "real" break and the following box
+// for (int i = elementIdx; i < par.size(); i++) {
+// KnuthElement tempElement = getElement(i);
+// if (tempElement.isBox()) {
+// break;
+// } else if (tempElement.isGlue()) {
+// newWidth += tempElement.getW();
+// newStretch += tempElement.getY();
+// newShrink += tempElement.getZ();
+// } else if (tempElement.isForcedBreak() && i != elementIdx) {
+// break;
+// }
+// }
if (r <= -1) {
if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
lastTooLong = createNode(elementIdx, line + 1, fitnessClass,
- newWidth, newStretch, newShrink,
+ sumsAfter.getWidthAfter(),
+ sumsAfter.getStretchAfter(),
+ sumsAfter.getShrinkAfter(),
r, availableShrink, availableStretch,
difference, demerits, node);
if (log.isTraceEnabled()) {
difference, fitnessClass);
}
lastTooShort = createNode(elementIdx, line + 1, fitnessClass,
- newWidth, newStretch, newShrink,
+ sumsAfter.getWidthAfter(),
+ sumsAfter.getStretchAfter(),
+ sumsAfter.getShrinkAfter(),
r, availableShrink, availableStretch,
difference, demerits, node);
if (log.isTraceEnabled()) {
* @param line number of the previous line; this element will end line number (line+1)
* @param elementIdx the element's index
*/
- private void addBreaks(int line, int elementIdx) {
+ void addBreaks(int line, int elementIdx) {
if (!best.hasRecords()) {
return;
}
- int newWidth = totalWidth;
- int newStretch = totalStretch;
- int newShrink = totalShrink;
-
- // add the width, stretch and shrink of glue elements after
- // the break
- // this does not affect the dimension of the line / page, only
- // the values stored in the node; these would be as if the break
- // was just before the next box element, thus ignoring glues and
- // penalties between the "real" break and the following box
- for (int i = elementIdx; i < par.size(); i++) {
- KnuthElement tempElement = getElement(i);
- if (tempElement.isBox()) {
- break;
- } else if (tempElement.isGlue()) {
- newWidth += tempElement.getW();
- newStretch += tempElement.getY();
- newShrink += tempElement.getZ();
- } else if (tempElement.isForcedBreak() && i != elementIdx) {
- break;
- }
- }
+ sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink);
+// int newWidth = totalWidth;
+// int newStretch = totalStretch;
+// int newShrink = totalShrink;
+//
+// // add the width, stretch and shrink of glue elements after
+// // the break
+// // this does not affect the dimension of the line / page, only
+// // the values stored in the node; these would be as if the break
+// // was just before the next box element, thus ignoring glues and
+// // penalties between the "real" break and the following box
+// for (int i = elementIdx; i < par.size(); i++) {
+// KnuthElement tempElement = getElement(i);
+// if (tempElement.isBox()) {
+// break;
+// } else if (tempElement.isGlue()) {
+// newWidth += tempElement.getW();
+// newStretch += tempElement.getY();
+// newShrink += tempElement.getZ();
+// } else if (tempElement.isForcedBreak() && i != elementIdx) {
+// break;
+// }
+// }
// add nodes to the active nodes list
double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
+ " from fitness class " + i);
}
KnuthNode newNode = createNode(elementIdx, line + 1, i,
- newWidth, newStretch, newShrink);
- addNode(line + 1, newNode);
+ sumsAfter.getWidthAfter(),
+ sumsAfter.getStretchAfter(),
+ sumsAfter.getShrinkAfter());
+ registerActiveNode(newNode);
}
}
best.reset();
* @param r
* @return the fitness class
*/
- private int computeFitness(double r) {
+ int computeFitness(double r) {
if (r < -0.5) {
return 0;
} else if (r <= 0.5) {
}
/**
- * Add a node at the end of the given line's existing active nodes.
- * If this is the first node in the line, adjust endLine accordingly.
- * @param line number of the line ending at the node's corresponding breakpoint
- * @param node the active node to add
+ * Registers a new active node. If this is the first node in the corresponding line,
+ * adjust endLine accordingly.
+ * @param node the active node to register
*/
- public void addNode(int line, KnuthNode node) {
- int headIdx = line * 2;
+ public void registerActiveNode(KnuthNode node) {
+ int headIdx = node.line * 2;
if (headIdx >= activeLines.length) {
KnuthNode[] oldList = activeLines;
activeLines = new KnuthNode[headIdx + headIdx];
activeLines[headIdx + 1].next = node;
} else {
activeLines[headIdx] = node;
- endLine = line + 1;
+ endLine = node.line + 1;
}
activeLines[headIdx + 1] = node;
activeNodeCount++;
}
/**
- * Remove the given active node registered for the given line. If there are no more active nodes
- * for this line, adjust the startLine accordingly.
- * @param line number of the line ending at the node's corresponding breakpoint
+ * Deactivates the given node. If there are no more active nodes for the corresponding
+ * line, adjust the startLine accordingly.
* @param node the node to deactivate
*/
- public void removeNode(int line, KnuthNode node) {
- int headIdx = line * 2;
- KnuthNode n = getNode(line);
+ public void deactivateNode(KnuthNode node) {
+ int headIdx = node.line * 2;
+ KnuthNode n = getNode(node.line);
if (n != node) {
// nodes could be rightly deactivated in a different order
KnuthNode prevNode = null;
package org.apache.fop.layoutmgr;
+import java.text.NumberFormat;
+import java.util.Iterator;
import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
-import org.apache.fop.layoutmgr.breaking.OutOfLineRecord;
+import org.apache.fop.layoutmgr.breaking.BeforeFloatsRecord;
+import org.apache.fop.layoutmgr.breaking.ElasticLength;
+import org.apache.fop.layoutmgr.breaking.FootnotesRecord;
+import org.apache.fop.layoutmgr.breaking.BeforeFloatsRecord.BeforeFloatsProgress;
+import org.apache.fop.layoutmgr.breaking.FootnotesRecord.FootnotesProgress;
import org.apache.fop.traits.MinOptMax;
public class PageBreakingAlgorithm extends BreakingAlgorithm {
+ /* TODO vh: all of the following parameters should be available through a config
+ * option
+ */
+ /**
+ * Minimum allowed fill ratio for pages. Underfull pages which are filled at
+ * least this ratio are considered to be feasible breaks.
+ */
+ /* Set to 1.0 for now, otherwise this breaks testcases. */
+ public static final double MIN_NORMAL_PAGE_FILL_RATIO = 1.0;
+
+ /**
+ * Minimum allowed fill ratio for float-only pages. Underfull pages which are filled
+ * at least this ratio are considered to be feasible breaks.
+ */
+ public static final double MIN_FLOAT_PAGE_FILL_RATIO = 1.0;
+
+ /**
+ * Minimum acceptable ratio of normal content on pages containing both normal text and
+ * out-of-lines.
+ */
+ public static final double TEXT_FRACTION = 0.05;
+
+ /**
+ * Are float-only pages allowed?
+ */
+ public static final boolean FLOAT_PAGES_ALLOWED = true;
+
+ /**
+ * Are footnotes allowed on float-only pages?
+ */
+ public static final boolean FOOTNOTES_ALLOWED_ON_FLOAT_PAGES = true;
+
+ /**
+ * Are footnotes-only pages allowed?
+ */
+ public static final boolean FOOTNOTES_ONLY_PAGES_ALLOWED = true;
+
+ /**
+ * Additional demerits for an underfull page, which however has an acceptable fill ratio.
+ */
+ private static final double UNDERFULL_PAGE_DEMERITS = 20000;
+
+ /**
+ * This mode is chosen when out-of-lines must be typeset on a page containing normal
+ * content.
+ */
+ public static final int NORMAL_MODE = 0;
+
+ /**
+ * This mode is chosen when out-of-lines must be typeset on a float-only page.
+ */
+ public static final int FLOAT_PAGE_MODE = 1;
+
+ /**
+ * This mode is chosen when out-of-lines must be typeset on a float-only page at the
+ * end of a page-sequence.
+ */
+ public static final int FLUSH_MODE = 2;
+
/** the logger for the class */
protected static Log classLog = LogFactory.getLog(PageBreakingAlgorithm.class);
private LayoutManager topLevelLM;
private PageSequenceLayoutManager.PageProvider pageProvider;
+ private PageBreakingLayoutListener layoutListener;
/** List of PageBreakPosition elements. */
private LinkedList pageBreaks = null;
- private OutOfLineRecord footnotes;
- private OutOfLineRecord floats;
+ private NormalContentProgressInfo normalContentProgress = new NormalContentProgressInfo();
+ private FootnotesRecord footnotesRecord;
+ private BeforeFloatsRecord beforeFloatsRecord;
+ private FootnotesRecord.FootnotesProgress footnotesProgress;
+ private BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress;
+ private ActiveNodeRecorder activeNodeRecorder = new ActiveNodeRecorder();
+
// demerits for a page break that splits a footnote
private int splitFootnoteDemerits = 5000;
// demerits for a page break that defers a whole footnote to the following page
private int deferredFootnoteDemerits = 10000;
- private int deferredFloatDemerits = 10000;
-
- // the method noBreakBetween(int, int) uses these variables
- // to store parameters and result of the last call, in order
- // to reuse them and take less time
- private int storedPrevBreakIndex = -1;
- private int storedBreakIndex = -1;
- private boolean storedValue = false;
+ private int deferredFloatDemerits = 2000;
//Controls whether overflows should be warned about or not
private boolean autoHeight = false;
public PageBreakingAlgorithm(LayoutManager topLevelLM,
PageSequenceLayoutManager.PageProvider pageProvider,
+ PageBreakingLayoutListener layoutListener,
int alignment, int alignmentLast,
MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength,
boolean partOverflowRecovery, boolean autoHeight,
this.log = classLog;
this.topLevelLM = topLevelLM;
this.pageProvider = pageProvider;
- best = new BestPageRecords();
- footnotes = new OutOfLineRecord((MinOptMax) footnoteSeparatorLength.clone());
- floats = new OutOfLineRecord((MinOptMax) floatSeparatorLength.clone());
+ this.layoutListener = layoutListener;
+ best = new BestPageRecords(log);
+ footnotesRecord = new FootnotesRecord(footnoteSeparatorLength);
+ beforeFloatsRecord = new BeforeFloatsRecord(floatSeparatorLength);
+ footnotesProgress = footnotesRecord.new FootnotesProgress();
+ beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress();
// add some stretch, to avoid a restart for every page containing footnotes
if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) {
footnoteSeparatorLength.max += 10000;
*/
public class KnuthPageNode extends KnuthNode {
- public OutOfLineRecord.ProgressInfo footnotesProgress;
- public OutOfLineRecord.ProgressInfo floatsProgress;
+ public FootnotesRecord.FootnotesProgress footnotesProgress;
+ public BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress;
public KnuthPageNode(int position, int line, int fitness,
int totalWidth, int totalStretch, int totalShrink,
- OutOfLineRecord.ProgressInfo footnotesProgress,
- OutOfLineRecord.ProgressInfo floatsProgress,
+ FootnotesRecord.FootnotesProgress footnotesProgress,
+ BeforeFloatsRecord.BeforeFloatsProgress floatsProgress,
double adjustRatio, int availableShrink, int availableStretch,
int difference, double totalDemerits, KnuthNode previous) {
super(position, line, fitness,
totalWidth, totalStretch, totalShrink,
adjustRatio, availableShrink, availableStretch,
difference, totalDemerits, previous);
- this.footnotesProgress = footnotesProgress.copy();
- this.floatsProgress = floatsProgress.copy();
+ this.footnotesProgress = footnotesRecord.new FootnotesProgress(footnotesProgress);
+ this.beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress(floatsProgress);
}
}
*/
protected class BestPageRecords extends BestRecords {
- private OutOfLineRecord.ProgressInfo[] bestFootnotesProgress
- = new OutOfLineRecord.ProgressInfo[4];
- private OutOfLineRecord.ProgressInfo[] bestFloatsProgress
- = new OutOfLineRecord.ProgressInfo[4];
-
+ private FootnotesRecord.FootnotesProgress[] bestFootnotesProgress
+ = new FootnotesRecord.FootnotesProgress[4];
+ private BeforeFloatsRecord.BeforeFloatsProgress[] bestFloatsProgress
+ = new BeforeFloatsRecord.BeforeFloatsProgress[4];
+
+ public BestPageRecords(Log log) {
+ super(log);
+ }
+
public void addRecord(double demerits, KnuthNode node, double adjust,
int availableShrink, int availableStretch,
- int difference, int fitness) {
+ int difference, int fitness,
+ FootnotesRecord.FootnotesProgress footnotesProgress,
+ BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress) {
super.addRecord(demerits, node, adjust,
availableShrink, availableStretch,
difference, fitness);
- bestFootnotesProgress[fitness] = footnotes.getProgress().copy();
- bestFloatsProgress[fitness] = floats.getProgress().copy();
+ bestFootnotesProgress[fitness]
+ = footnotesRecord.new FootnotesProgress(footnotesProgress);
+ bestFloatsProgress[fitness]
+ = beforeFloatsRecord.new BeforeFloatsProgress(beforeFloatsProgress);
}
- public int getFootnotesLength(int fitness) {
- return bestFootnotesProgress[fitness].getAlreadyInsertedLength();
+ public FootnotesRecord.FootnotesProgress getFootnoteProgress(int fitness) {
+ return bestFootnotesProgress[fitness];
}
- public int getFootnoteListIndex(int fitness) {
- return bestFootnotesProgress[fitness].getLastInsertedIndex();
+ public BeforeFloatsRecord.BeforeFloatsProgress getFloatProgress(int fitness) {
+ return bestFloatsProgress[fitness];
+ }
+ }
+
+ /**
+ * This class records information about the amount of normal content that has been
+ * handled so far.
+ */
+ public class NormalContentProgressInfo {
+
+ /**
+ * Position in the Knuth sequence.
+ */
+ int position;
+
+ /**
+ * Cumulative lengths of normal content inserted so far. This corresponds to the
+ * totalWidth, totalStretch, totalShrink described in Knuth's algorithm.
+ */
+ ElasticLength insertedDims = new ElasticLength();
+
+ /**
+ * Initializes this record to handle the given Knuth sequence, such that no
+ * content has been inserted yet.
+ *
+ * @param par the sequence of normal content that will have to be typeset
+ */
+ void initialize(KnuthSequence par) {
+ insertedDims.reset();
}
- public int getFootnoteElementIndex(int fitness) {
- return bestFootnotesProgress[fitness].getLastElementOfLastInsertedIndex();
+ public String toString() {
+ return "Position: " + position + "; inserted: " + insertedDims;
}
+ }
- public OutOfLineRecord.ProgressInfo getFootnoteProgress(int fitness) {
- return bestFootnotesProgress[fitness];
+ /**
+ * Tests candidate nodes to determine whether they are feasible, and if so records
+ * them.
+ */
+ public class ActiveNodeRecorder {
+
+ /** Adjustment ratio for the currently tested page. */
+ private double adjustmentRatio;
+
+ /** Fill ratio of the currently tested page. */
+ private double fillRatio;
+
+ private int fitnessClass;
+
+ /**
+ * Difference between the physical page's BPD and the BPD of the page's content.
+ */
+ private int difference;
+
+ /** Used to record feasible breaks in flush mode. */
+ private LinkedList queue;
+
+ /**
+ * <code>true</code> if a layout must be found, even if there is no feasible
+ * break. This usually consists of selecting a too-short or too-long node.
+ */
+ private boolean force;
+
+ /**
+ * Sets the behavior of the algorithm when no feasible break is found.
+ *
+ * @param force if <code>true</code>, a too-short or too-long node must be chosen
+ * as a feasible break; otherwise no node is created.
+ */
+ void setForce(boolean force) {
+ this.force = force;
}
- public OutOfLineRecord.ProgressInfo getFloatProgress(int fitness) {
- return bestFloatsProgress[fitness];
+ /**
+ * Computes the adjustment ratio for the current page.
+ *
+ * @param totalLength total amount of content on the page (including floats and
+ * footnotes)
+ * @param pageBPD available space on the page
+ * @param minFillRatio minimum acceptable fill ratio for the page. If the content
+ * must be too much stretched to fill the page, it is allowed to stretch less
+ * provided the resulting fill ratio is superior or equal to this ratio
+ */
+ void computeAdjustmentRatio(ElasticLength totalLength, int pageBPD, double minFillRatio) {
+ difference = pageBPD - totalLength.getLength();
+ fillRatio = 1.0;
+ if (difference > 0) { // too short
+ double totalStretch = totalLength.getStretch();
+ if (totalStretch <= 0) {
+ fillRatio = ((double) totalLength.getLength()) / pageBPD;
+ if (fillRatio >= minFillRatio) {
+ adjustmentRatio = 0;
+ } else {
+ adjustmentRatio = INFINITE_RATIO;
+ }
+ } else {
+ adjustmentRatio = difference / totalStretch;
+ if (adjustmentRatio > threshold) {
+ fillRatio = (totalLength.getLength() + totalStretch * threshold) / pageBPD;
+ if (fillRatio >= minFillRatio) {
+ adjustmentRatio = threshold;
+ }
+ }
+ }
+ } else if (difference < 0) { // too long
+ double totalShrink = totalLength.getShrink();
+ if (totalShrink > 0) {
+ adjustmentRatio = difference / totalShrink;
+ } else {
+ adjustmentRatio = -INFINITE_RATIO;
+ }
+ } else {
+ adjustmentRatio = 0;
+ }
+ }
+
+ /**
+ * Computes the total demerits of the page layout up to the current page.
+ *
+ * @param footnotes information about footnotes put on the current page
+ * @param beforeFloats information about before-floats put on the current page
+ * @param lastNormalElementIdx index of the last Knuth element representing normal
+ * content put on the current page
+ * @param activeNode node representing the previous page break
+ * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+ * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ */
+ double computeDemerits(FootnotesRecord.FootnotesProgress footnotes,
+ BeforeFloatsRecord.BeforeFloatsProgress beforeFloats,
+ int lastNormalElementIdx,
+ KnuthPageNode activeNode,
+ int mode) {
+ // TODO penalty for footnotes of floats ending on penalty elements
+ KnuthElement lastNormalElement = (KnuthElement) par.get(lastNormalElementIdx);
+ double demerits = 0;
+ double f = Math.abs(adjustmentRatio);
+ /*
+ * If the adjustment ratio is too high, the demerits will be "almost
+ * infinite" (10^22). Adding demerits for a deferred float (10000) thus
+ * won't change the demerits value. We may end up with two breakpoints
+ * with the same demerits, whereas in one case there are deferred floats
+ * and not in the other case. The case with no deferred floats is still
+ * preferable, so we must have the possibility to distinguish it. By
+ * forcing f to threshold it becomes possible to make the difference
+ * when there are deferred floats.
+ */
+ if (f > threshold) {
+ f = threshold;
+ }
+ f = 1 + 100 * f * f * f;
+ double minPageFillRatio;
+ if (mode == NORMAL_MODE) {
+ minPageFillRatio = MIN_NORMAL_PAGE_FILL_RATIO;
+ if (!lastNormalElement.isPenalty()) {
+ demerits = f * f;
+ } else {
+ double penalty = lastNormalElement.getP();
+ if (penalty >= 0) {
+ f += penalty;
+ demerits = f * f;
+ } else if (!lastNormalElement.isForcedBreak()) {
+ demerits = f * f - penalty * penalty;
+ } else {
+ demerits = f * f;
+ }
+ if (((KnuthPenalty) lastNormalElement).isFlagged()
+ && getElement(activeNode.position).isPenalty()
+ && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
+ // add demerit for consecutive breaks at flagged penalties
+ demerits += repeatedFlaggedDemerit;
+ }
+ }
+ } else {
+ minPageFillRatio = MIN_FLOAT_PAGE_FILL_RATIO;
+ demerits = f * f;
+ }
+ fitnessClass = computeFitness(adjustmentRatio);
+ if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
+ // add demerit for consecutive breaks
+ // with very different fitness classes
+ demerits += incompatibleFitnessDemerit;
+ }
+
+ demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits;
+ if (footnotes.isLastSplit()) {
+ demerits += footnotes.getNbSplit() * splitFootnoteDemerits;
+ }
+ demerits += beforeFloats.getNbOfDeferred() * deferredFloatDemerits;
+ if (beforeFloats.isLastSplit()) {
+ demerits += beforeFloats.getNbSplit() * splitFootnoteDemerits;
+ }
+ if (fillRatio < minPageFillRatio) {
+ /* To select too-short nodes among the least too underfull pages. This formula
+ * will give smaller results than below but, anyway, too-short nodes are
+ * handled separately
+ */
+ demerits += (2.0 - fillRatio) * UNDERFULL_PAGE_DEMERITS;
+ } else if (minPageFillRatio < 1.0) {
+ /* demerits += x * UNDERFULL_PAGE_DEMERITS
+ * The idea is that x tends to 1.0 when fillRatio tends to
+ * MIN_PAGE_FILL_RATIO, to give the preference to full pages. Of course the
+ * following formula works only if MIN_PAGE_FILL_RATIO != 1.0, hence the test
+ */
+ demerits += (1.0 - fillRatio) / (1.0 - minPageFillRatio) * UNDERFULL_PAGE_DEMERITS;
+ }
+ demerits += activeNode.totalDemerits;
+ return demerits;
+ }
+
+ /**
+ * Tests if the node corresponding to the given parameters represents a feasible
+ * break, and if so computes its demerits and records it. This methods returns
+ * <code>true</code> if there is potential room for putting additional content on
+ * the page. Otherwise, this indicates that it is not even worth trying.
+ *
+ * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+ * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ * @param normalProgress progress information for the normal content
+ * @param footnotesProgress progress information for footnotes
+ * @param beforeFloatsProgress progress information for before-floats
+ * @param previousNode node representing the previous page break
+ * @return <code>true</code> if some additional content may potentially be added
+ * on the page (adjustment ratio > -1); otherwise <code>false</code>
+ * (adjustment ratio <= -1)
+ */
+ public boolean handleNode(int mode,
+ NormalContentProgressInfo normalProgress,
+ FootnotesRecord.FootnotesProgress footnotesProgress,
+ BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+ KnuthPageNode previousNode) {
+ int pageBPD = getLineWidth(previousNode.line);
+ ElasticLength totalLength = new ElasticLength(footnotesProgress.getInserted());
+ totalLength.add(beforeFloatsProgress.getInserted());
+ if (mode == NORMAL_MODE) {
+ totalLength.add(normalProgress.insertedDims);
+ computeAdjustmentRatio(totalLength, pageBPD, MIN_NORMAL_PAGE_FILL_RATIO);
+ } else {
+ computeAdjustmentRatio(totalLength, pageBPD, MIN_FLOAT_PAGE_FILL_RATIO);
+ }
+ switch (mode) {
+ case NORMAL_MODE: {
+ int beforeFloatActualBPD = beforeFloatsProgress.getInserted().getLength();
+ if (adjustmentRatio < 0) {
+ beforeFloatActualBPD += beforeFloatsProgress.getInserted().getShrink()
+ * adjustmentRatio;
+ } else if (adjustmentRatio > 0) {
+ beforeFloatActualBPD += beforeFloatsProgress.getInserted().getStretch()
+ * adjustmentRatio;
+ }
+ if (((double) beforeFloatActualBPD) / pageBPD >= 1.0 - TEXT_FRACTION) {
+ // Not acceptable page, but if some further footnotes are
+ // added, may become feasible thanks to shrinking
+ double minBeforeFloatFraction
+ = ((double) (beforeFloatsProgress.getInserted().getLength()
+ - beforeFloatsProgress.getInserted().getShrink())) / pageBPD;
+ return adjustmentRatio > -1 && minBeforeFloatFraction < 1.0 - TEXT_FRACTION;
+ } else {
+ if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+ double demerits = computeDemerits(footnotesProgress,
+ beforeFloatsProgress,
+ normalProgress.position,
+ previousNode,
+ NORMAL_MODE);
+ if (demerits < best.getDemerits(fitnessClass)) {
+ ((BestPageRecords) best).addRecord(demerits,
+ previousNode,
+ adjustmentRatio,
+ totalLength.getShrink(),
+ totalLength.getStretch(),
+ difference, fitnessClass,
+ footnotesProgress, beforeFloatsProgress);
+ lastTooShort = null;
+ }
+ return true;
+ } else if (force) {
+ double demerits = computeDemerits(footnotesProgress,
+ beforeFloatsProgress,
+ normalProgress.position,
+ previousNode,
+ NORMAL_MODE);
+ sumsAfter.compute(normalProgress.position,
+ totalWidth, totalStretch, totalShrink);
+ if (adjustmentRatio < -1) {
+ if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+ lastTooLong = createNode(normalProgress.position,
+ previousNode.line + 1,
+ fitnessClass,
+ sumsAfter.getWidthAfter(),
+ sumsAfter.getStretchAfter(),
+ sumsAfter.getShrinkAfter(),
+ adjustmentRatio,
+ totalLength.getShrink(),
+ totalLength.getStretch(),
+ difference, demerits, previousNode);
+ }
+ return false;
+ } else {
+ if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+ if (considerTooShort) {
+ ((BestPageRecords) best).addRecord(demerits,
+ previousNode,
+ adjustmentRatio,
+ totalLength.getShrink(),
+ totalLength.getStretch(),
+ difference, fitnessClass,
+ footnotesProgress, beforeFloatsProgress);
+ }
+ if (log.isDebugEnabled()) {
+ NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
+ nf.setMaximumFractionDigits(2);
+ nf.setGroupingUsed(false);
+ log.debug("Registering too-short node [demerits="
+ + nf.format(demerits)
+ + ", adjRatio=" + nf.format(adjustmentRatio)
+ + ", page " + (previousNode.line + 1)
+ + "]");
+ }
+ lastTooShort = createNode(normalProgress.position,
+ previousNode.line + 1,
+ fitnessClass,
+ sumsAfter.getWidthAfter(),
+ sumsAfter.getStretchAfter(),
+ sumsAfter.getShrinkAfter(),
+ adjustmentRatio,
+ totalLength.getShrink(),
+ totalLength.getStretch(),
+ difference, demerits, previousNode);
+ }
+ return true;
+ }
+ }
+ return adjustmentRatio >= -1;
+ }
+ }
+ case FLOAT_PAGE_MODE:
+ if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+ double demerits = computeDemerits(footnotesProgress,
+ beforeFloatsProgress,
+ normalProgress.position,
+ previousNode,
+ NORMAL_MODE);
+ KnuthNode node = createNode(normalProgress.position,
+ previousNode.line + 1,
+ fitnessClass,
+ previousNode.totalWidth,
+ previousNode.totalStretch,
+ previousNode.totalShrink,
+ adjustmentRatio,
+ totalLength.getShrink(),
+ totalLength.getStretch(),
+ difference,
+ demerits,
+ previousNode);
+ registerActiveNode(node);
+ lastTooShort = null;
+ }
+ return adjustmentRatio >= -1;
+ default: // case FLUSH_MODE:
+ if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+ double demerits = computeDemerits(footnotesProgress,
+ beforeFloatsProgress,
+ par.size() - 1,
+ previousNode,
+ NORMAL_MODE);
+ KnuthNode node = createNode(par.size() - 1,
+ previousNode.line + 1,
+ fitnessClass,
+ 0, 0, 0,
+ adjustmentRatio,
+ totalLength.getShrink(), totalLength.getStretch(),
+ difference, demerits, previousNode);
+ queue.addLast(node);
+ lastTooShort = null;
+ return true;
+ } else if (force) {
+ double demerits = computeDemerits(footnotesProgress,
+ beforeFloatsProgress,
+ par.size() - 1,
+ previousNode,
+ NORMAL_MODE);
+ if (adjustmentRatio < -1) {
+ if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+ lastTooLong = createNode(par.size() - 1,
+ previousNode.line + 1,
+ fitnessClass,
+ 0, 0, 0,
+ adjustmentRatio,
+ totalLength.getShrink(), totalLength.getStretch(),
+ difference, demerits, previousNode);
+ }
+ return false;
+ } else {
+ if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+ lastTooShort = createNode(par.size() - 1,
+ previousNode.line + 1,
+ fitnessClass,
+ 0, 0, 0,
+ adjustmentRatio,
+ totalLength.getShrink(), totalLength.getStretch(),
+ difference, demerits, previousNode);
+ if (considerTooShort) {
+ queue.addLast(lastTooShort);
+ }
+ }
+ return true;
+ }
+ }
+ return adjustmentRatio >= -1;
+ }
+ }
+
+ /**
+ * When in flush mode, uses the given queue for registering new active nodes. TODO
+ * vh: highly temporary! As in flush mode the handling is a bit different,
+ * activeLines cannot be re-used. Will have to unify the handling of active nodes
+ * eventually.
+ *
+ * @param queue FIFO structure for registering active nodes in flush mode
+ */
+ void setQueue(LinkedList queue) {
+ this.queue = queue;
}
}
protected void initialize() {
super.initialize();
- footnotes.initialize();
- floats.initialize();
+ normalContentProgress.initialize(par);
+ footnotesRecord.initialize();
+ beforeFloatsRecord.initialize();
+ footnotesProgress.initialize();
+ beforeFloatsProgress.initialize();
+ activeNodeRecorder.setForce(force);
}
public KnuthNode createNode(int position, int line, int fitness,
int difference, double totalDemerits, KnuthNode previous) {
return new KnuthPageNode(position, line, fitness,
totalWidth, totalStretch, totalShrink,
- footnotes.getProgress(), floats.getProgress(),
+ footnotesProgress, beforeFloatsProgress,
adjustRatio, availableShrink, availableStretch,
difference, totalDemerits, previous);
}
protected void handleBox(KnuthBox box) {
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasFootnoteAnchors()) {
- footnotes.add(((KnuthBlockBox) box).getFootnoteElementLists());
+ footnotesRecord.add(((KnuthBlockBox) box).getFootnoteElementLists());
}
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasFloatAnchors()) {
- floats.add(((KnuthBlockBox) box).getFloatElementLists());
+ beforeFloatsRecord.add(((KnuthBlockBox) box).getFloatElementLists());
}
}
protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
int returnValue = super.restartFrom(restartingNode, currentIndex);
- footnotes.resetNewSinceLastBreakpoint();
- floats.resetNewSinceLastBreakpoint();
- if (footnotes.existing() || floats.existing()) {
+ if (footnotesRecord.existing() || beforeFloatsRecord.existing()) {
// remove from footnotesList the note lists that will be met
// after the restarting point
for (int j = currentIndex; j >= restartingNode.position; j--) {
- KnuthElement resettedElement = getElement(j);
- if (resettedElement instanceof KnuthBlockBox
- && ((KnuthBlockBox) resettedElement).hasFootnoteAnchors()) {
- footnotes.reset(((KnuthBlockBox) resettedElement).getFootnoteElementLists());
+ KnuthElement resetElement = getElement(j);
+ if (resetElement instanceof KnuthBlockBox
+ && ((KnuthBlockBox) resetElement).hasFootnoteAnchors()) {
+ footnotesRecord.reset(((KnuthBlockBox) resetElement).getFootnoteElementLists());
}
- if (resettedElement instanceof KnuthBlockBox
- && ((KnuthBlockBox) resettedElement).hasFloatAnchors()) {
- floats.reset(((KnuthBlockBox) resettedElement).getFloatElementLists());//TODO
+ if (resetElement instanceof KnuthBlockBox
+ && ((KnuthBlockBox) resetElement).hasFloatAnchors()) {
+ beforeFloatsRecord.reset(((KnuthBlockBox) resetElement).getFloatElementLists());
}
}
}
}
protected void considerLegalBreak(KnuthElement element, int elementIdx) {
- super.considerLegalBreak(element, elementIdx);
- footnotes.resetNewSinceLastBreakpoint();
- floats.resetNewSinceLastBreakpoint();
- }
- protected int computeDifference(KnuthNode activeNode, KnuthElement element,
- int elementIndex) {
- KnuthPageNode pageNode = (KnuthPageNode) activeNode;
- int actualWidth = totalWidth - pageNode.totalWidth;
- if (element.isPenalty()) {
- actualWidth += element.getW();
+ if (log.isTraceEnabled()) {
+ log.trace("considerLegalBreak() at " + elementIdx
+ + " (" + totalWidth + "+" + totalStretch + "-" + totalShrink
+ + "), parts/lines: " + startLine + "-" + endLine);
+ log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
}
- if (footnotes.existing()) {
- footnotes.setProgress(pageNode.footnotesProgress);
- // compute the total length of the footnotes not yet inserted
- int allFootnotes = footnotes.getTotalLength()
- - pageNode.footnotesProgress.getAlreadyInsertedLength();
- if (allFootnotes > 0) {
- // this page contains some footnote citations
- // add the footnote separator width
- actualWidth += footnotes.getSeparatorLength().opt;
- if (actualWidth + allFootnotes <= getLineWidth()) {
- // there is enough space to insert all footnotes:
- // add the whole allFootnotes length
- actualWidth += allFootnotes;
- footnotes.insertAll();
- } else {
- boolean canDeferOldFootnotes = checkCanDeferOldOutOfLines(footnotes,
- pageNode.position, elementIndex);
- int footnoteSplit;
- if ((canDeferOldFootnotes || footnotes.newSinceLastBreakpoint())
- && (footnoteSplit = footnotes.getFootnoteSplit(
- pageNode.footnotesProgress,
- getLineWidth() - actualWidth, canDeferOldFootnotes)) > 0) {
- // it is allowed to break or even defer footnotes if either:
- // - there are new footnotes in the last piece of content, and
- // there is space to add at least a piece of the first one
- // - or the previous page break deferred some footnote lines, and
- // this is the first feasible break; in this case it is allowed
- // to break and defer, if necessary, old and new footnotes
- actualWidth += footnoteSplit;
- } else {
- // there is no space to add the smallest piece of footnote,
- // or we are trying to add a piece of content with no footnotes and
- // it does not fit in the page, because of previous footnote bodies
- // that cannot be broken:
- // add the whole allFootnotes length, so this breakpoint will be discarded
- actualWidth += allFootnotes;
- footnotes.insertAll();
- }
+
+ lastDeactivated = null;
+ lastTooLong = null;
+ for (int line = startLine; line < endLine; line++) {
+ for (KnuthPageNode node = (KnuthPageNode) getNode(line);
+ node != null;
+ node = (KnuthPageNode) node.next) {
+ if (node.position == elementIdx) {
+ continue;
}
- } // else: all footnotes have already been placed on previous pages
- }
- if (floats.existing()) {
- floats.setProgress(pageNode.floatsProgress);
- // compute the total length of the floats not yet inserted
- int allFloats = floats.getTotalLength()
- - pageNode.floatsProgress.getAlreadyInsertedLength();
- if (allFloats > 0
- && getLineWidth() - actualWidth - floats.getSeparatorLength().opt > 0) {
- // this page contains some float citations
- // add the float separator width
- int split = floats.getFloatSplit(pageNode.floatsProgress,
- getLineWidth() - actualWidth - floats.getSeparatorLength().opt);
- if (split > 0) {
- actualWidth += floats.getSeparatorLength().opt + split;
+ footnotesProgress.setPrevious(node.footnotesProgress);
+ beforeFloatsProgress.setPrevious(node.beforeFloatsProgress);
+ footnotesProgress.handleSplit();
+ beforeFloatsProgress.handleSplit();
+ normalContentProgress.insertedDims.set(totalShrink - node.totalShrink,
+ totalWidth - node.totalWidth, totalStretch - node.totalStretch);
+ normalContentProgress.position = elementIdx;
+ if (element.isPenalty()) {
+ normalContentProgress.insertedDims.add(0, element.getW(), 0);
}
- }
- }
- /* Another algorithm exactly mimicing the handling of footnotes: it should force
- * more floats to be on the same page as their citations, at the price of more
- * underfull pages (thus a higher total number of pages). If the current method
- * works well enough, we may keep it.
- */
-// if (floats.existing()) {
-// floats.setProgress(pageNode.floatsProgress);
-// // compute the total length of the floats not yet inserted
-// int allFloats = floats.getTotalLength()
-// - pageNode.floatsProgress.getAlreadyInsertedLength();
-// if (allFloats > 0) {
-// // this page contains some float citations
-// // add the float separator width
-// actualWidth += floats.getSeparatorLength().opt;
-// if (actualWidth + allFloats <= getLineWidth()) {
-// // there is enough space to insert all floats:
-// // add the whole allFloats length
-// actualWidth += allFloats;
-// floats.insertAll();
-// } else {
-// boolean canDeferOldFloats = checkCanDeferOldOutOfLines(floats,
-// pageNode.position, elementIndex);
-// int floatSplit;
-// if ((canDeferOldFloats || floats.newSinceLastBreakpoint())
-// && (floatSplit = floats.getFloatSplit(
-// pageNode.floatsProgress,
-// getLineWidth() - actualWidth)) > 0) {
-// actualWidth += floatSplit;
-// } else {
-// actualWidth += allFloats;
-// floats.insertAll();
-// }
-// }
-// } // else: all floats have already been placed on previous pages
-// }
- return getLineWidth(activeNode.line) - actualWidth;
- }
-
- /**
- * Checks whether out-of-line objects from preceding pages may be deferred
- * to the page after the given element.
- *
- * @param outOfLine informations about the out-of-line objects
- * @param activeNodePosition index in the Knuth sequence of the currently considered
- * active node
- * @param contentElementIndex index in the Knuth sequence of the currently considered
- * legal breakpoint
- * @return <code>true</code> if it is allowed to defer some out-of-line objects on
- * following pages
- */
- private boolean checkCanDeferOldOutOfLines(OutOfLineRecord outOfLine,
- int activeNodePosition,
- int contentElementIndex) {
- return (noBreakBetween(activeNodePosition, contentElementIndex)
- && outOfLine.deferred());
- }
-
- /**
- * Returns true if there is no legal breakpoint between the two given elements.
- * @param prevBreakIndex index of the element from the currently considered active
- * node
- * @param breakIndex index of the currently considered breakpoint
- * @return true if no element between the two is a legal breakpoint
- */
- private boolean noBreakBetween(int prevBreakIndex, int breakIndex) {
- // this method stores the parameters and the return value from previous calls
- // in order to avoid scanning the element list unnecessarily:
- // - if there is no break between element #i and element #j
- // there will not be a break between #(i+h) and #j too
- // - if there is a break between element #i and element #j
- // there will be a break between #(i-h) and #(j+k) too
- if (storedPrevBreakIndex != -1
- && ((prevBreakIndex >= storedPrevBreakIndex
- && breakIndex == storedBreakIndex
- && storedValue)
- || (prevBreakIndex <= storedPrevBreakIndex
- && breakIndex >= storedBreakIndex
- && !storedValue))) {
- // use the stored value, do nothing
- } else {
- // compute the new value
- int index;
- // ignore suppressed elements
- for (index = prevBreakIndex + 1;
- !par.getElement(index).isBox();
- index++) {
- //nop
- }
- // find the next break
- for (;
- index < breakIndex;
- index++) {
- if (par.getElement(index).isGlue() && par.getElement(index - 1).isBox()
- || par.getElement(index).isPenalty()
- && ((KnuthElement) par.getElement(index)).getP() < KnuthElement.INFINITE) {
- // break found
- break;
+ boolean recorded = activeNodeRecorder.handleNode(NORMAL_MODE,
+ normalContentProgress, footnotesProgress, beforeFloatsProgress, node);
+ if (!recorded || element.isForcedBreak()) {
+ deactivateNode(node);
+ lastDeactivated = compareNodes(lastDeactivated, node);
+ }
+ if (recorded) {
+ footnotesProgress.consider(NORMAL_MODE, activeNodeRecorder,
+ normalContentProgress, beforeFloatsProgress, node);
}
- }
- // update stored parameters and value
- storedPrevBreakIndex = prevBreakIndex;
- storedBreakIndex = breakIndex;
- storedValue = (index == breakIndex);
- }
- return storedValue;
- }
- protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
- // compute the adjustment ratio
- if (difference > 0) {
- int maxAdjustment = totalStretch - activeNode.totalStretch;
- // add the footnote separator stretch if some footnote content will be added
- if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) {
- maxAdjustment += footnotes.getSeparatorLength().max - footnotes.getSeparatorLength().opt;
- }
- // add the float separator stretch if some float content will be added
- if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
- maxAdjustment += floats.getSeparatorLength().max - floats.getSeparatorLength().opt;
- }
- if (maxAdjustment > 0) {
- return (double) difference / maxAdjustment;
- } else {
- return INFINITE_RATIO;
- }
- } else if (difference < 0) {
- int maxAdjustment = totalShrink - activeNode.totalShrink;
- // add the footnote separator shrink if some footnote content will be added
- if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) {
- maxAdjustment += footnotes.getSeparatorLength().opt - footnotes.getSeparatorLength().min;
}
- // add the float separator shrink if some float content will be added
- if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
- maxAdjustment += floats.getSeparatorLength().opt - floats.getSeparatorLength().min;
- }
- if (maxAdjustment > 0) {
- return (double) difference / maxAdjustment;
- } else {
- return -INFINITE_RATIO;
- }
- } else {
- return 0;
+ addBreaks(line, elementIdx);
}
}
- protected double computeDemerits(KnuthNode activeNode, KnuthElement element,
- int fitnessClass, double r) {
- double demerits = 0;
- // compute demerits
- double f = Math.abs(r);
- /* If the adjustment ratio is too high, the demerits will be "almost infinite"
- * (10^22). Adding demerits for a deferred float (10000) thus won't change the
- * demerits value. We may end up with two breakpoints with the same demerits,
- * whereas in one case there are deferred floats and not in the other case. The
- * case with no deferred floats is still preferable, so we must have the
- * possibility to distinguish it. By forcing f to 1 it becomes possible to make
- * the difference when there are deferred floats.
- * TODO vh: use threshold instead of 1 (currently threshold == 1 but it might be
- * configurable)
- */
- if (f > 1) {
- f = 1;
- }
- f = 1 + 100 * f * f * f;
- if (element.isPenalty() && element.getP() >= 0) {
- f += element.getP();
- demerits = f * f;
- } else if (element.isPenalty() && !element.isForcedBreak()) {
- double penalty = element.getP();
- demerits = f * f - penalty * penalty;
- } else {
- demerits = f * f;
- }
+ // TODO vh: refactor
+ // It may happen that several successive float-only pages are created; in such cases
+ // the progress informations must be saved as they'll still be used after this method
+ // call
+ private Stack footnotesStack = new Stack();
- if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
- && getElement(activeNode.position).isPenalty()
- && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
- // add demerit for consecutive breaks at flagged penalties
- demerits += repeatedFlaggedDemerit;
- }
- if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
- // add demerit for consecutive breaks
- // with very different fitness classes
- demerits += incompatibleFitnessDemerit;
- }
+ private Stack beforeFloatsStack = new Stack();
- if (footnotes.existing()) {
- demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits;
- if (footnotes.isSplit()) {
- demerits += splitFootnoteDemerits;
- }
- }
- if (floats.existing()) {
- demerits += floats.getNbOfDeferred() * deferredFloatDemerits;
+ public void registerActiveNode(KnuthNode node) {
+ super.registerActiveNode(node);
+ KnuthPageNode pageNode = (KnuthPageNode) node;
+ if (pageNode.position < par.size() - 1
+ && (pageNode.footnotesProgress.remaining() || pageNode.beforeFloatsProgress.remaining())
+ && FLOAT_PAGES_ALLOWED) {
+ footnotesStack.push(footnotesProgress);
+ footnotesProgress = footnotesRecord.new FootnotesProgress();
+ beforeFloatsStack.push(beforeFloatsProgress);
+ beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress();
+
+ footnotesProgress.setPrevious(pageNode.footnotesProgress);
+ beforeFloatsProgress.setPrevious(pageNode.beforeFloatsProgress);
+ footnotesProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder,
+ normalContentProgress, beforeFloatsProgress, pageNode);
+ beforeFloatsProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder,
+ normalContentProgress, footnotesProgress, pageNode);
+ footnotesProgress.consider(FLOAT_PAGE_MODE, activeNodeRecorder,
+ normalContentProgress, beforeFloatsProgress, pageNode);
+
+ footnotesProgress = (FootnotesProgress) footnotesStack.pop();
+ beforeFloatsProgress = (BeforeFloatsProgress) beforeFloatsStack.pop();
}
- demerits += activeNode.totalDemerits;
- return demerits;
}
+ // TODO vh: refactor
protected void finish() {
- for (int i = startLine; i < endLine; i++) {
- for (KnuthPageNode node = (KnuthPageNode) getNode(i);
- node != null;
- node = (KnuthPageNode) node.next) {
- if (node.footnotesProgress.getAlreadyInsertedLength()
- < footnotes.getTotalLength()) {
- // layout remaining footnote bodies
- footnotes.createFootnotePages(node, this, getLineWidth());
+ lastTooShort = null;
+ lastTooLong = null;
+ Vector bestNodes = new Vector();
+ int startIndex = startLine;
+ int endIndex = endLine;
+ bestNodes.setSize(endIndex - startIndex);
+ // Declared as a LinkedList as we need access to the queue-like methods defined
+ // in LinkedList
+ LinkedList queue = new LinkedList();
+ activeNodeRecorder.setQueue(queue);
+ Object n = null;
+ do {
+ for (int i = startIndex; i < endIndex; i++) {
+ for (KnuthPageNode node = (KnuthPageNode) getNode(i);
+ node != null;
+ node = (KnuthPageNode) node.next) {
+ deactivateNode(node);
+ if (node.footnotesProgress.remaining()
+ || node.beforeFloatsProgress.remaining()) {
+ queue.addFirst(node);
+ flush(queue, bestNodes, startIndex);
+ } else {
+ if (bestNodes.get(i - startIndex) == null
+ || node.totalDemerits < ((KnuthNode) bestNodes.get(i - startIndex)).totalDemerits) {
+ bestNodes.set(i - startIndex, node);
+ }
+ }
}
- if (node.floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
- // layout remaining float bodies
- floats.createFloatPages(node, this, getLineWidth());
+ }
+ Iterator iter = bestNodes.iterator();
+ while (iter.hasNext() && (n = iter.next()) == null);
+ if (n == null) {
+ log.debug("Recovering");
+ KnuthNode recovered = null;
+ if (lastTooShort == null) {
+ if (lastTooLong == null) {
+ log.debug("Both lastTooShort and lastTooLong null!");
+ } else {
+ recovered = lastTooLong;
+ }
+ } else {
+ recovered = lastTooShort;
+ }
+ recovered.totalDemerits = 0;
+ registerActiveNode(recovered);
+ startIndex = recovered.line;
+ endIndex = startIndex + 1;
+ lastTooLong = null;
+ lastTooShort = null;
+ }
+ } while (n == null);
+ Iterator nodeIter = bestNodes.iterator();
+ while (nodeIter.hasNext()) {
+ KnuthNode node = (KnuthNode) nodeIter.next();
+ if (node != null) { // TODO
+ int tmp = endLine;
+ registerActiveNode(node);
+ if (startLine > node.line) {
+ startLine = node.line;
+ }
+ if (endLine < tmp) {
+ endLine = tmp;
}
}
}
}
+ // TODO vh: refactor
+ private void flush(LinkedList queue, Vector bestNodes, int startIndex) {
+ do {
+ KnuthPageNode node = (KnuthPageNode) queue.removeFirst();
+ if (node.footnotesProgress.remaining() || node.beforeFloatsProgress.remaining()) {
+ footnotesProgress.setPrevious(node.footnotesProgress);
+ beforeFloatsProgress.setPrevious(node.beforeFloatsProgress);
+ footnotesProgress.handleSplit(FLUSH_MODE, activeNodeRecorder,
+ null, beforeFloatsProgress, node);
+ beforeFloatsProgress.handleSplit(FLUSH_MODE, activeNodeRecorder,
+ null, footnotesProgress, node);
+ footnotesProgress.consider(FLUSH_MODE, activeNodeRecorder,
+ null, beforeFloatsProgress, node);
+ } else {
+ int index = node.line - startIndex;
+ if (index >= bestNodes.size()) {
+ bestNodes.setSize(index + 1);
+ }
+ if (bestNodes.get(index) == null
+ || node.totalDemerits < ((KnuthNode) bestNodes.get(index)).totalDemerits) {
+ bestNodes.set(index, node);
+ }
+ }
+ } while (!queue.isEmpty());
+ }
+
/**
* @return a list of PageBreakPosition elements
*/
// ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
int difference = bestActiveNode.difference;
if (difference + bestActiveNode.availableShrink < 0) {
- if (!autoHeight && log.isWarnEnabled()) {
- log.warn(FONode.decorateWithContextInfo(
- "Part/page " + (getPartCount() + 1)
- + " overflows the available area in block-progression dimension.",
- getFObj()));
+ if (!autoHeight) {
+ if (layoutListener != null) {
+ layoutListener.notifyOverflow(bestActiveNode.line - 1, getFObj());
+ } else if (log.isWarnEnabled()) {
+ log.warn(FONode.decorateWithContextInfo(
+ "Part/page " + (bestActiveNode.line - 1)
+ + " overflows the available area in block-progression dimension.",
+ getFObj()));
+ }
}
}
boolean isNonLastPage = (bestActiveNode.line < total);
if (firstFootnoteListIndex == -1) {
firstFootnoteListIndex++;
firstFootnoteElementIndex = 0;
- } else if (footnotes.getSequence(firstFootnoteListIndex) != null
- && firstFootnoteElementIndex == ((LinkedList) footnotes.
+ } else if (footnotesRecord.getSequence(firstFootnoteListIndex) != null
+ && firstFootnoteElementIndex == ((LinkedList) footnotesRecord.
getSequence(firstFootnoteListIndex)).size() - 1) {
// advance to the next list
firstFootnoteListIndex++;
}
// compute the indexes of the first float list
int firstFloatListIndex = ((KnuthPageNode) bestActiveNode.previous).
- floatsProgress.getLastInsertedIndex() + 1;
+ beforeFloatsProgress.getLastInsertedIndex() + 1;
// add nodes at the beginning of the list, as they are found
// backwards, from the last one to the first one
((KnuthPageNode) bestActiveNode).footnotesProgress.
getLastElementOfLastInsertedIndex(),
firstFloatListIndex,
- ((KnuthPageNode) bestActiveNode).floatsProgress.getLastInsertedIndex(),
+ ((KnuthPageNode) bestActiveNode).beforeFloatsProgress.getLastInsertedIndex(),
ratio, difference));
}
bestActiveNode = compareNodes(bestActiveNode, node);
}
if (node != bestActiveNode) {
- removeNode(i, node);
+ deactivateNode(node);
}
}
}
}
public LinkedList getFootnoteList(int index) {
- return (LinkedList) footnotes.getSequence(index);
+ return (LinkedList) footnotesRecord.getSequence(index);
}
public LinkedList getFloatList(int index) {
- return (LinkedList) floats.getSequence(index);
+ return (LinkedList) beforeFloatsRecord.getSequence(index);
}
/** @return the associated top-level formatting object. */
return bpd;
}
+ /**
+ * Interface to notify about layout events during page breaking.
+ */
+ public interface PageBreakingLayoutListener {
+
+ /**
+ * Issued when an overflow is detected
+ * @param part the number of the part (page) this happens on
+ * @param obj the root FO object where this happens
+ */
+ void notifyOverflow(int part, FObj obj);
+
+ }
+
}
import org.apache.fop.area.Resolvable;
import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.RetrieveMarker;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
import org.apache.fop.traits.MinOptMax;
(currentPageNum - startPageNum) + 1);
areaTreeHandler.notifyPageSequenceFinished(pageSeq,
(currentPageNum - startPageNum) + 1);
+ pageSeq.releasePageSequence();
log.debug("Ending layout");
}
context.setRefIPD(flowIPD);
}
+ /** @see org.apache.fop.layoutmgr.AbstractBreaker#getTopLevelLM() */
protected LayoutManager getTopLevelLM() {
- return null; // unneeded for PSLM
+ return pslm;
}
/** @see org.apache.fop.layoutmgr.AbstractBreaker#getPageProvider() */
return pageProvider;
}
+ /**
+ * @see org.apache.fop.layoutmgr.AbstractBreaker#getLayoutListener()
+ */
+ protected PageBreakingLayoutListener getLayoutListener() {
+ return new PageBreakingLayoutListener() {
+
+ public void notifyOverflow(int part, FObj obj) {
+ Page p = pageProvider.getPage(
+ false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST);
+ RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
+ Region.FO_REGION_BODY);
+ String err = FONode.decorateWithContextInfo(
+ "Content of the region-body on page "
+ + p.getPageViewport().getPageNumberString()
+ + " overflows the available area in block-progression dimension.",
+ obj);
+ if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) {
+ throw new RuntimeException(err);
+ } else {
+ PageSequenceLayoutManager.log.warn(err);
+ }
+ }
+
+ };
+ }
+
/** @see org.apache.fop.layoutmgr.AbstractBreaker */
protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
needColumnBalancing = false;
//Restart last page
PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(),
+ getPageProvider(), getLayoutListener(),
alg.getAlignment(), alg.getAlignmentLast(),
footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(), false, false);
//Restart last page
PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(),
+ getPageProvider(), getLayoutListener(),
alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(),
getCurrentPV().getBodyRegion().getColumnCount());
* Resolves unresolved elements applying the space resolution rules defined in 4.3.1.
* @param elems the element list
*/
- public static void resolveElementList(LinkedList elems) {
+ public static void resolveElementList(List elems) {
if (log.isTraceEnabled()) {
log.trace(elems);
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.breaking;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm;
+import org.apache.fop.layoutmgr.SpaceResolver;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * A class that handles the placement of before-floats. Stores informations about
+ * existing before-floats, already placed ones, split floats, etc.
+ */
+public class BeforeFloatsRecord extends OutOfLineRecord {
+
+ /**
+ * Dimensions of the recorded before-floats. As the whole dimensions of floats are
+ * often required to determine placements, they are computed only once and stored in a
+ * dedicated field.
+ */
+ private List elasticLengths = new ArrayList();
+
+ /**
+ * Progress informations for before-floats. Among other things, this class handles the
+ * splitting of floats over several pages.
+ */
+ public class BeforeFloatsProgress extends ProgressInfo {
+
+ /**
+ * Creates and initializes a new record.
+ */
+ public BeforeFloatsProgress() {
+ super();
+ }
+
+ /**
+ * Creates a copy of the given record.
+ *
+ * @param beforeFloatProgress original progress information
+ */
+ public BeforeFloatsProgress(BeforeFloatsProgress beforeFloatProgress) {
+ super(beforeFloatProgress);
+ }
+
+ /**
+ * Checks whether there is still an entire float which has not been handled.
+ *
+ * @return <code>true</code> if there is still at least one entire non-handled
+ * float
+ */
+ private boolean hasNextFull() {
+ return lastInsertedIndex < knuthSequences.size() - 1;
+ }
+
+ /**
+ * Places on the current page the next entire float, without considering to split
+ * it.
+ */
+ private void nextFull() {
+ lastInsertedIndex++;
+ lastElementOfLastInsertedIndex
+ = ((List) knuthSequences.get(lastInsertedIndex)).size() - 1;
+ alreadyInserted.add((ElasticLength) elasticLengths.get(lastInsertedIndex));
+ }
+
+ /**
+ * If the float on the previous page was split, put the rest of it on the current
+ * page. This method is only called when typesetting a page which also has normal
+ * content. If the float on the previous page was split, this implies that this
+ * was a float-only page with only one float (no normal content, no footnote, no
+ * other float). This is the only case where a before-float is allowed to be
+ * split.
+ */
+ public void handleSplit() {
+ if (isLastSplit()) {
+ do {
+ nextInsideBreak();
+ } while (!endOfOutOfLine());
+ addSeparator();
+ }
+ }
+
+ /**
+ * If the float on the previous page was split, put the rest of it on the current
+ * page. This method is called when building a float-only page, either inside a
+ * page sequence or at the end of it.
+ *
+ * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ * @param activeNodeRecorder
+ * @param normalContentProgress information about normal content already typeset
+ * @param footnotesProgress information about footnotes already typeset
+ * @param previousNode node ending the previous page
+ */
+ public void handleSplit(int mode,
+ PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+ PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+ FootnotesRecord.FootnotesProgress footnotesProgress,
+ KnuthPageNode previousNode) {
+ if (isLastSplit()) {
+ do {
+ nextInsideBreak();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ footnotesProgress, this, previousNode)) {
+ break;
+ }
+ } while (!endOfOutOfLine());
+ }
+ }
+
+ /**
+ * Considers the placement of floats on the current page.
+ *
+ * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+ * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ * @param activeNodeRecorder
+ * @param normalContentProgress information about normal content already typeset
+ * @param footnotesProgress information about footnotes already typeset
+ * @param previousNode node ending the previous page
+ */
+ public void consider(int mode,
+ PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+ PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+ FootnotesRecord.FootnotesProgress footnotesProgress,
+ KnuthPageNode previousNode) {
+ setPrevious(previousNode.beforeFloatsProgress);
+ switch (mode) {
+ case PageBreakingAlgorithm.NORMAL_MODE:
+ if (alreadyInserted.getLength() == 0) {
+ addSeparator();
+ }
+ while (hasNextFull()) {
+ nextFull();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ footnotesProgress, this, previousNode)) {
+ break;
+ }
+ }
+ break;
+ case PageBreakingAlgorithm.FLOAT_PAGE_MODE:
+ case PageBreakingAlgorithm.FLUSH_MODE:
+ if (footnotesProgress.alreadyInserted.getLength() == 0
+ && alreadyInserted.getLength() == 0
+ && hasNextFull()) { // first split allowed
+ lastInsertedIndex++;
+ lastElementOfLastInsertedIndex = -1;
+ do {
+ nextInsideBreak();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ footnotesProgress, this, previousNode)) {
+ break;
+ }
+ } while (!endOfOutOfLine());
+ }
+ while (hasNextFull()) {
+ nextFull();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ footnotesProgress, this, previousNode)) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates a new record for handling before-floats.
+ *
+ * @param separator dimensions of the separator between the before-float area
+ * and the normal content
+ */
+ public BeforeFloatsRecord(MinOptMax separator) {
+ super(separator);
+ }
+
+ public void initialize() {
+ super.initialize();
+ elasticLengths = new ArrayList();
+ }
+
+ public void add(List elementLists) {
+ // compute the total length of the footnotes
+ ListIterator elementListsIterator = elementLists.listIterator();
+ while (elementListsIterator.hasNext()) {
+ List knuthSequence = (List) elementListsIterator.next();
+
+ //Space resolution (Note: this does not respect possible stacking constraints
+ //between footnotes!)
+ SpaceResolver.resolveElementList(knuthSequence);
+
+ knuthSequences.add(knuthSequence);
+ Iterator elementIter = knuthSequence.iterator();
+ ElasticLength floatDims = new ElasticLength();
+ while (elementIter.hasNext()) {
+ KnuthElement element = (KnuthElement) elementIter.next();
+ if (element.isBox()) {
+ floatDims.add(0, element.getW(), 0);
+ } else if (element.isGlue()) {
+ floatDims.add(element.getZ(), element.getW(), element.getY());
+ }
+ }
+ elasticLengths.add(floatDims);
+ }
+ }
+
+ public void reset(List elementLists) {
+ super.reset(elementLists);
+ for (int i = 0; i < elementLists.size(); i++) {
+ elasticLengths.remove(elasticLengths.size() - 1);
+ }
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.breaking;
+
+/**
+ * A length with three components: the natural value, the amount of authorized shrink, the
+ * amount of authorized stretch.
+ */
+public class ElasticLength {
+
+ private int shrink;
+
+ private int length;
+
+ private int stretch;
+
+ /**
+ * Creates a new length of 0, with 0 shrink and 0 stretch.
+ */
+ public ElasticLength() {
+ shrink = 0;
+ length = 0;
+ stretch = 0;
+ }
+
+ /**
+ * Creates a copy of the given length.
+ *
+ * @param el an elastic length
+ */
+ public ElasticLength(ElasticLength el) {
+ shrink = el.shrink;
+ length = el.length;
+ stretch = el.stretch;
+ }
+
+ /**
+ * Creates a new length with the given components.
+ *
+ * @param shrink amount of authorized shrink
+ * @param length natural length
+ * @param stretch amount of authorized stretch
+ */
+ public ElasticLength(int shrink, int length, int stretch) {
+ set(shrink, length, stretch);
+ }
+
+ /**
+ * Returns the amount by which this length may be shrinked.
+ *
+ * @return the amount by which this length may be shrinked.
+ */
+ public int getShrink() {
+ return shrink;
+ }
+
+ /**
+ * Returns the natural value of this length.
+ *
+ * @return the natural value of this length
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Returns the amount by which this length may be stretched.
+ *
+ * @return the amount by which this length may be stretched.
+ */
+ public int getStretch() {
+ return stretch;
+ }
+
+ /**
+ * Resets the three components of this length to 0.
+ */
+ public void reset() {
+ set(0, 0, 0);
+ }
+
+ /**
+ * Sets the components of this length to the given values.
+ *
+ * @param shrink authorized shrink
+ * @param length natural length
+ * @param stretch authorized stretch
+ */
+ public void set(int shrink, int length, int stretch) {
+ this.shrink = shrink;
+ this.length = length;
+ this.stretch = stretch;
+ }
+
+ /**
+ * Sets the components of this length to those of the given length.
+ *
+ * @param elasticLength a length
+ */
+ public void set(ElasticLength elasticLength) {
+ set(elasticLength.shrink, elasticLength.length, elasticLength.stretch);
+ }
+
+ /**
+ * Adds the given values to the components of this length.
+ *
+ * @param shrink additional authorized shrink
+ * @param length additional natural length
+ * @param stretch additional authorized stretch
+ */
+ public void add(int shrink, int length, int stretch) {
+ this.shrink += shrink;
+ this.length += length;
+ this.stretch += stretch;
+ }
+
+ /**
+ * Adds to the components of this length those of the given length.
+ *
+ * @param elasticLength a length of which each component will be added to this ones'
+ */
+ public void add(ElasticLength elasticLength) {
+ add(elasticLength.shrink, elasticLength.length, elasticLength.stretch);
+ }
+
+ public String toString() {
+ return "[" + shrink + "," + length + "," + stretch + "]";
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.breaking;
+
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * A class that handles the placement of footnotes. Stores informations about
+ * existing footnotes, already placed ones, split footnotes, etc.
+ */
+public class FootnotesRecord extends OutOfLineRecord {
+
+ /**
+ * Progress informations for footnotes. When building pages, footnotes content will
+ * be put one unbreakable part at a time.
+ */
+ public class FootnotesProgress extends ProgressInfo {
+
+ /**
+ * Creates and initializes a new record.
+ */
+ public FootnotesProgress() {
+ super();
+ }
+
+ /**
+ * Creates a copy of the given record.
+ *
+ * @param footnotesProgress original progress information
+ */
+ public FootnotesProgress(FootnotesProgress footnotesProgress) {
+ super(footnotesProgress);
+ }
+
+ /**
+ * Checks whether there is some not yet typeset footnote content.
+ *
+ * @return <code>true</code> if the end of the footnote element list has not been
+ * reached yet
+ */
+ private boolean hasNext() {
+ return knuthSequences.size() > 0
+ && (lastInsertedIndex < knuthSequences.size() - 1 || !endOfOutOfLine());
+ }
+
+ /**
+ * Insert footnote content on the current page up to the next legal break. This
+ * may be within a footnote or at the end of one.
+ */
+ private void next() {
+ if (lastInsertedIndex < 0 || endOfOutOfLine()) {
+ // Go to the next footnote
+ lastInsertedIndex++;
+ lastElementOfLastInsertedIndex = -1;
+ nbSplit = 1;
+ } else { // We are still inside a footnote
+ nextInsideBreak();
+ }
+ }
+
+ /**
+ * If the last footnote of the previous page was split, places at least one more
+ * chunk of it on the current page. This would look very weird if the rest of a
+ * footnote split on one page would appear only two pages further. So this is
+ * necessary to put at least one chunk on the current page. If this leads to an
+ * unfeasible page, then the previous page will never appear in the optimal page
+ * layout anyway.
+ */
+ public void handleSplit() {
+ if (isLastSplit()) {
+ next();
+ addSeparator();
+ }
+ }
+
+ /**
+ * If the current page is a float-only page, handles the splitting of the last
+ * footnote of the previous page. Usually by adding at least a chunk of it on the
+ * current page, unless footnotes are not allowed on float-only pages (TODO this
+ * may lead to weird results (footnote continued only two pages further)).
+ *
+ * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ * @param activeNodeRecorder
+ * @param normalContentProgress information about normal content already typeset
+ * @param beforeFloatsProgress information about before-floats already typeset
+ * @param previousNode node ending the previous page
+ */
+ public void handleSplit(int mode,
+ PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+ PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+ BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+ KnuthPageNode previousNode) {
+ if (isLastSplit()) {
+ if (mode == PageBreakingAlgorithm.FLOAT_PAGE_MODE) {
+ if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) {
+ next();
+ addSeparator();
+ if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) {
+ activeNodeRecorder.handleNode(mode, normalContentProgress,
+ this, beforeFloatsProgress, previousNode);
+ }
+ }
+ } else { // mode == PageBreakingAlgorithm.FLUSH_MODE
+ next();
+ addSeparator();
+ activeNodeRecorder.handleNode(mode, normalContentProgress,
+ this, beforeFloatsProgress, previousNode);
+ }
+ }
+ }
+
+ /**
+ * Considers the placement of footnotes on the current page.
+ *
+ * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+ * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+ * PageBreakingAlgorithm#FLUSH_MODE}
+ * @param activeNodeRecorder
+ * @param normalContentProgress information about normal content already typeset
+ * @param beforeFloatsProgress information about before-floats already typeset
+ * @param previousNode node ending the previous page
+ */
+ public void consider(int mode,
+ PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+ PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+ BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+ KnuthPageNode previousNode) {
+ beforeFloatsProgress.consider(mode, activeNodeRecorder,
+ normalContentProgress, this, previousNode);
+ if (alreadyInserted.getLength() == 0) {
+ addSeparator();
+ }
+ switch (mode) {
+ case PageBreakingAlgorithm.NORMAL_MODE:
+ while (hasNext()) {
+ next();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ this, beforeFloatsProgress, previousNode)) {
+ break;
+ }
+ beforeFloatsProgress.consider(mode, activeNodeRecorder,
+ normalContentProgress, this, previousNode);
+ }
+ break;
+ case PageBreakingAlgorithm.FLOAT_PAGE_MODE:
+ if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) {
+ while (hasNext()) {
+ next();
+ if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) {
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ this, beforeFloatsProgress, previousNode)) {
+ break;
+ }
+ }
+ beforeFloatsProgress.consider(mode, activeNodeRecorder,
+ normalContentProgress, this, previousNode);
+ }
+ }
+ break;
+ case PageBreakingAlgorithm.FLUSH_MODE:
+ while (hasNext()) {
+ next();
+ if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+ this, beforeFloatsProgress, previousNode)) {
+ break;
+ }
+ beforeFloatsProgress.consider(mode, activeNodeRecorder,
+ normalContentProgress, this, previousNode);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates a new record for handling footnotes.
+ *
+ * @param footnoteSeparator dimensions of the separator between the normal content and
+ * the footnote area
+ */
+ public FootnotesRecord(MinOptMax footnoteSeparator) {
+ super(footnoteSeparator);
+ }
+}
--- /dev/null
+package org.apache.fop.layoutmgr.breaking;
+
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.LeafPosition;
+
+/**
+ * Private class to store information about inline breaks.
+ * Each value holds the start and end indexes into a List of
+ * inline break positions.
+ */
+public class LineBreakPosition extends LeafPosition {
+ /*
+ * TODO vh: fields temporarily made public to ease the moving of
+ * LineBreakPosition from a LineLayoutManager inner class to a top-level
+ * class.
+ */
+ public int iParIndex; // index of the Paragraph this Position refers to
+ public int iStartIndex; //index of the first element this Position refers to
+ public int availableShrink;
+ public int availableStretch;
+ public int difference;
+ public double dAdjust; // Percentage to adjust (stretch or shrink)
+ public double ipdAdjust; // Percentage to adjust (stretch or shrink)
+ public int startIndent;
+ public int lineHeight;
+ public int lineWidth;
+ public int spaceBefore;
+ public int spaceAfter;
+ public int baseline;
+
+ LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
+ int shrink, int stretch, int diff,
+ double ipdA, double adjust, int ind,
+ int lh, int lw, int sb, int sa, int bl) {
+ super(lm, iBreakIndex);
+ availableShrink = shrink;
+ availableStretch = stretch;
+ difference = diff;
+ iParIndex = index;
+ this.iStartIndex = iStartIndex;
+ ipdAdjust = ipdA;
+ dAdjust = adjust;
+ startIndent = ind;
+ lineHeight = lh;
+ lineWidth = lw;
+ spaceBefore = sb;
+ spaceAfter = sa;
+ baseline = bl;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.layoutmgr.breaking;
+
+import java.util.ListIterator;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.layoutmgr.BreakingAlgorithm;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthSequence;
+import org.apache.fop.layoutmgr.inline.AlignmentContext;
+import org.apache.fop.layoutmgr.inline.KnuthInlineBox;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager.Paragraph;
+
+public class LineBreakingAlgorithm extends BreakingAlgorithm {
+ private LineLayoutManager thisLLM;
+ private int pageAlignment;
+ private int activePossibility;
+ private int addedPositions;
+ private int textIndent;
+ private int fillerMinWidth;
+ private int lineHeight;
+ private int lead;
+ private int follow;
+// private int maxDiff;
+ private static final double MAX_DEMERITS = 10e6;
+
+ public LineBreakingAlgorithm (int pageAlign,
+ int textAlign, int textAlignLast,
+ int indent, int fillerWidth,
+ int lh, int ld, int fl, boolean first,
+ int maxFlagCount, LineLayoutManager llm) {
+ super(textAlign, textAlignLast, first, false, maxFlagCount);
+ pageAlignment = pageAlign;
+ textIndent = indent;
+ fillerMinWidth = fillerWidth;
+ lineHeight = lh;
+ lead = ld;
+ follow = fl;
+ thisLLM = llm;
+ activePossibility = -1;
+// maxDiff = fobj.getWidows() >= fobj.getOrphans()
+// ? fobj.getWidows()
+// : fobj.getOrphans();
+ }
+
+ public void updateData1(int lineCount, double demerits) {
+ thisLLM.getLineLayouts().addPossibility(lineCount, demerits);
+ log.trace("Layout possibility in " + lineCount + " lines; break at position:");
+ }
+
+ public void updateData2(KnuthNode bestActiveNode,
+ KnuthSequence par,
+ int total) {
+ // compute indent and adjustment ratio, according to
+ // the value of text-align and text-align-last
+ int indent = 0;
+ int difference = bestActiveNode.difference;
+ int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+ indent += (textAlign == Constants.EN_CENTER)
+ ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
+ indent += (bestActiveNode.line == 1 && bFirst && thisLLM.isFirstInBlock()) ? textIndent : 0;
+ double ratio = (textAlign == Constants.EN_JUSTIFY
+ || difference < 0 && -difference <= bestActiveNode.availableShrink)
+ ? bestActiveNode.adjustRatio : 0;
+
+ // add nodes at the beginning of the list, as they are found
+ // backwards, from the last one to the first one
+
+ // the first time this method is called, initialize activePossibility
+ if (activePossibility == -1) {
+ activePossibility = 0;
+ addedPositions = 0;
+ }
+
+ if (addedPositions == thisLLM.getLineLayouts().getLineCount(activePossibility)) {
+ activePossibility++;
+ addedPositions = 0;
+ }
+
+ if (difference + bestActiveNode.availableShrink < 0) {
+ if (log.isWarnEnabled()) {
+ log.warn(FONode.decorateWithContextInfo(
+ "Line " + (addedPositions + 1)
+ + " of a paragraph overflows the available area.", thisLLM.getFObj()));
+ }
+ }
+
+ //log.debug("LLM> (" + (thisLLM.getLineLayouts().getLineNumber(activePossibility) - addedPositions)
+ // + ") difference = " + difference + " ratio = " + ratio);
+ thisLLM.getLineLayouts().addBreakPosition(makeLineBreakPosition(par,
+ (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
+ bestActiveNode.position,
+ bestActiveNode.availableShrink - (addedPositions > 0
+ ? 0 : ((Paragraph)par).getLineFiller().opt - ((Paragraph)par).getLineFiller().min),
+ bestActiveNode.availableStretch,
+ difference, ratio, indent), activePossibility);
+ addedPositions++;
+ }
+
+ /* reset activePossibility, as if breakpoints have not yet been computed
+ */
+ public void resetAlgorithm() {
+ activePossibility = -1;
+ }
+
+ private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
+ int firstElementIndex,
+ int lastElementIndex,
+ int availableShrink,
+ int availableStretch,
+ int difference,
+ double ratio,
+ int indent) {
+ // line height calculation - spaceBefore may differ from spaceAfter
+ // by 1mpt due to rounding
+ int spaceBefore = (lineHeight - lead - follow) / 2;
+ int spaceAfter = lineHeight - lead - follow - spaceBefore;
+ // height before the main baseline
+ int lineLead = lead;
+ // maximum follow
+ int lineFollow = follow;
+ // true if this line contains only zero-height, auxiliary boxes
+ // and the actual line width is 0; in this case, the line "collapses"
+ // i.e. the line area will have bpd = 0
+ boolean bZeroHeightLine = (difference == thisLLM.getLineWidth());
+
+ // if line-stacking-strategy is "font-height", the line height
+ // is not affected by its content
+ if (((Block) thisLLM.getFObj()).getLineStackingStrategy() != Constants.EN_FONT_HEIGHT) {
+ ListIterator inlineIterator
+ = par.listIterator(firstElementIndex);
+ AlignmentContext lastAC = null;
+ int maxIgnoredHeight = 0; // See spec 7.13
+ for (int j = firstElementIndex;
+ j <= lastElementIndex;
+ j++) {
+ KnuthElement element = (KnuthElement) inlineIterator.next();
+ if (element instanceof KnuthInlineBox ) {
+ AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
+ if (ac != null && lastAC != ac) {
+ if (!ac.usesInitialBaselineTable()
+ || ac.getAlignmentBaselineIdentifier() != Constants.EN_BEFORE_EDGE
+ && ac.getAlignmentBaselineIdentifier() != Constants.EN_AFTER_EDGE) {
+ int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
+ if (alignmentOffset + ac.getAltitude() > lineLead) {
+ lineLead = alignmentOffset + ac.getAltitude();
+ }
+ if (ac.getDepth() - alignmentOffset > lineFollow) {
+ lineFollow = ac.getDepth() - alignmentOffset;
+ }
+ } else {
+ if (ac.getHeight() > maxIgnoredHeight) {
+ maxIgnoredHeight = ac.getHeight();
+ }
+ }
+ lastAC = ac;
+ }
+ if (bZeroHeightLine
+ && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
+ bZeroHeightLine = false;
+ }
+ }
+ }
+
+ if (lineFollow < maxIgnoredHeight - lineLead) {
+ lineFollow = maxIgnoredHeight - lineLead;
+ }
+ }
+
+ thisLLM.setConstantLineHeight(lineLead + lineFollow);
+
+ if (bZeroHeightLine) {
+ return new LineBreakPosition(thisLLM,
+ thisLLM.getKnuthParagraphs().indexOf(par),
+ firstElementIndex, lastElementIndex,
+ availableShrink, availableStretch,
+ difference, ratio, 0, indent,
+ 0, thisLLM.getLineWidth(), 0, 0, 0);
+ } else {
+ return new LineBreakPosition(thisLLM,
+ thisLLM.getKnuthParagraphs().indexOf(par),
+ firstElementIndex, lastElementIndex,
+ availableShrink, availableStretch,
+ difference, ratio, 0, indent,
+ lineLead + lineFollow,
+ thisLLM.getLineWidth(), spaceBefore, spaceAfter,
+ lineLead);
+ }
+ }
+
+ public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
+ double threshold, boolean force,
+ int allowedBreaks) {
+ return super.findBreakingPoints(par, /*lineWidth,*/
+ threshold, force, allowedBreaks);
+ }
+
+ protected int filterActiveNodes() {
+ KnuthNode bestActiveNode = null;
+
+ if (pageAlignment == Constants.EN_JUSTIFY) {
+ // leave all active nodes and find the optimum line number
+ //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits);
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ }
+ }
+
+ // scan the node set once again and remove some nodes
+ //log.debug("LBA.filterActiveList> layout selection");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
+ //if (false) {
+ if (node.line != bestActiveNode.line
+ && node.totalDemerits > MAX_DEMERITS) {
+ //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+ deactivateNode(node);
+ } else {
+ //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
+ }
+ }
+ }
+ } else {
+ // leave only the active node with fewest total demerits
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ if (node != bestActiveNode) {
+ deactivateNode(node);
+ }
+ }
+ }
+ }
+ return bestActiveNode.line;
+ }
+}
\ No newline at end of file
package org.apache.fop.layoutmgr.breaking;
import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.PageBreakingAlgorithm;
import org.apache.fop.layoutmgr.SpaceResolver;
-import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode;
import org.apache.fop.traits.MinOptMax;
/**
* Helper class for dealing with out-of-line objects (before-floats and footnotes) when
* breaking text into pages. It stores the necessary informations to place out-of-line
* objects, and provides methods to manipulate them.
- *
+ *
* @see PageBreakingAlgorithm
*/
public class OutOfLineRecord {
-
+
/**
- * Stores informations about how many out-of-line objects have already been handled.
+ * During page breaking, records which out-of-line objects have already been handled,
+ * and how much of them are placed on the current page.
*/
- public static class ProgressInfo {
-
- /** Cumulated BPD length of all out-of-line objects inserted so far. */
- private int alreadyInsertedLength = 0;
+ class ProgressInfo {
/** Index of the last inserted out-of-line object. */
- private int lastInsertedIndex = -1;
+ int lastInsertedIndex;
/**
* Index of the last inserted Knuth element of the last inserted out-of-line
- * object. Currently only used for footnotes, as before-floats may not be split on
- * several pages. Might be useful when later dealing with floats that cannot even
- * be put on a page alone, however.
+ * object.
+ */
+ int lastElementOfLastInsertedIndex;
+
+ /**
+ * Amount of out-of-lines put on the current page.
*/
- private int lastElementOfLastInsertedIndex = -1;
-
+ ElasticLength alreadyInserted = new ElasticLength();
+
/**
- * Initializes this record, as if no out-of-line object were handled yet.
+ * Number of times the last out-of-line is split. The initial value is 1, and is
+ * reset each time the end of an out-of-line is reached. The purpose is to compute
+ * additional demerits for split out-of-lines.
*/
- private void initialize() {
- alreadyInsertedLength = 0;
- lastInsertedIndex = -1;
- lastElementOfLastInsertedIndex = -1;
+ int nbSplit;
+
+ /**
+ * Creates and initializes a new record.
+ *
+ * @see OutOfLineRecord#initialize()
+ */
+ ProgressInfo() {
+ initialize();
}
/**
- * @return a copy of this record
+ * Creates a copy of the given record.
+ *
+ * @param progressInfo original record
*/
- public ProgressInfo copy() {
- ProgressInfo info = new ProgressInfo();
- info.alreadyInsertedLength = alreadyInsertedLength;
- info.lastInsertedIndex = lastInsertedIndex;
- info.lastElementOfLastInsertedIndex = lastElementOfLastInsertedIndex;
- return info;
+ ProgressInfo(ProgressInfo progressInfo) {
+ this.lastInsertedIndex = progressInfo.lastInsertedIndex;
+ this.lastElementOfLastInsertedIndex = progressInfo.lastElementOfLastInsertedIndex;
+ this.alreadyInserted.set(progressInfo.alreadyInserted);
+ this.nbSplit = progressInfo.nbSplit;
}
/**
- * Returns the cumulated length of all already typeset out-of-line objects.
- * @return the total length in the block-progression-direction
+ * Returns the amount of out-of-lines inserted on the current page.
+ *
+ * @return the amount of out-of-lines inserted on the current page
*/
- public int getAlreadyInsertedLength() {
- return alreadyInsertedLength;
+ public ElasticLength getInserted() {
+ return alreadyInserted;
}
/**
- * Returns the index of the last element of the last already typeset out-of-line
- * object.
- * @return the index of the last placed KnuthElement
+ * Returns the number of times the last out-of-line is split.
+ *
+ * @return the number of times the last out-of-line is split
+ */
+ public int getNbSplit() {
+ return nbSplit;
+ }
+
+ /**
+ * Returns the index of the last element of the last inserted out-of-line.
+ *
+ * @return the index of the last element of the last inserted out-of-line
*/
public int getLastElementOfLastInsertedIndex() {
return lastElementOfLastInsertedIndex;
}
/**
- * @return the index of the last already typeset out-of-line object.
+ * Returns the index of the last inserted out-of-line.
+ *
+ * @return the index of the last inserted out-of-line
*/
public int getLastInsertedIndex() {
return lastInsertedIndex;
}
- public String toString() {
- return "length=" + alreadyInsertedLength
- + ", index=" + lastInsertedIndex
- + ", elt=" + lastElementOfLastInsertedIndex;
+ /**
+ * Initializes this record such that no out-of-line has been inserted yet.
+ */
+ public void initialize() {
+ lastInsertedIndex = -1;
+ lastElementOfLastInsertedIndex = -1;
+ alreadyInserted.reset();
+ nbSplit = 1;
}
- }
-
- /**
- * Sequences of KnuthElement corresponding to already encountered out-of-line objects.
- * This is a List of List of KnuthElement.
- */
- private List knuthSequences = null;
- /**
- * Each element of this list corresponds to the cumulated length in the BPD of all the
- * out-of-line objects up to the given index. This is a List of Integer.
- *
- * @see OutOfLineRecord#knuthSequences
- */
- private List cumulativeLengths = null;
+ /**
+ * Records progress status for out-of-lines up to the previous page.
+ *
+ * @param info progress informations for the previous page
+ */
+ public void setPrevious(ProgressInfo info) {
+ lastInsertedIndex = info.lastInsertedIndex;
+ lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex;
+ if (lastInsertedIndex >= 0) {
+ List lastOutOfLine = ((List) knuthSequences.get(lastInsertedIndex));
+ // If the last out-of-line was split, go just before the first next box
+ while (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 2
+ && !((KnuthElement) lastOutOfLine.get(lastElementOfLastInsertedIndex + 1)).isBox()) {
+ lastElementOfLastInsertedIndex++;
+ }
+ if (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 1) {
+ // We haven't reached the end of the out-of-line yet
+ nbSplit = info.nbSplit + 1;
+ } else {
+ nbSplit = 1;
+ }
+ }
+ alreadyInserted.reset();
+ }
- /**
- * True if new out-of-line objects are cited in the sequence of Knuth elements since
- * the last encountered legal breakpoint.
- *
- * @see OutOfLineRecord#newSinceLastBreakpoint()
- */
- private boolean newSinceLastBreakpoint = false;
+ /**
+ * Checks whether there are still out-of-line objects to be placed.
+ *
+ * @return <code>true</code> if not all out-of-lines have been placed yet,
+ * otherwise <code>false</code>
+ */
+ public boolean remaining() {
+ return (lastInsertedIndex < knuthSequences.size() - 1) || isLastSplit();
+ }
- /**
- * Index of the first newly encountered out-of-line object since the last legal
- * breakpoint.
- *
- * @see OutOfLineRecord#knuthSequences
- */
- private int firstNewIndex = 0;
+ /**
+ * Returns the number of still to-be-placed out-of-lines.
+ * @return the number of not yet typeset out-of-line objects.
+ */
+ public int getNbOfDeferred() {
+ return knuthSequences.size() - 1 - lastInsertedIndex;
+ }
- /**
- * Dimension in the BPD of the separator between the out-of-line area and the main
- * area.
- */
- private MinOptMax separatorLength = null;
+ /**
+ * Checks whether the end of the current out-of-line has been reached.
+ *
+ * @return <code>true</code> if the whole out-of-line has been placed, otherwise
+ * <code>false</code>
+ */
+ boolean endOfOutOfLine() {
+ return lastElementOfLastInsertedIndex
+ >= ((List) knuthSequences.get(lastInsertedIndex)).size() - 1;
+ }
- /**
- * Record of already handled out-of-line objects.
- *
- * @see ProgressInfo
- */
- private ProgressInfo progressInfo;
+ /**
+ * Checks whether the last out-of-line placed on the current page must be split or
+ * not.
+ *
+ * @return <code>true</code> if the last typeset out-of-line object must be split on
+ * several pages.
+ */
+ public boolean isLastSplit() {
+ return lastInsertedIndex >= 0 && !endOfOutOfLine();
+ }
- public OutOfLineRecord(MinOptMax separatorLength) {
- this.separatorLength = separatorLength;
- this.progressInfo = new ProgressInfo();
- }
+ /**
+ * Adds the dimensions of the separator between the out-of-line area and the main
+ * content to the amount of out-of-lines already placed on the current page.
+ */
+ public void addSeparator() {
+ alreadyInserted.add(separator);
+ }
- /**
- * Initializes this record, as if no out-of-line object were handled yet.
- */
- public void initialize() {
- knuthSequences = null;
- cumulativeLengths = null;
- newSinceLastBreakpoint = false;
- firstNewIndex = 0;
- progressInfo.initialize();
- }
+ /**
+ * Places on the current page out-of-line content up to the next legal break in the current
+ * out-of-line. This method is meant to be called by subclasses of this class, not
+ * by external classes even in the same package.
+ * <p><strong>Pre-condition:</strong> it is supposed that we are <em>inside</em>
+ * the out-of-line, and that we haven't reached its end yet.
+ */
+ void nextInsideBreak() {
+ List knuthSequence = (List) knuthSequences.get(lastInsertedIndex);
+ Iterator elementIter = knuthSequence.listIterator(lastElementOfLastInsertedIndex + 1);
+ boolean prevIsBox = false;
+ do {
+ lastElementOfLastInsertedIndex++;
+ KnuthElement element = (KnuthElement) elementIter.next();
+ if (element.isBox()) {
+ alreadyInserted.add(0, element.getW(), 0);
+ prevIsBox = true;
+ } else if (element.isGlue()) {
+ if (prevIsBox) {
+ break;
+ }
+ alreadyInserted.add(element.getZ(), element.getW(), element.getY());
+ prevIsBox = false;
+ } else {
+ if (element.getP() < KnuthElement.INFINITE) {
+ alreadyInserted.add(0, element.getW(), 0);
+ break;
+ }
+ prevIsBox = false;
+ }
+ } while (lastElementOfLastInsertedIndex < knuthSequence.size() - 1);
+ if (lastElementOfLastInsertedIndex == knuthSequence.size() - 1) {
+ nbSplit = 0;
+ }
+ }
- /**
- * @return the informations about already handled out-of-line objects
- */
- public ProgressInfo getProgress() {
- return this.progressInfo;
+ public String toString() {
+ return "index=" + lastInsertedIndex
+ + ", elt=" + lastElementOfLastInsertedIndex
+ + ", inserted=" + alreadyInserted
+ + ", splits=" + nbSplit;
+ }
}
/**
- * @return the length in the BPD of the separator between the out-of-line area and the
- * main area.
+ * Sequences of KnuthElement corresponding to already encountered out-of-line objects.
+ * This is a List of List of KnuthElement.
*/
- public MinOptMax getSeparatorLength() {
- return separatorLength;
- }
+ List knuthSequences = new ArrayList();
/**
- * @return the total length of already encountered out-of-line objects
+ * Dimension in the BPD of the separator between the out-of-line area and the main
+ * area.
*/
- public int getTotalLength() {
- if (cumulativeLengths == null || cumulativeLengths.size() == 0) {
- return 0;
- } else {
- return ((Integer) cumulativeLengths.get(cumulativeLengths.size() - 1)).intValue();
- }
- }
+ private ElasticLength separator = null;
/**
- * @return true if out-of-line objects have already been encountered (but not
- * necessarily typeset yet)
+ * Creates a new record for a given type of out-of-lines.
+ *
+ * @param separator dimensions of the separator between the out-of-line area and the
+ * main area
*/
- public boolean existing() {
- return (knuthSequences != null && knuthSequences.size() > 0);
- }
-
- public void resetNewSinceLastBreakpoint() {
- newSinceLastBreakpoint = false;
+ public OutOfLineRecord(MinOptMax separator) {
+ this.separator = new ElasticLength(separator.opt - separator.min,
+ separator.opt,
+ separator.max - separator.opt);
}
/**
- * @return true if new out-of-line objects are cited in the sequence of Knuth
- * elements since the last encountered legal breakpoint.
+ * Initializes this record, as if no out-of-line object were handled yet.
*/
- public boolean newSinceLastBreakpoint() {
- return newSinceLastBreakpoint;
+ public void initialize() {
+ knuthSequences = new ArrayList();
}
/**
* Records one or more newly encountered out-of-line objects.
- * @param elementLists the list of corresponding Knuth sequences
+ *
+ * @param elementLists the list of corresponding Knuth sequences. This is a
+ * List<List<KnuthElement>>.
*/
public void add(List elementLists) {
- // Initialize stuff if necessary
- if (knuthSequences == null) {
- knuthSequences = new ArrayList();
- cumulativeLengths = new ArrayList();
- }
- if (!newSinceLastBreakpoint) {
- newSinceLastBreakpoint = true;
- firstNewIndex = knuthSequences.size();
- }
- // compute the total length of the footnotes
ListIterator elementListsIterator = elementLists.listIterator();
while (elementListsIterator.hasNext()) {
- LinkedList noteList = (LinkedList) elementListsIterator.next();
-
- //Space resolution (Note: this does not respect possible stacking constraints
- //between footnotes!)
- SpaceResolver.resolveElementList(noteList);
-
- int noteLength = 0;
- knuthSequences.add(noteList);
- ListIterator noteListIterator = noteList.listIterator();
- while (noteListIterator.hasNext()) {
- KnuthElement element = (KnuthElement) noteListIterator.next();
- if (element.isBox() || element.isGlue()) {
- noteLength += element.getW();
- }
- }
- cumulativeLengths.add(new Integer(getTotalLength() + noteLength));
- }
- }
-
- /**
- * Sets the progress informations to the given values. Called whenever a new active
- * node is considered; the informations regarding already handled out-of-line objects
- * must be set to the active node's values in order to know from where to start the
- * placement of further objects.
- *
- * @param info progress informations of the currently considered active node
- */
- public void setProgress(ProgressInfo info) {
- this.progressInfo.alreadyInsertedLength = info.alreadyInsertedLength;
- this.progressInfo.lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex;
- this.progressInfo.lastInsertedIndex = info.lastInsertedIndex;
- }
+ List knuthSequence = (List) elementListsIterator.next();
- /* Unless I'm wrong, newOnThisPagePlusPiecesFromPrevious always implies
- * notAllInserted. And if A => B, then A && B <=> B
- * So this code may be simplified, see deferred() below
- */
- /**
-// * Returns true if their are (pieces of) footnotes to be typeset on the
-// * current page.
-// * @param listIndex index of the last inserted footnote for the
-// * currently considered active node
-// * @param elementIndex index of the last element of the last inserted footnote
-// * @param length total length of all footnotes inserted so far
-// */
-// public boolean deferredFootnotes(ProgressInfo progressInfo) {
-// boolean newOnThisPagePlusPiecesFromPrevious =
-// newSinceLastBreakpoint()
-// && firstNewIndex != 0
-// && (progressInfo.lastInsertedIndex < firstNewIndex - 1
-// || progressInfo.lastElementOfLastInsertedIndex <
-// ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1);
-// boolean notAllInserted = progressInfo.alreadyInsertedLength < getTotalLength();
-// return notAllInserted;
-// }
+ // Space resolution (Note: this does not respect possible stacking constraints
+ // /between/ out-of-lines!)
+ SpaceResolver.resolveElementList(knuthSequence);
- /**
- * @return <code>true</code> if some out-of-line objects have not already been
- * typeset.
- */
- public boolean deferred() {
- return progressInfo.alreadyInsertedLength < getTotalLength();
- }
-
- /**
- * @return the number of not yet typeset out-of-line objects.
- */
- public int getNbOfDeferred() {
- return knuthSequences.size() - 1 - progressInfo.lastInsertedIndex;
+ knuthSequences.add(knuthSequence);
+ }
}
/**
- * @return <code>true</code> if the last typeset out-of-line object must be split on
- * several pages.
+ * Returns <code>true</code> if out-of-lines have already been encountered.
+ *
+ * @return <code>true</code> if out-of-lines are recorded, possibly not yet typeset
*/
- public boolean isSplit() {
- return (progressInfo.lastElementOfLastInsertedIndex
- < ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1);
+ public boolean existing() {
+ return knuthSequences.size() > 0;
}
/**
* it does not exist
*/
public List getSequence(int index) {
- /*TODO vh: bof */
- if (knuthSequences == null) {
- return null;
- } else {
- return (List) knuthSequences.get(index);
- }
+ return (List) knuthSequences.get(index);
}
- /**
- * Tries to split the flow of footnotes to put one part on the current page.
- * @param prevNodeProgress informations about footnotes already inserted on the
- * previous page
- * @param availableLength available space for footnotes on this page
- * @param canDeferOldFootnotes
- * @return the length of footnotes which could be inserted on this page
- */
- public int getFootnoteSplit(ProgressInfo prevNodeProgress,
- int availableLength, boolean canDeferOldFootnotes) {
- if (availableLength <= 0) {
- progressInfo.alreadyInsertedLength = prevNodeProgress.getAlreadyInsertedLength();
- return 0;
- } else {
- // the split should contain a piece of the last footnote
- // together with all previous, not yet inserted footnotes;
- // but if this is not possible, try adding as much content as possible
- int splitLength = 0;
- ListIterator noteListIterator = null;
- KnuthElement element = null;
- boolean somethingAdded = false;
-
- // prevNodeProgress.lastInsertedIndex and
- // prevNodeProgress.lastElementOfLastInsertedIndex points to the last footnote element
- // already placed in a page: advance to the next element
- int listIndex = prevNodeProgress.lastInsertedIndex;
- int elementIndex = prevNodeProgress.lastElementOfLastInsertedIndex;
- if (listIndex == -1
- || elementIndex == ((LinkedList) knuthSequences.get(listIndex)).size() - 1) {
- listIndex++;
- elementIndex = 0;
- } else {
- elementIndex++;
- }
-
- // try adding whole notes
- // if there are more than 1 footnote to insert
- if (knuthSequences.size() - 1 > listIndex) {
- // add the previous footnotes: these cannot be broken or deferred
- if (!canDeferOldFootnotes
- && newSinceLastBreakpoint()
- && firstNewIndex > 0) {
- splitLength = ((Integer) cumulativeLengths.get(firstNewIndex - 1)).intValue()
- - prevNodeProgress.alreadyInsertedLength;
- listIndex = firstNewIndex;
- elementIndex = 0;
- }
- // try adding the new footnotes
- while (((Integer) cumulativeLengths.get(listIndex)).intValue()
- - prevNodeProgress.alreadyInsertedLength <= availableLength) {
- splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue()
- - prevNodeProgress.alreadyInsertedLength;
- somethingAdded = true;
- listIndex++;
- elementIndex = 0;
- }
- // as this method is called only if it is not possible to insert
- // all footnotes, at this point listIndex and elementIndex points to
- // an existing element, the next one we will try to insert
- }
-
- // try adding a split of the next note
- noteListIterator = ((List) knuthSequences.get(listIndex)).listIterator(elementIndex);
-
- int prevSplitLength = 0;
- int prevIndex = -1;
- int index = -1;
-
- while (!somethingAdded || splitLength <= availableLength) {
- if (!somethingAdded) {
- somethingAdded = true;
- } else {
- prevSplitLength = splitLength;
- prevIndex = index;
- }
- // get a sub-sequence from the note element list
- boolean bPrevIsBox = false;
- while (noteListIterator.hasNext()) {
- // as this method is called only if it is not possible to insert
- // all footnotes, and we have already tried (and failed) to insert
- // this whole footnote, the while loop will never reach the end
- // of the note sequence
- element = (KnuthElement) noteListIterator.next();
- if (element.isBox()) {
- // element is a box
- splitLength += element.getW();
- bPrevIsBox = true;
- } else if (element.isGlue()) {
- // element is a glue
- if (bPrevIsBox) {
- // end of the sub-sequence
- index = noteListIterator.previousIndex();
- break;
- }
- bPrevIsBox = false;
- splitLength += element.getW();
- } else {
- // element is a penalty
- if (element.getP() < KnuthElement.INFINITE) {
- // end of the sub-sequence
- index = noteListIterator.previousIndex();
- break;
- }
- }
- }
- }
- // if prevSplitLength is 0, this means that the available length isn't enough
- // to insert even the smallest split of the last footnote, so we cannot end a
- // page here
- // if prevSplitLength is > 0 we can insert some footnote content in this page
- // and insert the remaining in the following one
- if (!somethingAdded) {
- // there was not enough space to add a piece of the first new footnote
- // this is not a good break
- prevSplitLength = 0;
- } else if (prevSplitLength > 0) {
- // prevIndex is -1 if we have added only some whole footnotes
- progressInfo.lastInsertedIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
- progressInfo.lastElementOfLastInsertedIndex = (prevIndex != -1)
- ? prevIndex
- : ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;
- }
- progressInfo.alreadyInsertedLength
- = prevNodeProgress.getAlreadyInsertedLength() + prevSplitLength;
- return prevSplitLength;
- }
- }
-
- /**
- * Tries to split the flow of floats to put some floats on the current page.
- * @param prevProgress floats already inserted on the previous page
- * @param availableLength available space for floats
- * @return the length of floats which could be placed on the current page
- */
- public int getFloatSplit(ProgressInfo prevProgress, int availableLength) {
- /*
- * Normally this method is called only when there is some place for
- * floats => availableLength > 0
- */
- int splitLength = 0;
- int listIndex = prevProgress.lastInsertedIndex + 1;
-
- while (listIndex < knuthSequences.size()
- && ((Integer) cumulativeLengths.get(listIndex)).intValue()
- - prevProgress.alreadyInsertedLength <= availableLength) {
- splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue()
- - prevProgress.alreadyInsertedLength;
- listIndex++;
- }
- progressInfo.lastInsertedIndex = listIndex - 1;
- progressInfo.alreadyInsertedLength = prevProgress.alreadyInsertedLength + splitLength;
- return splitLength;
- }
-
- /**
- * Places on the current page all of the out-of-line objects not yet inserted.
- */
- public void insertAll() {
- progressInfo.alreadyInsertedLength = getTotalLength();
- progressInfo.lastInsertedIndex = knuthSequences.size() - 1;
- progressInfo.lastElementOfLastInsertedIndex
- = ((List) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;
- }
/**
* When restarting the algorithm from a given point, reset the informations about
* out-of-line objects to the values at that point.
* @param elementLists out-of-line sequences which are met after the restarting point,
- * and thus must be removed from the list of already encoutered objects.
+ * and thus must be removed from the list of already encountered objects.
*/
public void reset(List elementLists) {
for (int i = 0; i < elementLists.size(); i++) {
knuthSequences.remove(knuthSequences.size() - 1);
- cumulativeLengths.remove(cumulativeLengths.size() - 1);
- }
- }
-
- /**
- * When the whole normal flow has been typeset and there are still footnotes to be
- * placed, creates as many pages as necessary to place them.
- */
- public void createFootnotePages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) {
- progressInfo.alreadyInsertedLength = lastNode.footnotesProgress.getAlreadyInsertedLength();
- progressInfo.lastInsertedIndex = lastNode.footnotesProgress.getLastInsertedIndex();
- progressInfo.lastElementOfLastInsertedIndex = lastNode.footnotesProgress.getLastElementOfLastInsertedIndex();
- int availableBPD = lineWidth;
- int split = 0;
- KnuthPageNode prevNode = lastNode;
-
- // create pages containing the remaining footnote bodies
- while (progressInfo.alreadyInsertedLength < getTotalLength()) {
- // try adding some more content
- if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - progressInfo.alreadyInsertedLength
- <= availableBPD) {
- // add a whole footnote
- availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue()
- - progressInfo.alreadyInsertedLength;
- progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue();
- progressInfo.lastElementOfLastInsertedIndex
- = ((LinkedList)knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;
- } else if ((split = getFootnoteSplit(progressInfo, availableBPD, true))
- > 0) {
- // add a piece of a footnote
- availableBPD -= split;
- // footnoteListIndex has already been set in getFootnoteSplit()
- // footnoteElementIndex has already been set in getFootnoteSplit()
- } else {
- // cannot add any content: create a new node and start again
- KnuthPageNode node = (KnuthPageNode)
- algo.createNode(lastNode.position, prevNode.line + 1, 1,
- progressInfo.alreadyInsertedLength - prevNode.footnotesProgress.getAlreadyInsertedLength(),
- 0, 0,
- 0, 0, 0,
- 0, 0, prevNode);
- algo.addNode(node.line, node);
- algo.removeNode(prevNode.line, prevNode);
-
- prevNode = node;
- availableBPD = lineWidth;
- }
- }
- // create the last node
- KnuthPageNode node = (KnuthPageNode)
- algo.createNode(lastNode.position, prevNode.line + 1, 1,
- getTotalLength() - prevNode.footnotesProgress.getAlreadyInsertedLength(), 0, 0,
- 0, 0, 0,
- 0, 0, prevNode);
- algo.addNode(node.line, node);
- algo.removeNode(prevNode.line, prevNode);
- }
-
- /* TODO vh: won't work when there are also footnotes. To be merged with createFootnotePages */
- public void createFloatPages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) {
- progressInfo.alreadyInsertedLength = lastNode.floatsProgress.getAlreadyInsertedLength();
- progressInfo.lastInsertedIndex = lastNode.floatsProgress.getLastInsertedIndex();
- int availableBPD = lineWidth;
- KnuthPageNode prevNode = lastNode;
-
- // create pages containing the remaining float bodies
- while (progressInfo.alreadyInsertedLength < getTotalLength()) {
- // try adding some more content
- if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex + 1)).intValue() - progressInfo.alreadyInsertedLength
- <= availableBPD) {
- // add a whole float
- progressInfo.lastInsertedIndex++;
- availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue()
- - progressInfo.alreadyInsertedLength;
- progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue();
- } else {
- // cannot add any content: create a new node and start again
- KnuthPageNode node = (KnuthPageNode)
- algo.createNode(lastNode.position, prevNode.line + 1, 1,
- progressInfo.alreadyInsertedLength - prevNode.floatsProgress.getAlreadyInsertedLength(),
- 0, 0,
- 0, 0, 0,
- 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode);
- algo.addNode(node.line, node);
- algo.removeNode(prevNode.line, prevNode);
-
- prevNode = node;
- availableBPD = lineWidth;
- }
}
- // create the last node
- KnuthPageNode node = (KnuthPageNode)
- algo.createNode(lastNode.position, prevNode.line + 1, 1,
- getTotalLength() - prevNode.floatsProgress.getAlreadyInsertedLength(), 0, 0,
- 0, 0, 0,
- 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode);
- algo.addNode(node.line, node);
- algo.removeNode(prevNode.line, prevNode);
}
}
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.SpaceSpecifier;
+import org.apache.fop.layoutmgr.breaking.LineBreakPosition;
+import org.apache.fop.layoutmgr.breaking.LineBreakingAlgorithm;
import org.apache.fop.area.Area;
import org.apache.fop.area.LineArea;
import org.apache.fop.area.inline.InlineArea;
}
}
- /**
- * Private class to store information about inline breaks.
- * Each value holds the start and end indexes into a List of
- * inline break positions.
- */
- private static class LineBreakPosition extends LeafPosition {
- private int iParIndex; // index of the Paragraph this Position refers to
- private int iStartIndex; //index of the first element this Position refers to
- private int availableShrink;
- private int availableStretch;
- private int difference;
- private double dAdjust; // Percentage to adjust (stretch or shrink)
- private double ipdAdjust; // Percentage to adjust (stretch or shrink)
- private int startIndent;
- private int lineHeight;
- private int lineWidth;
- private int spaceBefore;
- private int spaceAfter;
- private int baseline;
-
- LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
- int shrink, int stretch, int diff,
- double ipdA, double adjust, int ind,
- int lh, int lw, int sb, int sa, int bl) {
- super(lm, iBreakIndex);
- availableShrink = shrink;
- availableStretch = stretch;
- difference = diff;
- iParIndex = index;
- this.iStartIndex = iStartIndex;
- ipdAdjust = ipdA;
- dAdjust = adjust;
- startIndent = ind;
- lineHeight = lh;
- lineWidth = lw;
- spaceBefore = sb;
- spaceAfter = sa;
- baseline = bl;
- }
-
- }
-
-
private int textAlignment = EN_JUSTIFY;
private int textAlignmentLast;
private int effectiveAlignment;
}
// this class represents a paragraph
- private class Paragraph extends InlineKnuthSequence {
+ public class Paragraph extends InlineKnuthSequence {
/** Number of elements to ignore at the beginning of the list. */
private int ignoreAtStart = 0;
/** Number of elements to ignore at the end of the list. */
lastLineEndIndent = endIndent;
}
+ public MinOptMax getLineFiller() {
+ return lineFiller;
+ }
+
public void startParagraph(int lw) {
lineWidth = lw;
startSequence();
}
}
- private class LineBreakingAlgorithm extends BreakingAlgorithm {
- private LineLayoutManager thisLLM;
- private int pageAlignment;
- private int activePossibility;
- private int addedPositions;
- private int textIndent;
- private int fillerMinWidth;
- private int lineHeight;
- private int lead;
- private int follow;
- private int maxDiff;
- private static final double MAX_DEMERITS = 10e6;
-
- public LineBreakingAlgorithm (int pageAlign,
- int textAlign, int textAlignLast,
- int indent, int fillerWidth,
- int lh, int ld, int fl, boolean first,
- int maxFlagCount, LineLayoutManager llm) {
- super(textAlign, textAlignLast, first, false, maxFlagCount);
- pageAlignment = pageAlign;
- textIndent = indent;
- fillerMinWidth = fillerWidth;
- lineHeight = lh;
- lead = ld;
- follow = fl;
- thisLLM = llm;
- activePossibility = -1;
- maxDiff = fobj.getWidows() >= fobj.getOrphans()
- ? fobj.getWidows()
- : fobj.getOrphans();
- }
-
- public void updateData1(int lineCount, double demerits) {
- lineLayouts.addPossibility(lineCount, demerits);
- log.trace("Layout possibility in " + lineCount + " lines; break at position:");
- }
-
- public void updateData2(KnuthNode bestActiveNode,
- KnuthSequence par,
- int total) {
- // compute indent and adjustment ratio, according to
- // the value of text-align and text-align-last
- int indent = 0;
- int difference = bestActiveNode.difference;
- int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
- indent += (textAlign == Constants.EN_CENTER)
- ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
- indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0;
- double ratio = (textAlign == Constants.EN_JUSTIFY
- || difference < 0 && -difference <= bestActiveNode.availableShrink)
- ? bestActiveNode.adjustRatio : 0;
-
- // add nodes at the beginning of the list, as they are found
- // backwards, from the last one to the first one
-
- // the first time this method is called, initialize activePossibility
- if (activePossibility == -1) {
- activePossibility = 0;
- addedPositions = 0;
- }
-
- if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
- activePossibility++;
- addedPositions = 0;
- }
-
- if (difference + bestActiveNode.availableShrink < 0) {
- if (log.isWarnEnabled()) {
- log.warn(FONode.decorateWithContextInfo(
- "Line " + (addedPositions + 1)
- + " of a paragraph overflows the available area.", getFObj()));
- }
- }
-
- //log.debug("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions)
- // + ") difference = " + difference + " ratio = " + ratio);
- lineLayouts.addBreakPosition(makeLineBreakPosition(par,
- (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
- bestActiveNode.position,
- bestActiveNode.availableShrink - (addedPositions > 0
- ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min),
- bestActiveNode.availableStretch,
- difference, ratio, indent), activePossibility);
- addedPositions++;
- }
-
- /* reset activePossibility, as if breakpoints have not yet been computed
- */
- public void resetAlgorithm() {
- activePossibility = -1;
- }
-
- private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
- int firstElementIndex,
- int lastElementIndex,
- int availableShrink,
- int availableStretch,
- int difference,
- double ratio,
- int indent) {
- // line height calculation - spaceBefore may differ from spaceAfter
- // by 1mpt due to rounding
- int spaceBefore = (lineHeight - lead - follow) / 2;
- int spaceAfter = lineHeight - lead - follow - spaceBefore;
- // height before the main baseline
- int lineLead = lead;
- // maximum follow
- int lineFollow = follow;
- // true if this line contains only zero-height, auxiliary boxes
- // and the actual line width is 0; in this case, the line "collapses"
- // i.e. the line area will have bpd = 0
- boolean bZeroHeightLine = (difference == iLineWidth);
-
- // if line-stacking-strategy is "font-height", the line height
- // is not affected by its content
- if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
- ListIterator inlineIterator
- = par.listIterator(firstElementIndex);
- AlignmentContext lastAC = null;
- int maxIgnoredHeight = 0; // See spec 7.13
- for (int j = firstElementIndex;
- j <= lastElementIndex;
- j++) {
- KnuthElement element = (KnuthElement) inlineIterator.next();
- if (element instanceof KnuthInlineBox ) {
- AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
- if (ac != null && lastAC != ac) {
- if (!ac.usesInitialBaselineTable()
- || ac.getAlignmentBaselineIdentifier() != EN_BEFORE_EDGE
- && ac.getAlignmentBaselineIdentifier() != EN_AFTER_EDGE) {
- int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
- if (alignmentOffset + ac.getAltitude() > lineLead) {
- lineLead = alignmentOffset + ac.getAltitude();
- }
- if (ac.getDepth() - alignmentOffset > lineFollow) {
- lineFollow = ac.getDepth() - alignmentOffset;
- }
- } else {
- if (ac.getHeight() > maxIgnoredHeight) {
- maxIgnoredHeight = ac.getHeight();
- }
- }
- lastAC = ac;
- }
- if (bZeroHeightLine
- && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
- bZeroHeightLine = false;
- }
- }
- }
-
- if (lineFollow < maxIgnoredHeight - lineLead) {
- lineFollow = maxIgnoredHeight - lineLead;
- }
- }
-
- constantLineHeight = lineLead + lineFollow;
-
- if (bZeroHeightLine) {
- return new LineBreakPosition(thisLLM,
- knuthParagraphs.indexOf(par),
- firstElementIndex, lastElementIndex,
- availableShrink, availableStretch,
- difference, ratio, 0, indent,
- 0, iLineWidth, 0, 0, 0);
- } else {
- return new LineBreakPosition(thisLLM,
- knuthParagraphs.indexOf(par),
- firstElementIndex, lastElementIndex,
- availableShrink, availableStretch,
- difference, ratio, 0, indent,
- lineLead + lineFollow,
- iLineWidth, spaceBefore, spaceAfter,
- lineLead);
- }
- }
-
- public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
- double threshold, boolean force,
- int allowedBreaks) {
- return super.findBreakingPoints(par, /*lineWidth,*/
- threshold, force, allowedBreaks);
- }
-
- protected int filterActiveNodes() {
- KnuthNode bestActiveNode = null;
-
- if (pageAlignment == EN_JUSTIFY) {
- // leave all active nodes and find the optimum line number
- //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits);
- bestActiveNode = compareNodes(bestActiveNode, node);
- }
- }
-
- // scan the node set once again and remove some nodes
- //log.debug("LBA.filterActiveList> layout selection");
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
- //if (false) {
- if (node.line != bestActiveNode.line
- && node.totalDemerits > MAX_DEMERITS) {
- //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
- removeNode(i, node);
- } else {
- //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
- }
- }
- }
- } else {
- // leave only the active node with fewest total demerits
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- bestActiveNode = compareNodes(bestActiveNode, node);
- if (node != bestActiveNode) {
- removeNode(i, node);
- }
- }
- }
- }
- return bestActiveNode.line;
- }
- }
-
private int constantLineHeight = 12000;
follow = f;
}
+ public LineLayoutPossibilities getLineLayouts() {
+ return lineLayouts;
+ }
+
+ public boolean isFirstInBlock() {
+ return isFirstInBlock;
+ }
+
+ public int getLineWidth() {
+ return iLineWidth;
+ }
+
+ public void setConstantLineHeight(int constantLineHeight) {
+ this.constantLineHeight = constantLineHeight;
+ }
+
+ public List getKnuthParagraphs() {
+ return knuthParagraphs;
+ }
+
/** @see org.apache.fop.layoutmgr.LayoutManager */
public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this);
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
if (firstLM == null) {
firstLM = lastLM;
}
+ } else if (pos instanceof SpaceHandlingBreakPosition) {
+ positionList.add(pos);
} else {
// pos was created by this ListBlockLM, so it must be ignored
}
int penaltyHeight = step
+ getMaxRemainingHeight(fullHeights, partialHeights)
- totalHeight;
+
+ //Additional penalty height from penalties in the source lists
+ int additionalPenaltyHeight = 0;
+ KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]);
+ if (endEl instanceof KnuthPenalty) {
+ additionalPenaltyHeight = ((KnuthPenalty)endEl).getW();
+ }
+ endEl = (KnuthElement)elementLists[1].get(end[1]);
+ if (endEl instanceof KnuthPenalty) {
+ additionalPenaltyHeight = Math.max(
+ additionalPenaltyHeight, ((KnuthPenalty)endEl).getW());
+ }
+
int boxHeight = step - addedBoxHeight - penaltyHeight;
+ penaltyHeight += additionalPenaltyHeight; //Add AFTER calculating boxHeight!
// add the new elements
addedBoxHeight += boxHeight;
if (keepWithNextActive || mustKeepTogether()) {
p = KnuthPenalty.INFINITE;
}
- //returnList.add(new KnuthPenalty(penaltyHeight, p, false, stepPosition, false));
returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context));
}
}
private int startXOffset;
private int usedBPD;
+ private TableStepper stepper = new TableStepper(this);
+
/**
* Main constructor
* @param parent Parent layout manager
this.headerList = getKnuthElementsForRowIterator(
headerIter, context, alignment, TableRowIterator.HEADER);
ElementListUtils.removeLegalBreaks(this.headerList);
- this.headerNetHeight = ElementListUtils.calcContentLength(this.headerList);
+ this.headerNetHeight =
+ ElementListUtils.calcContentLength(this.headerList);
if (log.isDebugEnabled()) {
- log.debug("==> Header: " + headerNetHeight + " - " + this.headerList);
+ log.debug("==> Header: "
+ + headerNetHeight + " - " + this.headerList);
}
TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
getTableLM(), true, this.headerList);
KnuthBox box = new KnuthBox(headerNetHeight, pos, false);
if (getTableLM().getTable().omitHeaderAtBreak()) {
- //We can simply add the table header at the beginning of the whole list
+ //We can simply add the table header at the start
+ //of the whole list
headerAsFirst = box;
} else {
headerAsSecondToLast = box;
this.footerList = getKnuthElementsForRowIterator(
footerIter, context, alignment, TableRowIterator.FOOTER);
ElementListUtils.removeLegalBreaks(this.footerList);
- this.footerNetHeight = ElementListUtils.calcContentLength(this.footerList);
+ this.footerNetHeight =
+ ElementListUtils.calcContentLength(this.footerList);
if (log.isDebugEnabled()) {
- log.debug("==> Footer: " + footerNetHeight + " - " + this.footerList);
- }
- if (true /*getTableLM().getTable().omitFooterAtBreak()*/) {
- //We can simply add the table header at the end of the whole list
- TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
- getTableLM(), false, this.footerList);
- KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
- footerAsLast = box;
+ log.debug("==> Footer: "
+ + footerNetHeight + " - " + this.footerList);
}
+ //We can simply add the table footer at the end of the whole list
+ TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
+ getTableLM(), false, this.footerList);
+ KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
+ footerAsLast = box;
}
LinkedList returnList = getKnuthElementsForRowIterator(
trIter, context, alignment, TableRowIterator.BODY);
* @param iter TableRowIterator instance to fetch rows from
* @param context Active LayoutContext
* @param alignment alignment indicator
- * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
+ * @param bodyType Indicates what kind of body is being processed
+ * (BODY, HEADER or FOOTER)
* @return An element list
*/
private LinkedList getKnuthElementsForRowIterator(TableRowIterator iter,
pen.setP(-KnuthPenalty.INFINITE);
pen.setBreakClass(rowFO.getBreakBefore());
} else if (last instanceof BreakElement) {
- BreakElement breakPoss = (BreakElement)last;
+ BreakElement breakPoss = (BreakElement) last;
breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
breakPoss.setBreakClass(rowFO.getBreakBefore());
}
log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
}
}
- //TODO It may make sense to reuse the stepper since it allocates quite some space
- TableStepper stepper = new TableStepper(this);
- LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup(
+ LinkedList returnedList = this.stepper.getCombinedKnuthElementsForRowGroup(
context, rowGroup, maxColumnCount, bodyType);
if (returnedList != null) {
returnList.addAll(returnedList);
*/
public TableStepper(TableContentLayoutManager tclm) {
this.tclm = tclm;
- this.activeRow = 0;
}
private void setup(int columnCount) {
+ this.activeRow = 0;
elementLists = new List[columnCount];
startRow = new int[columnCount];
start = new int[columnCount];
import java.awt.geom.AffineTransform;
import org.w3c.dom.Document;
+import org.w3c.dom.svg.SVGAElement;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGSVGElement;
import org.apache.fop.pdf.PDFState;
import org.apache.fop.pdf.PDFStream;
import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.svg.PDFAElementBridge;
import org.apache.fop.svg.PDFBridgeContext;
import org.apache.fop.svg.PDFGraphics2D;
import org.apache.fop.svg.SVGUserAgent;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.ViewBox;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.SVGConstants;
/**
* PDF XML handler for SVG (uses Apache Batik).
*/
protected void renderSVGDocument(RendererContext context,
Document doc) {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
PDFInfo pdfInfo = getPDFInfo(context);
if (pdfInfo.paintAsBitmap) {
try {
final float uaResolution = context.getUserAgent().getSourceResolution();
SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform());
- GVTBuilder builder = new GVTBuilder();
+ //Scale for higher resolution on-the-fly images from Batik
+ double s = uaResolution / deviceResolution;
+ AffineTransform resolutionScaling = new AffineTransform();
+ resolutionScaling.scale(s, s);
- //TODO This AffineTransform here has to be fixed!!!
- AffineTransform linkTransform = pdfInfo.pdfState.getTransform();
- linkTransform.translate(xOffset / 1000f, yOffset / 1000f);
+ //Transformation matrix that establishes the local coordinate system for the SVG graphic
+ //in relation to PDF's initial coordinate system.
+ AffineTransform baseTransform = (AffineTransform)renderer.currentBasicTransform.clone();
+ baseTransform.concatenate(pdfInfo.pdfState.getTransform());
+ GVTBuilder builder = new GVTBuilder();
+
//Controls whether text painted by Batik is generated using text or path operations
boolean strokeText = false;
Configuration cfg = pdfInfo.cfg;
BridgeContext ctx = new PDFBridgeContext(ua,
(strokeText ? null : pdfInfo.fi),
- linkTransform);
+ new AffineTransform());
GraphicsNode root;
try {
root = builder.build(ctx, doc);
+ builder = null;
} catch (Exception e) {
log.error("svg graphic could not be built: "
+ e.getMessage(), e);
float sx = pdfInfo.width / (float)w;
float sy = pdfInfo.height / (float)h;
- ctx = null;
- builder = null;
+ //Scaling and translation for the bounding box of the image
+ AffineTransform scaling = new AffineTransform(
+ sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f);
+
+ //Finish the baseTransform, now that we know everything
+ baseTransform.concatenate(scaling);
+ baseTransform.concatenate(resolutionScaling);
+
+ //Now that we have the full baseTransform, we can update the transformation matrix for
+ //the AElementBridge.
+ PDFAElementBridge aBridge = (PDFAElementBridge)ctx.getBridge(
+ SVGDOMImplementation.SVG_NAMESPACE_URI, SVGConstants.SVG_A_TAG);
+ aBridge.getCurrentTransform().setTransform(baseTransform);
/*
* Clip to the svg area.
* Note: To have the svg overlay (under) a text area then use
* an fo:block-container
*/
- PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ pdfInfo.currentStream.add("%SVG setup\n");
renderer.saveGraphicsState();
renderer.setColor(Color.black, false, null);
renderer.setColor(Color.black, true, null);
- // transform so that the coordinates (0,0) is from the top left
- // and positive is down and to the right. (0,0) is where the
- // viewBox puts it.
- pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
- + yOffset / 1000f + " cm\n");
+
+ if (!scaling.isIdentity()) {
+ pdfInfo.currentStream.add("%viewbox\n");
+ pdfInfo.currentStream.add(CTMHelper.toPDFString(scaling, false) + " cm\n");
+ }
SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
- //AffineTransform at = ViewBox.getPreserveAspectRatioTransform(
- // svg, w / 1000f, h / 1000f);
- AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
- pdfInfo.width / 1000f, pdfInfo.height / 1000f);
- /*
- if (!at.isIdentity()) {
- double[] vals = new double[6];
- at.getMatrix(vals);
- pdfInfo.currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
- }*/
if (pdfInfo.pdfContext == null) {
pdfInfo.pdfContext = pdfInfo.pdfPage;
pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
pdfInfo.currentFontName, pdfInfo.currentFontSize);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
- pdfInfo.pdfState.push();
- AffineTransform transform = new AffineTransform();
- // scale to viewbox
- transform.translate(xOffset / 1000f, yOffset / 1000f);
- if (deviceResolution != uaResolution) {
- //Scale for higher resolution on-the-fly images from Batik
- double s = uaResolution / deviceResolution;
- at.scale(s, s);
- pdfInfo.currentStream.add("" + PDFNumber.doubleOut(s) + " 0 0 "
- + PDFNumber.doubleOut(s) + " 0 0 cm\n");
+ if (!resolutionScaling.isIdentity()) {
+ pdfInfo.currentStream.add("%resolution scaling for " + uaResolution + " -> " + deviceResolution + "\n");
+ pdfInfo.currentStream.add(
+ CTMHelper.toPDFString(resolutionScaling, false) + " cm\n");
graphics.scale(1 / s, 1 / s);
}
+
+ pdfInfo.currentStream.add("%SVG start\n");
- pdfInfo.pdfState.setTransform(transform);
+ pdfInfo.pdfState.push();
+ pdfInfo.pdfState.setTransform(baseTransform);
graphics.setPDFState(pdfInfo.pdfState);
graphics.setOutputStream(pdfInfo.outputStream);
try {
log.error("svg graphic could not be rendered: "
+ e.getMessage(), e);
}
-
- renderer.restoreGraphicsState();
pdfInfo.pdfState.pop();
+ renderer.restoreGraphicsState();
+ pdfInfo.currentStream.add("%SVG end\n");
}
/** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
transform = tf;
}
+ /** @return the transformation matrix for links */
+ public AffineTransform getCurrentTransform() {
+ return this.transform;
+ }
+
/**
* Returns 'a'.
* @return the name of this node
* @see org.apache.avalon.framework.activity.Initializable#initialize()
*/
public void initialize() throws Exception {
- if (this.fontInfo == null) {
+ if (this.fontInfo == null || this.cfg != null) {
fontInfo = new FontInfo();
FontSetup.setup(fontInfo, this.pdfContext.getFontList(), null);
//FontState fontState = new FontState("Helvetica", "normal",
+ PDFNumber.doubleOut(matrix[4], DEC) + " "
+ PDFNumber.doubleOut(matrix[5], DEC) + " cm\n");
}
+
+ /**
+ * This is mainly used for shading patterns which use the document-global coordinate system
+ * instead of the local one.
+ * @return the transformation matrix that established the basic user space for this document
+ */
+ protected AffineTransform getBaseTransform() {
+ AffineTransform at = new AffineTransform(graphicsState.getTransform());
+ return at;
+ }
/**
* This is a pdf specific method used to add a link to the
// Build proper transform from gradient space to page space
// ('Patterns' don't get userspace transform).
AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
+ transform = new AffineTransform(getBaseTransform());
transform.concatenate(getTransform());
transform.concatenate(gp.getTransform());
}
AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
+ transform = new AffineTransform(getBaseTransform());
transform.concatenate(getTransform());
transform.concatenate(rgp.getTransform());
bbox.add(new Double(rect.getY()));
AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
+ transform = new AffineTransform(getBaseTransform());
transform.concatenate(getTransform());
transform.concatenate(pp.getPatternTransform());
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="AD" type="update" fixes-bug="40270" due-to="Gary Reed">
+ Added relaxed validation for empty list-item-*, as suggested by Gary Reed.
+ </action>
+ <action context="Code" dev="AD" type="update">
+ Modified proportional-column-width() function to log an error if used
+ with table-layout=auto
+ </action>
+ <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.
+ </action>
+ <action context="Code" dev="JM" type="fix">
+ Fixed a memory-leak: The FO tree part of a page-sequence was not released when a
+ page-sequence was finished.
+ </action>
+ <action context="Code" dev="JM" type="fix">
+ Bugfix: Table headers and footers were swallowed when a table was nested in a list-block.
+ </action>
<action context="Code" dev="JM" type="fix">
Fixed a bug with indent handling when margins are used on a surrounding block and
not start/end-indent.
Initial support for page-number-citation-last (XSL 1.1). Works without problems
only for page-sequence so far.
</action>
+ <action context="Code" dev="VH" type="update" fixes-bug="39777">
+ Improved support of before-floats: allow float-only pages, take into
+ account floats' shrinkability/stretchability, handle the splitting of
+ big floats.
+ </action>
</release>
<release version="0.92beta" date="18 Apr 2006">
<action context="Code" dev="JM" type="fix">
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<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>
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.memory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+
+/**
+ * Debug tool to create and process large FO files by replicating them a specified number of times.
+ */
+public class MemoryEater {
+
+ private static void eatMemory(File foFile, int replicatorRepeats) throws Exception {
+
+ SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ FopFactory fopFactory = FopFactory.newInstance();
+
+ File xsltFile = new File("test/xsl/fo-replicator.xsl");
+ Source xslt = new StreamSource(xsltFile);
+
+ Source src = new StreamSource(foFile);
+
+ Transformer transformer = tFactory.newTransformer(xslt);
+ transformer.setParameter("repeats", new Integer(replicatorRepeats));
+
+ OutputStream out = new NullOutputStream(); //write to /dev/nul
+ Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
+ Result res = new SAXResult(fop.getDefaultHandler());
+
+ transformer.transform(src, res);
+
+ System.out.println("Generated " + fop.getResults().getPageCount() + " pages.");
+
+ }
+
+ private static void prompt() throws IOException {
+ BufferedReader in = new BufferedReader(new java.io.InputStreamReader(System.in));
+ System.out.print("Press return to continue...");
+ in.readLine();
+ }
+
+ /**
+ * Main method.
+ * @param args the command-line arguments
+ */
+ public static void main(String[] args) {
+ boolean doPrompt = true; //true if you want a chance to start the monitoring console
+ try {
+ int replicatorRepeats = 2;
+ if (args.length > 0) {
+ replicatorRepeats = Integer.parseInt(args[0]);
+ }
+ File testFile = new File("examples/fo/basic/readme.fo");
+
+ System.out.println("MemoryEater! About to replicate the test file "
+ + replicatorRepeats + " times...");
+ if (doPrompt) {
+ prompt();
+ }
+
+ System.out.println("Processing...");
+ long start = System.currentTimeMillis();
+
+ eatMemory(testFile, replicatorRepeats);
+
+ long duration = System.currentTimeMillis() - start;
+ System.out.println("Success! Job took " + duration + " ms");
+
+ if (doPrompt) {
+ prompt();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
<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>
<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>
underfull page is created so that the citation be on the same page as the
float.</description>
</testcase>
+ <testcase>
+ <name>Before-floats plus footnotes on last page</name>
+ <file>before-float_footnote_last-page.xml</file>
+ <description>When an additional page must be created at the end
+ of a flow for placing both a remaining before-float and a footnote, an
+ IndexOutOfBoundException occurs.</description>
+ </testcase>
+ <testcase>
+ <name>Before-float too large to fit on a page alone</name>
+ <file>before-float_large.xml</file>
+ <description>When a before-float is too large to even fit on a page alone,
+ it should be split on several pages. Currently, there is an OutOfMemory
+ error.</description>
+ </testcase>
</disabled-testcases>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a when an additional page must be created at the end
+ of a flow for remaining out-of-line objects, then both the remaining
+ before-floats and footnotes are placed on this page.
+ </p>
+ <p>
+ Currently disabled as this is a non-working feature (footnote is deferred
+ instead of being split).
+ </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="12cm" page-height="5.25cm">
+ <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"
+ space-after.minimum="2pt"
+ space-after.optimum="6pt"
+ space-after.maximum="14pt"
+ widows="1" orphans="1">
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float and a footnote.
+ This is a block with a float and a footnote.
+ This is a block with a float and a footnote.
+ The footnote citation is here<fo:footnote>
+ <fo:inline color="blue">1</fo:inline>
+ <fo:footnote-body color="blue">
+ <fo:block>
+ Footnote body. Footnote body. Footnote body. Footnote body.
+ Footnote body. Footnote body. Footnote body. Footnote body.
+ Footnote body. Footnote body. Footnote body. Footnote body.
+ Footnote body. Footnote body. Footnote body. Footnote body.
+ </fo:block>
+ </fo:footnote-body>
+ </fo:footnote>. The float anchor is <fo:inline color="red">here</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ </fo:float>.
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <!-- no before-float -->
+ <eval expected="0"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- two lines of footnotes -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block)"/>
+ <eval expected="2"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block/lineArea)"/>
+
+ <!-- second page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="57600"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- two remaining lines of footnotes -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/footnote/block)"/>
+ <eval expected="2"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/footnote/block/lineArea)"/>
+ <!-- nothing in the main area -->
+ <eval expected="0"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a before-float too large to fit on a page alone is
+ split on several pages.
+ </p>
+ <p>
+ Currently disabled as this feature still needs some debugging.
+ </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="12cm" page-height="5.25cm">
+ <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"
+ space-after.minimum="2pt"
+ space-after.optimum="6pt"
+ space-after.maximum="14pt"
+ widows="1" orphans="1">
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ The float anchor is <fo:inline color="red">here</fo:inline><fo:float
+ float="before" color="red"
+ space-after.minimum="3pt"
+ space-after.optimum="6pt"
+ space-after.maximum="9">
+ <fo:block space-after="inherit">
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is the float content. This is the float content.
+ This is the float content. This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <!--
+ Well, the result is highly dependent on the implemented algorithm. Tests
+ will have to be added when it is implemented. Currently the float
+ overflows the page instead of being split.
+ -->
+ </checks>
+</testcase>
<eval expected="3"
xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
<!-- only two lines in the last block -->
- <eval expected="2"
+ <eval expected="3"
xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[3]/lineArea)"/>
<!-- second page -->
<eval expected="0"
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks keep-together with overflow conditions.
+ </p>
+ <p>
+ Widows and Orphans are disabled in this test to avoid side-effects.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" widows="0" orphans="0">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+ <fo:region-body overflow="hidden"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="longer" page-width="5in" page-height="5.5 * 14.4pt">
+ <fo:region-body overflow="hidden"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="mix">
+ <fo:repeatable-page-master-reference master-reference="normal" maximum-repeats="2"/>
+ <fo:repeatable-page-master-reference master-reference="longer" maximum-repeats="1"/>
+ <fo:repeatable-page-master-reference master-reference="normal"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="mix">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block1</fo:block>
+ <fo:block>block2</fo:block>
+ <fo:block keep-together.within-page="10" color="blue">
+ <fo:block>block3</fo:block>
+ <fo:block>block4</fo:block>
+ <fo:block>block5</fo:block>
+ <fo:block>block6</fo:block>
+ <fo:block>block7</fo:block>
+ </fo:block>
+ <fo:block>block8</fo:block>
+ <fo:block>block9</fo:block>
+ <fo:block keep-together.within-page="10" color="green">
+ <fo:block>block10</fo:block>
+ <fo:block>block11</fo:block>
+ <fo:block>block12</fo:block>
+ <fo:block>block13</fo:block>
+ <fo:block>block14</fo:block>
+ </fo:block>
+ <fo:block>block15</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block12')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block13')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block14')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block15')]/ancestor::pageViewport/@nr"/>
+ </checks>
+</testcase>
<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">
</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
</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>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks markers 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>
</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
</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>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:marker marker-class-name="test">fo:table</fo:marker>
<fo:table-body>
<fo:table-cell>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:table-body>
<fo:marker marker-class-name="test">fo:table-body</fo:marker>
<fo:table-cell>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:table-header>
<fo:marker marker-class-name="test">fo:table-header</fo:marker>
<fo:table-cell>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:table-footer>
<fo:marker marker-class-name="test">fo:table-footer</fo:marker>
<fo:table-cell>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:table-body>
<fo:table-cell>
<fo:marker marker-class-name="test">fo:table-cell</fo:marker>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table color="black">
+ <fo:table color="black" table-layout="fixed">
<fo:marker marker-class-name="test">table1</fo:marker>
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
</fo:table-row>
</fo:table-body>
</fo:table>
- <fo:table color="red">
+ <fo:table color="red" table-layout="fixed">
<fo:marker marker-class-name="test">table2</fo:marker>
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
</fo:table-row>
</fo:table-body>
</fo:table>
- <fo:table color="blue">
+ <fo:table color="blue" table-layout="fixed">
<fo:marker marker-class-name="test">table3</fo:marker>
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
- <fo:table color="black">
+ <fo:table color="black" table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
<fo:marker marker-class-name="test">body1</fo:marker>
</fo:table-row>
</fo:table-body>
</fo:table>
- <fo:table color="red">
+ <fo:table color="red" table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
<fo:marker marker-class-name="test">body2</fo:marker>
</fo:table-row>
</fo:table-body>
</fo:table>
- <fo:table color="blue">
+ <fo:table color="blue" table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
<fo:marker marker-class-name="test">body3</fo:marker>
</fo:table-row>
</fo:table-body>
</fo:table>
- <fo:table>
+ <fo:table table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body color="black">
<fo:marker marker-class-name="test">body5</fo:marker>
</fo:layout-master-set>
<fo:page-sequence master-reference="normal" white-space-collapse="true">
<fo:flow flow-name="xsl-region-body">
- <fo:table color="black">
+ <fo:table color="black" table-layout="fixed">
<fo:table-column number-columns-repeated="2"/>
<fo:table-body>
<fo:table-row>
</fo:layout-master-set>\r
<fo:page-sequence master-reference="normal" white-space-collapse="true">\r
<fo:flow flow-name="xsl-region-body">\r
- <fo:table border-collapse="separate">\r
+ <fo:table border-collapse="separate" table-layout="fixed">\r
<fo:table-column />\r
<fo:table-column />\r
<fo:table-column />\r
</fo:layout-master-set>\r
<fo:page-sequence master-reference="normal" white-space-collapse="true">\r
<fo:flow flow-name="xsl-region-body">\r
- <fo:table>\r
+ <fo:table table-layout="fixed">\r
<fo:table-column column-number="1" />\r
<fo:table-column column-number="2" />\r
<fo:table-column column-number="3" />\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks lists with nested fo:tables. Table headers used to be
+ swallowed.
+ </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="3in" page-height="1in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:list-block>
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>1.</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:table table-layout="fixed" width="100%" border-collapse="separate" id="table">
+ <fo:table-column column-width="100pt" text-align="right"/>
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm" id="header-cell">
+ <fo:block text-align="center">Header</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">1</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">3</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">4</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">5</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">6</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">7</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+ <fo:block text-align="right">8</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:list-item-body>
+ </fo:list-item>
+ </fo:list-block>
+ <fo:block font-size="8pt">
+ <fo:block>trailing text...</fo:block>
+ <fo:block>trailing text...</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <true xpath="boolean(//pageViewport[@nr='1']//block[@prod-id='header-cell'])"/>
+ <eval expected="4" xpath="count(//pageViewport[@nr='1']//block[@prod-id='table']/block)"/>
+
+ <true xpath="boolean(//pageViewport[@nr='2']//block[@prod-id='header-cell'])"/>
+ <eval expected="4" xpath="count(//pageViewport[@nr='2']//block[@prod-id='table']/block)"/>
+
+ <true xpath="boolean(//pageViewport[@nr='3']//block[@prod-id='header-cell'])"/>
+ <eval expected="3" xpath="count(//pageViewport[@nr='3']//block[@prod-id='table']/block)"/>
+
+ </checks>
+</testcase>
</fo:layout-master-set>\r
<fo:page-sequence master-reference="normal" white-space-collapse="true">\r
<fo:flow flow-name="xsl-region-body">\r
- <fo:table>\r
+ <fo:table table-layout="fixed">\r
<fo:table-column column-number="1" />\r
<fo:table-column column-number="2" />\r
<fo:table-column column-number="3" />\r
</fo:layout-master-set>\r
<fo:page-sequence master-reference="normal" white-space-collapse="true">\r
<fo:flow flow-name="xsl-region-body">\r
- <fo:table background-color="yellow" background-repeat="no-repeat" background-position-horizontal="center" background-position-vertical="center" background-image="##img">\r
+ <fo:table table-layout="fixed" background-color="yellow" \r
+ background-repeat="no-repeat" \r
+ background-position-horizontal="center" \r
+ background-position-vertical="center" \r
+ background-image="##img">\r
<fo:table-column column-number="1" />\r
<fo:table-column column-number="2" />\r
<fo:table-column column-number="3" />\r
</fo:layout-master-set>\r
<fo:page-sequence master-reference="normal" white-space-collapse="true">\r
<fo:flow flow-name="xsl-region-body">\r
- <fo:table margin="0pt" padding="5%" background-color="yellow" border-collapse="separate">\r
+ <fo:table table-layout="fixed" margin="0pt" padding="5%" \r
+ background-color="yellow" border-collapse="separate">\r
<fo:table-column column-number="1" />\r
<fo:table-column column-number="2" />\r
<fo:table-column column-number="3" />\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<!-- Description: Splits an FO file into multiple FO files at page-sequence boundaries. -->
+<!-- Note: Requires Xalan-J! -->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:redirect="http://xml.apache.org/xalan/redirect"
+ extension-element-prefixes="redirect">
+
+ <xsl:template match="fo:page-sequence">
+ <redirect:write select="concat('page-sequence-', position(), '.fo')">
+ <fo:root>
+ <xsl:apply-templates select="../fo:layout-master-set"/>
+ <xsl:apply-templates select="../fo:declarations"/>
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </fo:root>
+ </redirect:write>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <xsl:param name="repeats" select="2"/>
+ <xsl:template match="@*|node()">
+ <xsl:param name="run"/>
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()">
+ <xsl:with-param name="run" select="$run"/>
+ </xsl:apply-templates>
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="fo:root">
+ <fo:root>
+ <xsl:apply-templates select="@*|fo:layout-master-set|fo:declarations">
+ <xsl:with-param name="run" select="0"/>
+ </xsl:apply-templates>
+ <xsl:call-template name="repeat">
+ <xsl:with-param name="n" select="$repeats"/>
+ <xsl:with-param name="what" select="fo:page-sequence"/>
+ </xsl:call-template>
+ </fo:root>
+ </xsl:template>
+ <xsl:template name="repeat">
+ <xsl:param name="n"/>
+ <xsl:param name="what"/>
+ <xsl:if test="number($n) > 0">
+ <xsl:apply-templates select="$what">
+ <xsl:with-param name="run" select="$n"/>
+ </xsl:apply-templates>
+ <xsl:call-template name="repeat">
+ <xsl:with-param name="n" select="number($n) - 1"/>
+ <xsl:with-param name="what" select="$what"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+ <xsl:template match="@id">
+ <xsl:param name="run"/>
+ <xsl:attribute name="id"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+ </xsl:template>
+ <xsl:template match="@ref-id">
+ <xsl:param name="run"/>
+ <xsl:attribute name="ref-id"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+ </xsl:template>
+ <xsl:template match="@internal-destination">
+ <xsl:param name="run"/>
+ <xsl:attribute name="internal-destination"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+ </xsl:template>
+ <xsl:template match="fo:page-number-citation">
+ <xsl:param name="run"/>
+ <fo:inline><xsl:value-of select="@ref-id"/></fo:inline>
+ </xsl:template>
+ <xsl:template match="fo:retrieve-marker|fo:marker">
+ <xsl:param name="run"/>
+ </xsl:template>
+</xsl:stylesheet>