aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache')
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java3
-rw-r--r--src/java/org/apache/fop/apps/FopFactory.java7
-rw-r--r--src/java/org/apache/fop/area/BodyRegion.java9
-rw-r--r--src/java/org/apache/fop/area/MainReference.java6
-rw-r--r--src/java/org/apache/fop/fo/FONode.java73
-rw-r--r--src/java/org/apache/fop/fo/FObj.java82
-rw-r--r--src/java/org/apache/fop/fo/FObjMixed.java2
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractGraphics.java10
-rw-r--r--src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java28
-rw-r--r--src/java/org/apache/fop/fo/pagination/Declarations.java2
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequence.java10
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java18
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java17
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java5
-rw-r--r--src/java/org/apache/fop/fo/pagination/Root.java10
-rw-r--r--src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java5
-rw-r--r--src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java5
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java226
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java8
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java17
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml2
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreaker.java128
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java8
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageProvider.java35
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java4
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFPattern.java2
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java2
-rw-r--r--src/java/org/apache/fop/render/pcl/Java2DRendererOption.java3
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java2
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLGenerator.java398
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java2
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java2
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java14
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRendererConfig.java11
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java4
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java19
-rw-r--r--src/java/org/apache/fop/render/ps/Gradient.java85
39 files changed, 1015 insertions, 261 deletions
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index 9e317f9a3..6434ce36e 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -134,6 +134,7 @@ public class FOUserAgent {
protected String keywords;
private final ImageSessionContext imageSessionContext;
+ private final SoftMapCache pdfObjectCache = new SoftMapCache(true);
/**
* Main constructor. <b>This constructor should not be called directly. Please use the
@@ -807,6 +808,6 @@ public class FOUserAgent {
}
public SoftMapCache getPDFObjectCache() {
- return factory.getPDFObjectCache();
+ return pdfObjectCache;
}
}
diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java
index 50211d3bc..d9c9708eb 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -37,7 +37,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver;
-import org.apache.xmlgraphics.image.loader.util.SoftMapCache;
import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.apps.io.InternalResourceResolver;
@@ -83,8 +82,6 @@ public final class FopFactory implements ImageContext {
private final ColorSpaceCache colorSpaceCache;
- private final SoftMapCache pdfObjectCache = new SoftMapCache(true);
-
private final FopFactoryConfig config;
private final InternalResourceResolver resolver;
@@ -436,8 +433,4 @@ public final class FopFactory implements ImageContext {
public ColorSpaceCache getColorSpaceCache() {
return this.colorSpaceCache;
}
-
- public SoftMapCache getPDFObjectCache() {
- return pdfObjectCache;
- }
}
diff --git a/src/java/org/apache/fop/area/BodyRegion.java b/src/java/org/apache/fop/area/BodyRegion.java
index 89bb206f2..df7e914b7 100644
--- a/src/java/org/apache/fop/area/BodyRegion.java
+++ b/src/java/org/apache/fop/area/BodyRegion.java
@@ -81,6 +81,15 @@ public class BodyRegion extends RegionReference {
return this.columnGap;
}
+ int getContentIPD() {
+ RegionViewport rv = getRegionViewport();
+ return getIPD() - rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
+ }
+
+ public int getColumnIPD() {
+ return (getContentIPD() - (columnCount - 1) * columnGap) / columnCount;
+ }
+
/**
* Get the main reference area.
*
diff --git a/src/java/org/apache/fop/area/MainReference.java b/src/java/org/apache/fop/area/MainReference.java
index efc16515d..9778db87f 100644
--- a/src/java/org/apache/fop/area/MainReference.java
+++ b/src/java/org/apache/fop/area/MainReference.java
@@ -59,12 +59,8 @@ public class MainReference extends Area {
//Remove the current one if it is empty
spanAreas.remove(spanAreas.size() - 1);
}
- RegionViewport rv = parent.getRegionViewport();
- int ipdWidth = parent.getIPD()
- - rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
-
Span newSpan = new Span(((spanAll) ? 1 : getColumnCount()),
- getColumnGap(), ipdWidth);
+ getColumnGap(), parent.getContentIPD());
spanAreas.add(newSpan);
return getCurrentSpan();
}
diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java
index c2c9940ca..f5eb8854e 100644
--- a/src/java/org/apache/fop/fo/FONode.java
+++ b/src/java/org/apache/fop/fo/FONode.java
@@ -20,7 +20,6 @@
package org.apache.fop.fo;
// Java
-import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.Stack;
@@ -321,9 +320,9 @@ public abstract class FONode implements Cloneable {
* has been reached.
* The default implementation simply calls {@link #finalizeNode()}, without
* sending any event to the {@link FOEventHandler}.
- * <br/><i>Note: the recommended way to override this method in subclasses is</i>
- * <br/><br/><code>super.endOfNode(); // invoke finalizeNode()
- * <br/>getFOEventHandler().endXXX(); // send endOfNode() notification</code>
+ * <p><i>Note: the recommended way to override this method in subclasses is</i></p>
+ * <p><code>super.endOfNode(); // invoke finalizeNode()</code></p>
+ * <p><code>getFOEventHandler().endXXX(); // send endOfNode() notification</code></p>
*
* @throws FOPException if there's a problem during processing
*/
@@ -622,7 +621,7 @@ public abstract class FONode implements Cloneable {
*
* @param propertyName the name of the property.
* @param propertyValue the value of the property.
- * * @param e optional property parsing exception.
+ * @param e optional property parsing exception.
* @throws ValidationException the validation error provoked by the method call
*/
protected void invalidPropertyValueError(String propertyName, String propertyValue, Exception e)
@@ -936,7 +935,7 @@ public abstract class FONode implements Cloneable {
* @param ranges a stack of delimited text ranges
* @return the (possibly) updated stack of delimited text ranges
*/
- public Stack collectDelimitedTextRanges(Stack<DelimitedTextRange> ranges) {
+ public Stack<DelimitedTextRange> collectDelimitedTextRanges(Stack<DelimitedTextRange> ranges) {
// if boundary before, then push new range
if (isRangeBoundaryBefore()) {
maybeNewRange(ranges);
@@ -965,9 +964,11 @@ public abstract class FONode implements Cloneable {
* @param currentRange the current range or null (if none)
* @return the (possibly) updated stack of delimited text ranges
*/
- protected Stack collectDelimitedTextRanges(Stack<DelimitedTextRange> ranges, DelimitedTextRange currentRange) {
- for (Iterator it = getChildNodes(); (it != null) && it.hasNext();) {
- ranges = ((FONode) it.next()).collectDelimitedTextRanges(ranges);
+ protected Stack<DelimitedTextRange> collectDelimitedTextRanges(
+ Stack<DelimitedTextRange> ranges, DelimitedTextRange currentRange) {
+
+ for (FONodeIterator it = getChildNodes(); (it != null) && it.hasNext();) {
+ ranges = it.next().collectDelimitedTextRanges(ranges);
}
return ranges;
}
@@ -1011,33 +1012,55 @@ public abstract class FONode implements Cloneable {
}
/**
- * Base iterator interface over a FO's children
+ * Base iterator interface over a FO's children, offering three methods on top of the base interface
+ * methods {@see java.util.ListIterator}.
*/
- public interface FONodeIterator extends ListIterator {
+ public interface FONodeIterator extends ListIterator<FONode> {
+
+ /** @return the next node */
+ FONode next();
+
+ /** @return the previous node */
+ FONode previous();
/**
- * Returns the parent node for this iterator's list
- * of child nodes
+ * Replace the node at the current index with the given <code>newNode</code>.
*
- * @return the parent node
+ * @param newNode the new node
*/
- FObj parentNode();
+ void set(FONode newNode);
/**
- * Convenience method with return type of FONode
- * (semantically equivalent to: <code>(FONode) next();</code>)
+ * Add the given <code>newNode</code> at the current position.
*
- * @return the next node (if any), as a type FONode
+ * @param newNode the new node
+ */
+ void add(FONode newNode);
+
+ /** @return <code>true</code> if there is a next node, <code>false</code> otherwise */
+ boolean hasNext();
+
+ /** @return <code>true</code> if there is a previous node, <code>false</code> otherwise */
+ boolean hasPrevious();
+
+ /** @return the current index */
+ int nextIndex();
+
+ /** @return the previous index */
+ int previousIndex();
+
+ /**
+ * Removes the node at the current position.
*/
- FONode nextNode();
+ void remove();
/**
- * Convenience method with return type of FONode
- * (semantically equivalent to: <code>(FONode) previous();</code>)
+ * Returns the parent node for this iterator's list
+ * of child nodes
*
- * @return the previous node (if any), as a type FONode
+ * @return the parent node
*/
- FONode previousNode();
+ FObj parent();
/**
* Returns the first node in the list, and decreases the index,
@@ -1046,7 +1069,7 @@ public abstract class FONode implements Cloneable {
*
* @return the first node in the list
*/
- FONode firstNode();
+ FONode first();
/**
* Returns the last node in the list, and advances the
@@ -1055,7 +1078,7 @@ public abstract class FONode implements Cloneable {
*
* @return the last node in the list
*/
- FONode lastNode();
+ FONode last();
}
diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java
index a649bfe6f..ce983dfb2 100644
--- a/src/java/org/apache/fop/fo/FObj.java
+++ b/src/java/org/apache/fop/fo/FObj.java
@@ -21,9 +21,7 @@ package org.apache.fop.fo;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
@@ -37,7 +35,6 @@ import org.apache.xmlgraphics.util.QName;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.flow.Marker;
-import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.PropertyMaker;
@@ -198,7 +195,7 @@ public abstract class FObj extends FONode implements Constants {
*/
private void checkId(String id) throws ValidationException {
if (!inMarker() && !id.equals("")) {
- Set idrefs = getBuilderContext().getIDReferences();
+ Set<String> idrefs = getBuilderContext().getIDReferences();
if (!idrefs.contains(id)) {
idrefs.add(id);
} else {
@@ -270,7 +267,7 @@ public abstract class FObj extends FONode implements Constants {
if (firstChild != null) {
firstChild.siblings[0] = null;
}
- } else {
+ } else if (child.siblings != null) {
FONode prevChild = child.siblings[0];
prevChild.siblings[1] = nextChild;
if (nextChild != null) {
@@ -328,7 +325,7 @@ public abstract class FObj extends FONode implements Constants {
* at the passed-in node (= first call to iterator.next() will
* return childNode)
* @param childNode First node in the iterator
- * @return A ListIterator or null if childNode isn't a child of
+ * @return A FONodeIterator or null if childNode isn't a child of
* this FObj.
*/
public FONodeIterator getChildNodes(FONode childNode) {
@@ -338,7 +335,7 @@ public abstract class FObj extends FONode implements Constants {
return it;
} else {
while (it.hasNext()
- && it.nextNode().siblings[1] != childNode) {
+ && it.next().siblings[1] != childNode) {
//nop
}
if (it.hasNext()) {
@@ -373,8 +370,8 @@ public abstract class FObj extends FONode implements Constants {
String mcname = marker.getMarkerClassName();
if (firstChild != null) {
// check for empty childNodes
- for (Iterator iter = getChildNodes(); iter.hasNext();) {
- FONode node = (FONode) iter.next();
+ for (FONodeIterator iter = getChildNodes(); iter.hasNext();) {
+ FONode node = iter.next();
if (node instanceof FObj
|| (node instanceof FOText
&& ((FOText) node).willCreateArea())) {
@@ -414,7 +411,7 @@ public abstract class FObj extends FONode implements Constants {
/** {@inheritDoc} */
protected String getContextInfoAlt() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
if (getLocalName() != null) {
sb.append(getName());
sb.append(", ");
@@ -444,13 +441,13 @@ public abstract class FObj extends FONode implements Constants {
if (getLocator() != null) {
return super.gatherContextInfo();
} else {
- ListIterator iter = getChildNodes();
+ FONodeIterator iter = getChildNodes();
if (iter == null) {
return null;
}
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
while (iter.hasNext()) {
- FONode node = (FONode) iter.next();
+ FONode node = iter.next();
String s = node.gatherContextInfo();
if (s != null) {
if (sb.length() > 0) {
@@ -466,7 +463,7 @@ public abstract class FObj extends FONode implements Constants {
/**
* Convenience method for validity checking. Checks if the
* incoming node is a member of the "%block;" parameter entity
- * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations
+ * as defined in Sect. 6.2 of the XSL 1.0 &amp; 1.1 Recommendations
*
* @param nsURI namespace URI of incoming node
* @param lName local name (i.e., no prefix) of incoming node
@@ -486,7 +483,7 @@ public abstract class FObj extends FONode implements Constants {
/**
* Convenience method for validity checking. Checks if the
* incoming node is a member of the "%inline;" parameter entity
- * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations
+ * as defined in Sect. 6.2 of the XSL 1.0 &amp; 1.1 Recommendations
*
* @param nsURI namespace URI of incoming node
* @param lName local name (i.e., no prefix) of incoming node
@@ -528,7 +525,7 @@ public abstract class FObj extends FONode implements Constants {
/**
* Convenience method for validity checking. Checks if the
* incoming node is a member of the neutral item list
- * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations
+ * as defined in Sect. 6.2 of the XSL 1.0 &amp; 1.1 Recommendations
* @param nsURI namespace URI of incoming node
* @param lName local name (i.e., no prefix) of incoming node
* @return true if a member, false if not
@@ -554,11 +551,6 @@ public abstract class FObj extends FONode implements Constants {
int found = 1;
FONode temp = getParent();
while (temp != null) {
- if (temp instanceof TableCell && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) {
- // note that if the retrieve-table-marker is not in a table-header/footer an exception is
- // thrown, so no need to reset this flag in that case
- ((TableCell) temp).flagAsHavingRetrieveTableMarker();
- }
if (temp.getNameId() == ancestorID) {
return found;
}
@@ -619,16 +611,16 @@ public abstract class FObj extends FONode implements Constants {
* @param bidiLevel a non-negative bidi embedding level
*/
public void setBidiLevel(int bidiLevel) {
+
assert bidiLevel >= 0;
- if (bidiLevel >= 0) {
- if ((this.bidiLevel < 0) || (bidiLevel < this.bidiLevel)) {
- this.bidiLevel = bidiLevel;
- if ((parent != null) && !isBidiPropagationBoundary()) {
- FObj foParent = (FObj) parent;
- int parentBidiLevel = foParent.getBidiLevel();
- if ((parentBidiLevel < 0) || (bidiLevel < parentBidiLevel)) {
- foParent.setBidiLevel(bidiLevel);
- }
+
+ if ((this.bidiLevel < 0) || (bidiLevel < this.bidiLevel)) {
+ this.bidiLevel = bidiLevel;
+ if ((parent != null) && !isBidiPropagationBoundary()) {
+ FObj foParent = (FObj) parent;
+ int parentBidiLevel = foParent.getBidiLevel();
+ if ((parentBidiLevel < 0) || (bidiLevel < parentBidiLevel)) {
+ foParent.setBidiLevel(bidiLevel);
}
}
}
@@ -765,12 +757,12 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public FObj parentNode() {
+ public FObj parent() {
return parentNode;
}
/** {@inheritDoc} */
- public Object next() {
+ public FONode next() {
if (currentNode != null) {
if (currentIndex != 0) {
if (currentNode.siblings != null
@@ -789,7 +781,7 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public Object previous() {
+ public FONode previous() {
if (currentNode.siblings != null
&& currentNode.siblings[0] != null) {
currentIndex--;
@@ -802,9 +794,8 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public void set(Object o) {
+ public void set(FONode newNode) {
if ((flags & F_SET_ALLOWED) == F_SET_ALLOWED) {
- FONode newNode = (FONode) o;
if (currentNode == parentNode.firstChild) {
parentNode.firstChild = newNode;
} else {
@@ -823,8 +814,7 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public void add(Object o) {
- FONode newNode = (FONode) o;
+ public void add(FONode newNode) {
if (currentIndex == -1) {
if (currentNode != null) {
FONode.attachSiblings(newNode, currentNode);
@@ -838,9 +828,9 @@ public abstract class FObj extends FONode implements Constants {
} else {
if (currentNode.siblings != null
&& currentNode.siblings[1] != null) {
- FONode.attachSiblings((FONode) o, currentNode.siblings[1]);
+ FONode.attachSiblings(newNode, currentNode.siblings[1]);
}
- FONode.attachSiblings(currentNode, (FONode) o);
+ FONode.attachSiblings(currentNode, newNode);
if (currentNode == parentNode.lastChild) {
parentNode.lastChild = newNode;
}
@@ -894,7 +884,7 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public FONode lastNode() {
+ public FONode last() {
while (currentNode != null
&& currentNode.siblings != null
&& currentNode.siblings[1] != null) {
@@ -905,20 +895,10 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
- public FONode firstNode() {
+ public FONode first() {
currentNode = parentNode.firstChild;
currentIndex = 0;
return currentNode;
}
-
- /** {@inheritDoc} */
- public FONode nextNode() {
- return (FONode) next();
- }
-
- /** {@inheritDoc} */
- public FONode previousNode() {
- return (FONode) previous();
- }
}
}
diff --git a/src/java/org/apache/fop/fo/FObjMixed.java b/src/java/org/apache/fop/fo/FObjMixed.java
index f66b095b1..6d712c9ce 100644
--- a/src/java/org/apache/fop/fo/FObjMixed.java
+++ b/src/java/org/apache/fop/fo/FObjMixed.java
@@ -149,7 +149,7 @@ public abstract class FObjMixed extends FObj {
= this.getChildNodes(this.currentTextNode);
FONode node;
while (nodeIter.hasNext()) {
- node = nodeIter.nextNode();
+ node = nodeIter.next();
assert (node instanceof FOText
|| node.getNameId() == FO_CHARACTER);
if (node.getNameId() == FO_CHARACTER) {
diff --git a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
index 34bf83fb4..9f56abb43 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
@@ -61,7 +61,6 @@ public abstract class AbstractGraphics extends FObj
private int displayAlign;
private int dominantBaseline;
private Length height;
- private String id;
private LengthRangeProperty inlineProgressionDimension;
private KeepProperty keepWithNext;
private KeepProperty keepWithPrevious;
@@ -92,6 +91,7 @@ public abstract class AbstractGraphics extends FObj
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
+ super.bind(pList);
commonAccessibility = CommonAccessibility.getInstance(pList);
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength();
@@ -104,7 +104,6 @@ public abstract class AbstractGraphics extends FObj
displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum();
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
height = pList.get(PR_HEIGHT).getLength();
- id = pList.get(PR_ID).getString();
inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
@@ -126,13 +125,6 @@ public abstract class AbstractGraphics extends FObj
return commonAccessibility;
}
- /**
- * @return the "id" property.
- */
- public String getId() {
- return id;
- }
-
/** @return the {@link CommonBorderPaddingBackground} */
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return commonBorderPaddingBackground;
diff --git a/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java
index 57ae9e7bb..8340736c7 100644
--- a/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java
+++ b/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java
@@ -25,6 +25,7 @@ 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.flow.table.TableCell;
/**
* Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_retrieve-table-marker">
@@ -81,6 +82,33 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker {
}
/**
+ * Overridden to flag the ancestor table-cell.
+ *
+ * @param ancestorID ID of node name to check for (e.g., FO_ROOT)
+ * @return number of levels above FO where ancestor exists,
+ * -1 if not found
+ */
+ @Override
+ protected int findAncestor(int ancestorID) {
+ int found = 1;
+ FONode temp = getParent();
+ while (temp != null) {
+ if (temp instanceof TableCell
+ && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) {
+ // note that if the retrieve-table-marker is not in a table-header/footer an exception is
+ // thrown, so no need to reset this flag in that case
+ ((TableCell) temp).flagAsHavingRetrieveTableMarker();
+ }
+ if (temp.getNameId() == ancestorID) {
+ return found;
+ }
+ found += 1;
+ temp = temp.getParent();
+ }
+ return -1;
+ }
+
+ /**
* Return the value for the <code>retrieve-position-within-table</code>
* property
* @return the value for retrieve-position-within-table; one of
diff --git a/src/java/org/apache/fop/fo/pagination/Declarations.java b/src/java/org/apache/fop/fo/pagination/Declarations.java
index 33c688921..be1ec13e2 100644
--- a/src/java/org/apache/fop/fo/pagination/Declarations.java
+++ b/src/java/org/apache/fop/fo/pagination/Declarations.java
@@ -78,7 +78,7 @@ public class Declarations extends FObj {
public void endOfNode() throws FOPException {
if (firstChild != null) {
for (FONodeIterator iter = getChildNodes(); iter.hasNext();) {
- FONode node = iter.nextNode();
+ FONode node = iter.next();
if (node.getName().equals("fo:color-profile")) {
ColorProfile cp = (ColorProfile)node;
if (!"".equals(cp.getColorProfileName())) {
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java
index 6afd15e81..a1a966e90 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequence.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java
@@ -454,4 +454,14 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra
this.flowMap.clear();
}
+ public SimplePageMaster getLastSimplePageMaster(int page, boolean isFirstPage, boolean isBlank) {
+ boolean isOddPage = ((page % 2) != 0); // please findbugs...
+ log.debug("getNextSimplePageMaster(page=" + page + " isOdd=" + isOddPage + " isFirst="
+ + isFirstPage + " isLast=true" + " isBlank=" + isBlank + ")");
+ if (pageSequenceMaster == null) {
+ return simplePageMaster;
+ }
+ return pageSequenceMaster.getLastSimplePageMaster(isOddPage, isFirstPage, isBlank, getMainFlow()
+ .getFlowName());
+ }
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
index 86d5ff663..f218e43b4 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
@@ -254,6 +254,24 @@ public class PageSequenceMaster extends FObj {
return FO_PAGE_SEQUENCE_MASTER;
}
+ public SimplePageMaster getLastSimplePageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlank,
+ String flowName) {
+ if (currentSubSequence == null) {
+ currentSubSequence = getNextSubSequence();
+ if (currentSubSequence == null) {
+ blockLevelEventProducer.missingSubsequencesInPageSequenceMaster(this, masterName,
+ getLocator());
+ }
+ if (currentSubSequence.isInfinite() && !currentSubSequence.canProcess(flowName)) {
+ throw new PageProductionException(
+ "The current sub-sequence will not terminate whilst processing the main flow");
+ }
+ }
+
+ SimplePageMaster pageMaster = currentSubSequence.getLastPageMaster(isOddPage, isFirstPage, isBlank,
+ blockLevelEventProducer);
+ return pageMaster;
+ }
}
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
index dc69c600d..2914fb9a8 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
@@ -31,6 +31,7 @@ import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.Property;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
/**
* Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_repeatable-page-master-alternatives">
@@ -136,6 +137,22 @@ public class RepeatablePageMasterAlternatives extends FObj
return null;
}
+ public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage,
+ BlockLevelEventProducer blockLevelEventProducer) {
+ for (ConditionalPageMasterReference cpmr : conditionalPageMasterRefs) {
+ if (cpmr.isValid(isOddPage, isFirstPage, true, isBlankPage)) {
+ return cpmr.getMaster();
+ }
+ }
+ blockLevelEventProducer.lastPageMasterReferenceMissing(this, getLocator());
+ for (ConditionalPageMasterReference cpmr : conditionalPageMasterRefs) {
+ if (cpmr.isValid(isOddPage, isFirstPage, false, isBlankPage)) {
+ return cpmr.getMaster();
+ }
+ }
+ throw new PageProductionException("Last page master not found: oddpage=" + isOddPage
+ + " firstpage=" + isFirstPage + " blankpage=" + isBlankPage);
+ }
/**
* Adds a new conditional page master reference.
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
index f6d41ce8b..5e43c02b1 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
@@ -101,6 +101,11 @@ public class RepeatablePageMasterReference extends FObj
return master;
}
+ public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isEmptyPage,
+ BlockLevelEventProducer blockLevelEventProducer) {
+ return getNextPageMaster(isOddPage, isFirstPage, true, isEmptyPage);
+ }
+
/**
* Get the value of the <code>maximum-repeats</code> property.
* @return the "maximum-repeats" property
diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java
index cb433a064..51309a65d 100644
--- a/src/java/org/apache/fop/fo/pagination/Root.java
+++ b/src/java/org/apache/fop/fo/pagination/Root.java
@@ -75,6 +75,16 @@ public class Root extends FObj implements CommonAccessibilityHolder {
*/
private FOEventHandler foEventHandler;
+ private PageSequence lastSeq;
+
+ public void setLastSeq(PageSequence seq) {
+ lastSeq = seq;
+ }
+
+ public PageSequence getLastSeq() {
+ return lastSeq;
+ }
+
/**
* Base constructor
*
diff --git a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
index ed0c041dd..2600909cb 100644
--- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
@@ -100,6 +100,11 @@ public class SinglePageMasterReference extends FObj
}
}
+ public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage,
+ BlockLevelEventProducer blockLevelEventProducer) {
+ return getNextPageMaster(isOddPage, isFirstPage, true, isBlankPage);
+ }
+
/** {@inheritDoc} */
public void reset() {
this.state = FIRST;
diff --git a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
index 271d80a95..0905ee8a8 100644
--- a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
+++ b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
@@ -20,6 +20,7 @@
package org.apache.fop.fo.pagination;
import org.apache.fop.fo.ValidationException;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
/**
* Classes that implement this interface can be added to a {@link PageSequenceMaster},
@@ -43,6 +44,10 @@ public interface SubSequenceSpecifier {
boolean isBlankPage)
throws PageProductionException;
+ SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage,
+ BlockLevelEventProducer blockLevelEventProducer)
+ throws PageProductionException;
+
/**
* Called before a new page sequence is rendered so subsequences can reset
* any state they keep during the formatting process.
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
index e1c6b3a74..d0594ce8a 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
@@ -42,6 +42,10 @@ public abstract class AbstractBreaker {
/** logging instance */
protected static final Log log = LogFactory.getLog(AbstractBreaker.class);
+ private LayoutManager originalRestartAtLM;
+ private Position positionAtBreak;
+ private List firstElementsForRestart;
+
/**
* A page break position.
*/
@@ -408,17 +412,36 @@ public abstract class AbstractBreaker {
alg.setConstantLineWidth(flowBPD);
int optimalPageCount = alg.findBreakingPoints(blockList, 1, true,
BreakingAlgorithm.ALL_BREAKS);
-
+ boolean ipdChangesOnNextPage = (alg.getIPDdifference() != 0);
+ boolean onLastPageAndIPDChanges = false;
+ if (!ipdChangesOnNextPage) {
+ onLastPageAndIPDChanges = (lastPageHasIPDChange() && !thereIsANonRestartableLM(alg)
+ && (shouldRedoLayout() || (wasLayoutRedone() && optimalPageCount > 1)));
+ }
if (alg.handlingFloat()) {
nextSequenceStartsOn = handleFloatLayout(alg, optimalPageCount, blockList, childLC);
- } else if (Math.abs(alg.getIPDdifference()) > 1) {
- addAreas(alg, optimalPageCount, blockList, blockList);
- // *** redo Phase 1 ***
- log.trace("IPD changes after page " + optimalPageCount);
+ } else if (ipdChangesOnNextPage || onLastPageAndIPDChanges) {
+ boolean visitedBefore = false;
+ if (onLastPageAndIPDChanges) {
+ visitedBefore = wasLayoutRedone();
+ prepareToRedoLayout(alg, optimalPageCount, blockList, blockList);
+ }
+
+ firstElementsForRestart = null;
+ LayoutManager restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges,
+ visitedBefore, blockList, 1);
+ if (restartAtLM == null) {
+ firstElementsForRestart = null;
+ restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges,
+ visitedBefore, blockList, 0);
+ }
+ if (ipdChangesOnNextPage) {
+ addAreas(alg, optimalPageCount, blockList, blockList);
+ }
blockLists.clear();
- nextSequenceStartsOn = getNextBlockListChangedIPD(childLC, alg,
- blockList);
blockListIndex = -1;
+ nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, positionAtBreak,
+ restartAtLM, firstElementsForRestart);
} else {
log.debug("PLM> optimalPageCount= " + optimalPageCount
+ " pageBreaks.size()= " + alg.getPageBreaks().size());
@@ -433,6 +456,92 @@ public abstract class AbstractBreaker {
blockLists = null;
}
+ private LayoutManager getRestartAtLM(PageBreakingAlgorithm alg, boolean ipdChangesOnNextPage,
+ boolean onLastPageAndIPDChanges, boolean visitedBefore,
+ BlockSequence blockList, int start) {
+ KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg
+ .getBestNodeForLastPage();
+ if (onLastPageAndIPDChanges && visitedBefore && this.originalRestartAtLM == null) {
+ optimalBreak = null;
+ }
+
+ int positionIndex = (optimalBreak != null) ? optimalBreak.position : start;
+ KnuthElement elementAtBreak = alg.getElement(positionIndex);
+ if (elementAtBreak.getPosition() == null) {
+ elementAtBreak = alg.getElement(0);
+ }
+ positionAtBreak = elementAtBreak.getPosition();
+ /* Retrieve the original position wrapped into this space position */
+ positionAtBreak = positionAtBreak.getPosition();
+ if (ipdChangesOnNextPage || (positionAtBreak != null && positionAtBreak.getIndex() > -1)) {
+ firstElementsForRestart = Collections.EMPTY_LIST;
+ if (ipdChangesOnNextPage) {
+ if (containsNonRestartableLM(positionAtBreak)) {
+ if (alg.getIPDdifference() > 0) {
+ EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj()
+ .getUserAgent().getEventBroadcaster();
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider
+ .get(eventBroadcaster);
+ eventProducer.nonRestartableContentFlowingToNarrowerPage(this);
+ }
+ firstElementsForRestart = new LinkedList();
+ boolean boxFound = false;
+ Iterator iter = blockList.listIterator(positionIndex + 1);
+ Position position = null;
+ while (iter.hasNext()
+ && (position == null || containsNonRestartableLM(position))) {
+ positionIndex++;
+ KnuthElement element = (KnuthElement) iter.next();
+ position = element.getPosition();
+ if (element.isBox()) {
+ boxFound = true;
+ firstElementsForRestart.add(element);
+ } else if (boxFound) {
+ firstElementsForRestart.add(element);
+ }
+ }
+ if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) {
+ /* Retrieve the original position wrapped into this space position */
+ positionAtBreak = position.getPosition();
+ } else {
+ positionAtBreak = null;
+ }
+ }
+ }
+ }
+ LayoutManager restartAtLM = null;
+ if (ipdChangesOnNextPage || !(positionAtBreak != null && positionAtBreak.getIndex() > -1)) {
+ if (positionAtBreak != null && positionAtBreak.getIndex() == -1) {
+ Position position;
+ Iterator iter = blockList.listIterator(positionIndex + 1);
+ do {
+ KnuthElement nextElement = (KnuthElement) iter.next();
+ position = nextElement.getPosition();
+ } while (position == null
+ || position instanceof SpaceResolver.SpaceHandlingPosition
+ || position instanceof SpaceResolver.SpaceHandlingBreakPosition
+ && position.getPosition().getIndex() == -1);
+ LayoutManager surroundingLM = positionAtBreak.getLM();
+ while (position.getLM() != surroundingLM) {
+ position = position.getPosition();
+ }
+ restartAtLM = position.getPosition().getLM();
+ }
+ if (onLastPageAndIPDChanges && restartAtLM != null) {
+ if (originalRestartAtLM == null) {
+ originalRestartAtLM = restartAtLM;
+ } else {
+ restartAtLM = originalRestartAtLM;
+ }
+ firstElementsForRestart = Collections.EMPTY_LIST;
+ }
+ }
+ if (onLastPageAndIPDChanges && !visitedBefore && positionAtBreak.getPosition() != null) {
+ restartAtLM = positionAtBreak.getPosition().getLM();
+ }
+ return restartAtLM;
+ }
+
/**
* Returns {@code true} if the given position or one of its descendants
* corresponds to a non-restartable LM.
@@ -709,84 +818,39 @@ public abstract class AbstractBreaker {
return nextSequenceStartsOn;
}
- /**
- * @param childLC LayoutContext to use
- * @param alg the pagebreaking algorithm
- * @param effectiveList the list of Knuth elements to be reused
- * @return the page on which the next content should appear after a hard break
- */
- private int getNextBlockListChangedIPD(LayoutContext childLC, PageBreakingAlgorithm alg,
- BlockSequence effectiveList) {
- int nextSequenceStartsOn;
- KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange();
- int positionIndex = optimalBreak.position;
- log.trace("IPD changes at index " + positionIndex);
- KnuthElement elementAtBreak = alg.getElement(positionIndex);
- Position positionAtBreak = elementAtBreak.getPosition();
- if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
- throw new UnsupportedOperationException(
- "Don't know how to restart at position " + positionAtBreak);
- }
- /* Retrieve the original position wrapped into this space position */
- positionAtBreak = positionAtBreak.getPosition();
- LayoutManager restartAtLM = null;
- List<KnuthElement> firstElements = Collections.emptyList();
- if (containsNonRestartableLM(positionAtBreak)) {
- if (alg.getIPDdifference() > 0) {
- EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj()
- .getUserAgent().getEventBroadcaster();
- BlockLevelEventProducer eventProducer
- = BlockLevelEventProducer.Provider.get(eventBroadcaster);
- eventProducer.nonRestartableContentFlowingToNarrowerPage(this);
- }
- firstElements = new LinkedList<KnuthElement>();
- boolean boxFound = false;
- Iterator<KnuthElement> iter = effectiveList.listIterator(positionIndex + 1);
- Position position = null;
- while (iter.hasNext()
- && (position == null || containsNonRestartableLM(position))) {
- positionIndex++;
- KnuthElement element = iter.next();
- position = element.getPosition();
- if (element.isBox()) {
- boxFound = true;
- firstElements.add(element);
- } else if (boxFound) {
- firstElements.add(element);
- }
- }
- if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) {
- /* Retrieve the original position wrapped into this space position */
- positionAtBreak = position.getPosition();
- } else {
- positionAtBreak = null;
+ protected boolean shouldRedoLayout() {
+ return false;
+ }
+
+ protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ return;
+ }
+
+ protected boolean wasLayoutRedone() {
+ return false;
+ }
+
+ private boolean thereIsANonRestartableLM(PageBreakingAlgorithm alg) {
+ KnuthNode optimalBreak = alg.getBestNodeForLastPage();
+ if (optimalBreak != null) {
+ int positionIndex = optimalBreak.position;
+ KnuthElement elementAtBreak = alg.getElement(positionIndex);
+ Position positionAtBreak = elementAtBreak.getPosition();
+ if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
+ return false;
}
- }
- if (positionAtBreak != null && positionAtBreak.getIndex() == -1) {
- /*
- * This is an indication that we are between two blocks
- * (possibly surrounded by another block), not inside a
- * paragraph.
- */
- Position position;
- Iterator<KnuthElement> iter = effectiveList.listIterator(positionIndex + 1);
- do {
- KnuthElement nextElement = iter.next();
- position = nextElement.getPosition();
- } while (position == null
- || position instanceof SpaceResolver.SpaceHandlingPosition
- || position instanceof SpaceResolver.SpaceHandlingBreakPosition
- && position.getPosition().getIndex() == -1);
- LayoutManager surroundingLM = positionAtBreak.getLM();
- while (position.getLM() != surroundingLM) {
- position = position.getPosition();
+ /* Retrieve the original position wrapped into this space position */
+ positionAtBreak = positionAtBreak.getPosition();
+ if (positionAtBreak != null && containsNonRestartableLM(positionAtBreak)) {
+ return true;
}
- restartAtLM = position.getPosition().getLM();
}
+ return false;
+ }
- nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN,
- positionAtBreak, restartAtLM, firstElements);
- return nextSequenceStartsOn;
+ protected boolean lastPageHasIPDChange() {
+ return false;
}
protected int handleFloatLayout(PageBreakingAlgorithm alg, int optimalPageCount, BlockSequence blockList,
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
index 7faa0565e..8435ad093 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
@@ -383,6 +383,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
if (curPage != null) {
finishPage();
}
+
+ while (forcePageCount != Constants.EN_NO_FORCE && getCurrentPageNum() < getLastPageNumber()) {
+ curPage = makeNewPage(true);
+ finishPage();
+ }
}
/** {@inheritDoc} */
@@ -390,4 +395,7 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
throw new IllegalStateException();
}
+ protected int getLastPageNumber() {
+ return currentPageNum;
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java
index 6a407f266..d043456be 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java
@@ -202,4 +202,21 @@ public interface BlockLevelEventProducer extends EventProducer {
* @event.severity WARN
*/
void nonRestartableContentFlowingToNarrowerPage(Object source);
+
+ /**
+ * A feasible layout has reached the given number of parts (columns or pages).
+ *
+ * @param source the event source
+ * @param partCount the number of parts that the layout has reached
+ * @event.severity INFO
+ */
+ void layoutHasReachedParts(Object source, int partCount);
+
+ /**
+ * Last page master reference missing.
+ *
+ * @param source the event source
+ * @event.severity WARN
+ */
+ void lastPageMasterReferenceMissing(Object source, Locator loc);
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml
index 6eb772db1..de040bdfe 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml
+++ b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml
@@ -31,4 +31,6 @@
<message key="missingSubsequencesInPageSequenceMaster">No subsequences in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message>
<message key="noMatchingPageMaster">No simple-page-master matching "{pageMasterName}" in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message>
<message key="nonRestartableContentFlowingToNarrowerPage">Content that cannot handle IPD changes is flowing to a narrower page. Part of it may be clipped by the page border.</message>
+ <message key="layoutHasReachedParts">A layout has reached {partCount} part(s).</message>
+ <message key="lastPageMasterReferenceMissing">page-position="last" master reference missing.{{locator}}</message>
</catalogue>
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
index ba676ab89..8cc9db790 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
@@ -51,6 +51,8 @@ public class PageBreaker extends AbstractBreaker {
private PageProvider pageProvider;
private Block separatorArea;
private boolean spanAllActive;
+ private boolean layoutRedone;
+ private int previousIndex;
private boolean handlingStartOfFloat;
private boolean handlingEndOfFloat;
private int floatHeight;
@@ -161,7 +163,7 @@ public class PageBreaker extends AbstractBreaker {
/** {@inheritDoc} */
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
Position positionAtIPDChange, LayoutManager restartLM, List firstElements) {
- if (!handlingFloat()) {
+ if (!layoutRedone && !handlingFloat()) {
if (!firstPart) {
// if this is the first page that will be created by
// the current BlockSequence, it could have a break
@@ -330,21 +332,55 @@ public class PageBreaker extends AbstractBreaker {
return;
}
- boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast()
- || pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1);
- if (!hasMoreContent()) {
- //last part is reached
- if (lastPageMasterDefined) {
- //last-page condition
- redoLayout(alg, partCount, originalList, effectiveList);
- return;
- }
+ if (shouldRedoLayout(partCount)) {
+ redoLayout(alg, partCount, originalList, effectiveList);
+ return;
}
//nothing special: just add the areas now
addAreas(alg, partCount, originalList, effectiveList);
}
+ protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList,
+ BlockSequence effectiveList) {
+ int newStartPos = 0;
+ int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
+ if (restartPoint > 0 && !layoutRedone) {
+ // Add definitive areas for the parts before the
+ // restarting point
+ addAreas(alg, restartPoint, originalList, effectiveList);
+ // Get page break from which we restart
+ PageBreakPosition pbp = alg.getPageBreaks().get(restartPoint - 1);
+ newStartPos = alg.par.getFirstBoxIndex(pbp.getLeafPos() + 1);
+ // Handle page break right here to avoid any side-effects
+ if (newStartPos > 0) {
+ handleBreakTrait(Constants.EN_PAGE);
+ }
+ }
+ pageBreakHandled = true;
+ // Update so the available BPD is reported correctly
+ int currentPageNum = pslm.getCurrentPageNum();
+ int currentColumn = pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex();
+ pageProvider.setStartOfNextElementList(currentPageNum, currentColumn, spanAllActive);
+
+ // Make sure we only add the areas we haven't added already
+ effectiveList.ignoreAtStart = newStartPos;
+ if (!layoutRedone) {
+ // Handle special page-master for last page
+ setLastPageIndex(currentPageNum);
+// BodyRegion lastBody = pageProvider.getPage(false, currentPageNum).getPageViewport().getBodyRegion();
+ pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
+ previousIndex = pageProvider.getIndexOfCachedLastPage();
+ } else {
+ setLastPageIndex(currentPageNum + 1);
+// pslm.setCurrentPage(previousPage);
+ pageProvider.discardCacheStartingWith(previousIndex);
+ pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
+ }
+ layoutRedone = true;
+ }
+
/**
* Restart the algorithm at the break corresponding to the given partCount. Used to
* re-do the part after the last break in case of either column-balancing or a last
@@ -565,6 +601,7 @@ public class PageBreaker extends AbstractBreaker {
return;
case Constants.EN_COLUMN:
case Constants.EN_AUTO:
+ case Constants.EN_PAGE:
case -1:
PageViewport pv = curPage.getPageViewport();
@@ -580,26 +617,35 @@ public class PageBreaker extends AbstractBreaker {
log.trace("Forcing new page with span");
curPage = pslm.makeNewPage(false);
curPage.getPageViewport().createSpan(true);
- } else if (pv.getCurrentSpan().hasMoreFlows()) {
- log.trace("Moving to next flow");
- pv.getCurrentSpan().moveToNextFlow();
} else {
- log.trace("Making new page");
- /*curPage = */pslm.makeNewPage(false);
+ if (breakVal == Constants.EN_PAGE) {
+ handleBreakBeforeFollowingPage(breakVal);
+ } else {
+ if (pv.getCurrentSpan().hasMoreFlows()) {
+ log.trace("Moving to next flow");
+ pv.getCurrentSpan().moveToNextFlow();
+ } else {
+ log.trace("Making new page");
+ pslm.makeNewPage(false);
+ }
+ }
}
return;
- case Constants.EN_PAGE:
default:
- log.debug("handling break-before after page " + pslm.getCurrentPageNum()
- + " breakVal=" + getBreakClassName(breakVal));
- if (needBlankPageBeforeNew(breakVal)) {
- log.trace("Inserting blank page");
- /*curPage = */pslm.makeNewPage(true);
- }
- if (needNewPage(breakVal)) {
- log.trace("Making new page");
- /*curPage = */pslm.makeNewPage(false);
- }
+ handleBreakBeforeFollowingPage(breakVal);
+ }
+ }
+
+ private void handleBreakBeforeFollowingPage(int breakVal) {
+ log.debug("handling break-before after page " + pslm.getCurrentPageNum() + " breakVal="
+ + getBreakClassName(breakVal));
+ if (needBlankPageBeforeNew(breakVal)) {
+ log.trace("Inserting blank page");
+ /* curPage = */pslm.makeNewPage(true);
+ }
+ if (needNewPage(breakVal)) {
+ log.trace("Making new page");
+ /* curPage = */pslm.makeNewPage(false);
}
}
@@ -641,6 +687,36 @@ public class PageBreaker extends AbstractBreaker {
}
}
+ protected boolean shouldRedoLayout() {
+ return shouldRedoLayout(-1);
+ }
+
+ protected boolean shouldRedoLayout(int partCount) {
+ boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast();
+ if (!lastPageMasterDefined && partCount != -1) {
+ lastPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1);
+ }
+ return (!hasMoreContent() && lastPageMasterDefined && !layoutRedone);
+ }
+
+ protected boolean wasLayoutRedone() {
+ return layoutRedone;
+ }
+
+ protected boolean lastPageHasIPDChange() {
+ boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast();
+ boolean onlyPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly();
+ if (lastPageMasterDefined && !onlyPageMasterDefined) {
+ // code not very robust and unable to handle situations were only and last are defined
+ int currentIPD = this.pageProvider.getCurrentIPD();
+ int lastPageIPD = this.pageProvider.getLastPageIPD();
+ if (lastPageIPD != -1 && currentIPD != lastPageIPD) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean handlingStartOfFloat() {
return handlingStartOfFloat;
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index 9327f8f8c..b72124c77 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -95,6 +95,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
private int ipdDifference;
private KnuthNode bestNodeForIPDChange;
+ public KnuthNode bestNodeForLastPage;
//Used to keep track of switches in keep-context
private int currentKeepContext = Constants.EN_AUTO;
@@ -1258,6 +1259,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* the IPD change. No need to do any special handling.
*/
ipdDifference = 0;
+ } else if (line > 0 /*&& (bestNodeForLastPage == null
+ || node.totalDemerits < bestNodeForLastPage.totalDemerits)*/) {
+ bestNodeForLastPage = node;
}
super.addNode(line, node);
}
@@ -1274,6 +1278,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
return pageProvider.compareIPDs(line);
}
+ KnuthNode getBestNodeForLastPage() {
+ return bestNodeForLastPage;
+ }
+
protected boolean handlingFloat() {
return (handlingStartOfFloat || handlingEndOfFloat);
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java
index 142f7ad72..ca41c8c1e 100644
--- a/src/java/org/apache/fop/layoutmgr/PageProvider.java
+++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java
@@ -201,8 +201,8 @@ public class PageProvider implements Constants {
return 0;
} else {
Page nextPage = getPage(false, column.pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST);
- return column.page.getPageViewport().getBodyRegion().getIPD()
- - nextPage.getPageViewport().getBodyRegion().getIPD();
+ return column.page.getPageViewport().getBodyRegion().getColumnIPD()
+ - nextPage.getPageViewport().getBodyRegion().getColumnIPD();
}
}
@@ -332,7 +332,7 @@ public class PageProvider implements Constants {
return page;
}
- private void discardCacheStartingWith(int index) {
+ protected void discardCacheStartingWith(int index) {
while (index < cachedPages.size()) {
this.cachedPages.remove(cachedPages.size() - 1);
if (!pageSeq.goToPreviousSimplePageMaster()) {
@@ -352,9 +352,38 @@ public class PageProvider implements Constants {
page.getPageViewport().setForeignAttributes(spm.getForeignAttributes());
page.getPageViewport().setWritingModeTraits(pageSeq);
cachedPages.add(page);
+ if (isLastPage) {
+ pageSeq.getRoot().setLastSeq(pageSeq);
+ } else if (!isFirstPage) {
+ pageSeq.getRoot().setLastSeq(null);
+ }
return page;
}
+ public int getIndexOfCachedLastPage() {
+ return indexOfCachedLastPage;
+ }
+
+ public int getLastPageIndex() {
+ return lastPageIndex;
+ }
+
+ public int getLastPageIPD() {
+ int index = this.cachedPages.size();
+ boolean isFirstPage = (startPageOfPageSequence == index);
+ SimplePageMaster spm = pageSeq.getLastSimplePageMaster(index, isFirstPage, false);
+ Page page = new Page(spm, index, "", false, false);
+ if (pageSeq.getRoot().getLastSeq() != null && pageSeq.getRoot().getLastSeq() != pageSeq) {
+ return -1;
+ }
+ return page.getPageViewport().getBodyRegion().getColumnIPD();
+ }
+
+ public int getCurrentIPD() {
+ return getPageFromColumnIndex(startColumnOfCurrentElementList).getPageViewport().getBodyRegion()
+ .getColumnIPD();
+ }
+
/**
* Indicates whether the column/page at the given index is on the first page of the page sequence.
*
diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
index 2e2bd0a22..0ee7121af 100644
--- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
@@ -254,6 +254,16 @@ public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager
return pageProvider.isOnFirstPage(partIndex);
}
+ protected int getLastPageNumber() {
+ return pageProvider.getLastPageIndex();
+ }
+
+ protected int getWidthOfCurrentPage() {
+ if (curPage != null) {
+ return (int) curPage.getPageViewport().getViewArea().getWidth();
+ }
+ return 0;
+ }
/**
* Registers the given footnotes so that they can be added to the current page, before any other footnote.
*
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java
index 457cfaef3..e6dc5b22d 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java
@@ -124,4 +124,8 @@ class TableContentPosition extends Position {
sb.append(")");
return sb.toString();
}
+
+ public Position getPosition() {
+ return this;
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java b/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
index 342a2f33c..c51c86997 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
@@ -68,7 +68,7 @@ public class TableRowIterator {
List rowGroupsList = new LinkedList();
// TODO this is ugly
for (FONodeIterator iter = table.getChildNodes(); iter.hasNext();) {
- FONode node = iter.nextNode();
+ FONode node = iter.next();
if (node instanceof TableBody) {
rowGroupsList.addAll(((TableBody) node).getRowGroups());
}
diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java
index 7acffa0c9..6602f544d 100644
--- a/src/java/org/apache/fop/pdf/PDFPattern.java
+++ b/src/java/org/apache/fop/pdf/PDFPattern.java
@@ -321,7 +321,7 @@ public class PDFPattern extends PDFPathPaint {
if (pdfStream != null) {
length += pdfStream.outputStreamData(encodedStream, stream);
}
-
+ patternDataStream = null;
return length;
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
index 235e051f9..8d562e465 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -523,7 +523,7 @@ public class IFParser implements IFConstants {
establishForeignAttributes(foreignAttributes);
documentHandler.startPage(index, name, pageMasterName,
new Dimension(width, height));
- documentHandler.getContext().setPageNumber(Integer.parseInt(name));
+ documentHandler.getContext().setPageNumber(index + 1);
resetForeignAttributes();
}
diff --git a/src/java/org/apache/fop/render/pcl/Java2DRendererOption.java b/src/java/org/apache/fop/render/pcl/Java2DRendererOption.java
index cb23f7191..9ec0b779b 100644
--- a/src/java/org/apache/fop/render/pcl/Java2DRendererOption.java
+++ b/src/java/org/apache/fop/render/pcl/Java2DRendererOption.java
@@ -27,7 +27,8 @@ public enum Java2DRendererOption implements RendererConfigOption {
RENDERING_MODE("rendering", PCLRenderingMode.class),
TEXT_RENDERING("text-rendering", Boolean.class),
- DISABLE_PJL("disable-pjl", Boolean.class);
+ DISABLE_PJL("disable-pjl", Boolean.class),
+ MODE_COLOR("color", Boolean.class);
private final String name;
diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
index 1991295bd..66655a721 100644
--- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
@@ -274,7 +274,7 @@ public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
try {
Rectangle printArea = this.currentPageDefinition.getLogicalPageRect();
gen.setCursorPos(0, 0);
- gen.paintBitmap(this.currentImage, printArea.getSize(), true);
+ gen.paintBitmap(this.currentImage, printArea.getSize(), true, pclUtil);
} catch (IOException ioe) {
throw new IFException("I/O error while encoding page image", ioe);
} finally {
diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
index 67d37fbb5..589779ad9 100644
--- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java
+++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
@@ -21,12 +21,19 @@ package org.apache.fop.render.pcl;
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
+import java.awt.image.SinglePixelPackedSampleModel;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -380,7 +387,7 @@ public class PCLGenerator {
* @param col the fill color
* @throws IOException In case of an I/O error
*/
- protected void fillRect(int w, int h, Color col) throws IOException {
+ protected void fillRect(int w, int h, Color col, boolean colorEnabled) throws IOException {
if ((w == 0) || (h == 0)) {
return;
}
@@ -399,12 +406,19 @@ public class PCLGenerator {
writeCommand("*c" + lineshade + "G");
writeCommand("*c2P"); //Shaded fill
} else {
- defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4);
+ if (colorEnabled) {
+ selectColor(col);
+ writeCommand("*c" + formatDouble4(w / 100.0) + "h"
+ + formatDouble4(h / 100.0) + "V");
+ writeCommand("*c0P"); //Solid fill
+ } else {
+ defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4);
- writeCommand("*c" + formatDouble4(w / 100.0) + "h"
- + formatDouble4(h / 100.0) + "V");
- writeCommand("*c32G");
- writeCommand("*c4P"); //User-defined pattern
+ writeCommand("*c" + formatDouble4(w / 100.0) + "h"
+ + formatDouble4(h / 100.0) + "V");
+ writeCommand("*c32G");
+ writeCommand("*c4P"); //User-defined pattern
+ }
}
// Reset pattern transparency mode.
setPatternTransparencyMode(true);
@@ -530,6 +544,13 @@ public class PCLGenerator {
}
}
+ public void selectColor(Color col) throws IOException {
+ writeCommand("*v6W");
+ writeBytes(new byte[]{0, 1, 1, 8, 8, 8});
+ writeCommand(String.format("*v%da%db%dc0I", col.getRed(), col.getGreen(), col.getBlue()));
+ writeCommand("*v0S");
+ }
+
/**
* Select the current pattern
* @param patternID the pattern ID (<ESC>*c#G command)
@@ -696,13 +717,15 @@ public class PCLGenerator {
* @param sourceTransparency true if the background should not be erased
* @throws IOException In case of an I/O error
*/
- public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency)
- throws IOException {
+ public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency,
+ PCLRenderingUtil pclUtil) throws IOException {
+ final boolean printerSupportsColor = pclUtil.isColorEnabled();
+ boolean monochrome = isMonochromeImage(img);
double targetHResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width);
double targetVResolution = img.getHeight() / UnitConv.mpt2in(targetDim.height);
double targetResolution = Math.max(targetHResolution, targetVResolution);
int resolution = (int)Math.round(targetResolution);
- int effResolution = calculatePCLResolution(resolution, true);
+ int effResolution = calculatePCLResolution(resolution, !(printerSupportsColor && !monochrome));
Dimension orgDim = new Dimension(img.getWidth(), img.getHeight());
Dimension effDim;
if (targetResolution == effResolution) {
@@ -713,27 +736,30 @@ public class PCLGenerator {
(int)Math.ceil(UnitConv.mpt2px(targetDim.height, effResolution)));
}
boolean scaled = !orgDim.equals(effDim);
-
- boolean monochrome = isMonochromeImage(img);
if (!monochrome) {
- //Transparency mask disabled. Doesn't work reliably
- /*
- final boolean transparencyDisabled = true;
- RenderedImage mask = (transparencyDisabled ? null : getMask(img, effDim));
- if (mask != null) {
- pushCursorPos();
- selectCurrentPattern(0, 1); //Solid white
- setTransparencyMode(true, true);
- paintMonochromeBitmap(mask, effResolution);
- popCursorPos();
- }
- */
+ if (printerSupportsColor) {
+ selectCurrentPattern(0, 0); //Solid black
+ renderImageAsColor(img, effResolution);
+ } else {
+ //Transparency mask disabled. Doesn't work reliably
+ /*
+ final boolean transparencyDisabled = true;
+ RenderedImage mask = (transparencyDisabled ? null : getMask(img, effDim));
+ if (mask != null) {
+ pushCursorPos();
+ selectCurrentPattern(0, 1); //Solid white
+ setTransparencyMode(true, true);
+ paintMonochromeBitmap(mask, effResolution);
+ popCursorPos();
+ }
+ */
- RenderedImage red = BitmapImageUtil.convertToMonochrome(
- img, effDim, this.ditheringQuality);
- selectCurrentPattern(0, 0); //Solid black
- setTransparencyMode(sourceTransparency /*|| mask != null*/, true);
- paintMonochromeBitmap(red, effResolution);
+ RenderedImage red = BitmapImageUtil.convertToMonochrome(
+ img, effDim, this.ditheringQuality);
+ selectCurrentPattern(0, 0); //Solid black
+ setTransparencyMode(sourceTransparency /*|| mask != null*/, true);
+ paintMonochromeBitmap(red, effResolution);
+ }
} else {
RenderedImage effImg = img;
if (scaled) {
@@ -755,6 +781,90 @@ public class PCLGenerator {
return (int)greyVal;
}
+ private void renderImageAsColor(RenderedImage imgOrg, int dpi) throws IOException {
+ BufferedImage img = new BufferedImage(imgOrg.getWidth(), imgOrg.getHeight(), BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = img.createGraphics();
+ g.setColor(Color.WHITE);
+ g.fillRect(0, 0, imgOrg.getWidth(), imgOrg.getHeight());
+ g.drawImage((Image) imgOrg, 0, 0, null);
+
+ if (!isValidPCLResolution(dpi)) {
+ throw new IllegalArgumentException("Invalid PCL resolution: " + dpi);
+ }
+ int w = img.getWidth();
+ ColorModel cm = img.getColorModel();
+ if (cm instanceof DirectColorModel) {
+ writeCommand("*v6W"); // ImagingMode
+ out.write(new byte[]{0, 3, 0, 8, 8, 8});
+ } else {
+ IndexColorModel icm = (IndexColorModel)cm;
+ writeCommand("*v6W"); // ImagingMode
+ out.write(new byte[]{0, 1, (byte)icm.getMapSize(), 8, 8, 8});
+
+ byte[] reds = new byte[256];
+ byte[] greens = new byte[256];
+ byte[] blues = new byte[256];
+
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ for (int i = 0; i < icm.getMapSize(); i++) {
+ writeCommand("*v" + (reds[i] & 0xFF) + "A"); //ColorComponentOne
+ writeCommand("*v" + (greens[i] & 0xFF) + "B"); //ColorComponentTwo
+ writeCommand("*v" + (blues[i] & 0xFF) + "C"); //ColorComponentThree
+ writeCommand("*v" + i + "I"); //AssignColorIndex
+ }
+ }
+ setRasterGraphicsResolution(dpi);
+ writeCommand("*r0f" + img.getHeight() + "t" + (w) + "S");
+ writeCommand("*r1A");
+
+ Raster raster = img.getData();
+
+ ColorEncoder encoder = new ColorEncoder(img);
+ // Transfer graphics data
+ if (cm.getTransferType() == DataBuffer.TYPE_BYTE) {
+ DataBufferByte dataBuffer = (DataBufferByte)raster.getDataBuffer();
+ if (img.getSampleModel() instanceof MultiPixelPackedSampleModel && dataBuffer.getNumBanks() == 1) {
+ byte[] buf = dataBuffer.getData();
+ MultiPixelPackedSampleModel sampleModel = (MultiPixelPackedSampleModel)img.getSampleModel();
+ int scanlineStride = sampleModel.getScanlineStride();
+ int idx = 0;
+ for (int y = 0, maxy = img.getHeight(); y < maxy; y++) {
+ for (int x = 0; x < scanlineStride; x++) {
+ encoder.add8Bits(buf[idx]);
+ idx++;
+ }
+ encoder.endLine();
+ }
+ } else {
+ throw new IOException("Unsupported image");
+ }
+ } else if (cm.getTransferType() == DataBuffer.TYPE_INT) {
+ DataBufferInt dataBuffer = (DataBufferInt)raster.getDataBuffer();
+ if (img.getSampleModel() instanceof SinglePixelPackedSampleModel && dataBuffer.getNumBanks() == 1) {
+ int[] buf = dataBuffer.getData();
+ SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel)img.getSampleModel();
+ int scanlineStride = sampleModel.getScanlineStride();
+ int idx = 0;
+ for (int y = 0, maxy = img.getHeight(); y < maxy; y++) {
+ for (int x = 0; x < scanlineStride; x++) {
+ encoder.add8Bits((byte)(buf[idx] >> 16));
+ encoder.add8Bits((byte)(buf[idx] >> 8));
+ encoder.add8Bits((byte)(buf[idx] >> 0));
+ idx++;
+ }
+ encoder.endLine();
+ }
+ } else {
+ throw new IOException("Unsupported image");
+ }
+ } else {
+ throw new IOException("Unsupported image");
+ }
+ // End raster graphics
+ writeCommand("*rB");
+ }
/**
* Paint a bitmap at the current cursor position. The bitmap must be a monochrome
* (1-bit) bitmap image.
@@ -921,4 +1031,236 @@ public class PCLGenerator {
}
+ private class ColorEncoder {
+ private int imgw;
+ private int bytewidth;
+ private byte ib; //current image bits
+
+ private int currentIndex;
+ private int len;
+ private int shiftBit = 0x80;
+ private int whiteLines;
+ final byte[] zeros;
+ final byte[] buff1;
+ final byte[] buff2;
+ final byte[] encodedRun;
+ final byte[] encodedTagged;
+ final byte[] encodedDelta;
+ byte[] seed;
+ byte[] current;
+ int compression;
+ int seedLen;
+
+ public ColorEncoder(RenderedImage img) {
+ imgw = img.getWidth();
+ bytewidth = imgw * 3 + 1;
+
+ zeros = new byte[bytewidth];
+ buff1 = new byte[bytewidth];
+ buff2 = new byte[bytewidth];
+ encodedRun = new byte[bytewidth];
+ encodedTagged = new byte[bytewidth];
+ encodedDelta = new byte[bytewidth];
+
+ seed = buff1;
+ current = buff2;
+
+ seedLen = 0;
+ compression = (-1);
+ System.arraycopy(zeros, 0, seed, 0, zeros.length);
+
+ }
+
+ private int runCompression(byte[] buff, int len) {
+ int bytes = 0;
+
+ try {
+ for (int i = 0; i < len;) {
+ int sameCount;
+ byte seed = current[i++];
+
+ for (sameCount = 1; i < len && current[i] == seed; i++) {
+ sameCount++;
+ }
+
+ for (; sameCount > 256; sameCount -= 256) {
+ buff[bytes++] = (byte)255;
+ buff[bytes++] = seed;
+ }
+ if (sameCount > 0) {
+ buff[bytes++] = (byte)(sameCount - 1);
+ buff[bytes++] = seed;
+ }
+
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return len + 1;
+ }
+ return bytes;
+ }
+
+ private int deltaCompression(byte[] seed, byte[] buff, int len) {
+ int bytes = 0;
+
+ try {
+ for (int i = 0; i < len;) {
+ int sameCount;
+ int diffCount;
+
+ for (sameCount = 0; i < len && current[i] == seed[i]; i++) {
+ sameCount++;
+ }
+ for (diffCount = 0; i < len && current[i] != seed[i]; i++) {
+ diffCount++;
+ }
+
+ for (; diffCount != 0;) {
+ int diffToWrite = (diffCount > 8) ? 8 : diffCount;
+ int sameToWrite = (sameCount > 31) ? 31 : sameCount;
+
+ buff[bytes++] = (byte)(((diffToWrite - 1) << 5) | sameToWrite);
+ sameCount -= sameToWrite;
+ if (sameToWrite == 31) {
+ for (; sameCount >= 255; sameCount -= 255) {
+ buff[bytes++] = (byte)255;
+ }
+ buff[bytes++] = (byte)sameCount;
+ sameCount = 0;
+ }
+
+ System.arraycopy(current, i - diffCount, buff, bytes, diffToWrite);
+ bytes += diffToWrite;
+
+ diffCount -= diffToWrite;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return len + 1;
+ }
+ return bytes;
+ }
+
+ private int tiffCompression(byte[] encodedTagged, int len) {
+ int literalCount = 0;
+ int bytes = 0;
+
+ try {
+ for (int from = 0; from < len;) {
+ int repeatLength;
+ int repeatValue = current[from];
+
+ for (repeatLength = 1; repeatLength < 128
+ && from + repeatLength < len
+ && current[from + repeatLength] == repeatValue;) {
+ repeatLength++;
+ }
+
+ if (literalCount == 128 || (repeatLength > 2 && literalCount > 0)) {
+ encodedTagged[bytes++] = (byte)(literalCount - 1);
+ System.arraycopy(current, from - literalCount, encodedTagged, bytes, literalCount);
+ bytes += literalCount;
+ literalCount = 0;
+ }
+ if (repeatLength > 2) {
+ encodedTagged[bytes++] = (byte)(1 - repeatLength);
+ encodedTagged[bytes++] = current[from];
+ from += repeatLength;
+ } else {
+ literalCount++;
+ from++;
+ }
+ }
+ if (literalCount > 0) {
+ encodedTagged[bytes++] = (byte)(literalCount - 1);
+ System.arraycopy(current, (3 * len) - literalCount, encodedTagged, bytes, literalCount);
+ bytes += literalCount;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return len + 1;
+ }
+ return bytes;
+ }
+
+ public void addBit(boolean bit) {
+ //Set image bit for black
+ if (bit) {
+ ib |= shiftBit;
+ }
+ shiftBit >>= 1;
+ if (shiftBit == 0) {
+ add8Bits(ib);
+ shiftBit = 0x80;
+ ib = 0;
+ }
+ }
+
+ public void add8Bits(byte b) {
+ current[currentIndex++] = b;
+ if (b != 0) {
+ len = currentIndex;
+ }
+ }
+
+ public void endLine() throws IOException {
+ if (len == 0) {
+ whiteLines++;
+ } else {
+ if (whiteLines > 0) {
+ writeCommand("*b" + whiteLines + "Y");
+ whiteLines = 0;
+ }
+
+ int unencodedCount = len;
+ int runCount = runCompression(encodedRun, len);
+ int tiffCount = tiffCompression(encodedTagged, len);
+ int deltaCount = deltaCompression(seed, encodedDelta, Math.max(len, seedLen));
+
+ int bestCount = Math.min(unencodedCount, Math.min(runCount, Math.min(tiffCount, deltaCount)));
+ int bestCompression;
+
+ if (bestCount == unencodedCount) {
+ bestCompression = 0;
+ } else if (bestCount == runCount) {
+ bestCompression = 1;
+ } else if (bestCount == tiffCount) {
+ bestCompression = 2;
+ } else {
+ bestCompression = 3;
+ }
+
+ if (compression != bestCompression) {
+ compression = bestCompression;
+ writeCommand("*b" + compression + "M");
+ }
+
+ if (bestCompression == 0) {
+ writeCommand("*b" + unencodedCount + "W");
+ out.write(current, 0, unencodedCount);
+ } else if (bestCompression == 1) {
+ writeCommand("*b" + runCount + "W");
+ out.write(encodedRun, 0, runCount);
+ } else if (bestCompression == 2) {
+ writeCommand("*b" + tiffCount + "W");
+ out.write(encodedTagged, 0, tiffCount);
+ } else if (bestCompression == 3) {
+ writeCommand("*b" + deltaCount + "W");
+ out.write(encodedDelta, 0, deltaCount);
+ }
+
+ if (current == buff1) {
+ seed = buff1;
+ current = buff2;
+ } else {
+ seed = buff2;
+ current = buff1;
+ }
+ seedLen = len;
+ }
+ shiftBit = 0x80;
+ ib = 0;
+ len = 0;
+ currentIndex = 0;
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java
index 014bc47a1..66336955e 100644
--- a/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java
+++ b/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java
@@ -139,7 +139,7 @@ public class PCLImageHandlerGraphics2D implements ImageHandler {
}
gen.paintBitmap(imgRend.getRenderedImage(), new Dimension(pos.width, pos.height),
- pclContext.isSourceTransparencyEnabled());
+ pclContext.isSourceTransparencyEnabled(), pclContext.getPCLUtil());
}
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java
index ebfb1829a..aabb9c5ac 100644
--- a/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java
+++ b/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java
@@ -66,7 +66,7 @@ public class PCLImageHandlerRenderedImage implements ImageHandler {
Point2D transPoint = pclContext.transformedPoint(pos.x, pos.y);
gen.setCursorPos(transPoint.getX(), transPoint.getY());
gen.paintBitmap(ri, new Dimension(pos.width, pos.height),
- pclContext.isSourceTransparencyEnabled());
+ pclContext.isSourceTransparencyEnabled(), pclContext.getPCLUtil());
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java
index 69465a7fd..65e996d08 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -211,7 +211,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
}
try {
setCursorPos(rect.x, rect.y);
- gen.fillRect(rect.width, rect.height, fillColor);
+ gen.fillRect(rect.width, rect.height, fillColor, getPCLUtil().isColorEnabled());
} catch (IOException ioe) {
throw new IFException("I/O error in fillRect()", ioe);
}
@@ -410,7 +410,11 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
Color textColor = state.getTextColor();
if (textColor != null) {
gen.setTransparencyMode(true, false);
- gen.selectGrayscale(textColor);
+ if (getDocumentHandler().getPCLUtil().isColorEnabled()) {
+ gen.selectColor(textColor);
+ } else {
+ gen.selectGrayscale(textColor);
+ }
}
gen.setTransparencyMode(true, true);
@@ -492,7 +496,11 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
Color textColor = state.getTextColor();
if (textColor != null) {
gen.setTransparencyMode(true, false);
- gen.selectGrayscale(textColor);
+ if (getDocumentHandler().getPCLUtil().isColorEnabled()) {
+ gen.selectColor(textColor);
+ } else {
+ gen.selectGrayscale(textColor);
+ }
}
if (x != -1 && y != -1) {
diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererConfig.java b/src/java/org/apache/fop/render/pcl/PCLRendererConfig.java
index 29fccc2c8..05e72b00e 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRendererConfig.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRendererConfig.java
@@ -32,6 +32,7 @@ import org.apache.fop.fonts.DefaultFontConfig.DefaultFontConfigParser;
import org.apache.fop.render.RendererConfig;
import static org.apache.fop.render.pcl.Java2DRendererOption.DISABLE_PJL;
+import static org.apache.fop.render.pcl.Java2DRendererOption.MODE_COLOR;
import static org.apache.fop.render.pcl.Java2DRendererOption.RENDERING_MODE;
import static org.apache.fop.render.pcl.Java2DRendererOption.TEXT_RENDERING;
@@ -65,6 +66,10 @@ public final class PCLRendererConfig implements RendererConfig {
return getParam(DISABLE_PJL, Boolean.class);
}
+ public Boolean isColorEnabled() {
+ return getParam(MODE_COLOR, Boolean.class);
+ }
+
private <T> T getParam(Java2DRendererOption option, Class<T> type) {
assert option.getType().equals(type);
return type.cast(params.get(option));
@@ -90,6 +95,12 @@ public final class PCLRendererConfig implements RendererConfig {
private void configure(Configuration cfg, PCLRendererConfig config) throws FOPException {
if (cfg != null) {
+ Configuration imagesCfg = cfg.getChild("images");
+ String imageMode = imagesCfg.getAttribute("mode", null);
+ if ("color".equalsIgnoreCase(imageMode)) {
+ config.setParam(MODE_COLOR, true);
+ }
+
String rendering = cfg.getChild(RENDERING_MODE.getName()).getValue(null);
if (rendering != null) {
try {
diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java
index 33376655f..7a92b7408 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java
@@ -67,7 +67,9 @@ public class PCLRendererConfigurator extends PrintRendererConfigurator {
if (config.isTextRendering() != null) {
pclUtil.setAllTextAsBitmaps(config.isTextRendering());
}
-
+ if (config.isColorEnabled() != null) {
+ pclUtil.setColorEnabled(config.isColorEnabled());
+ }
}
@Override
diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java
index 32693764f..674605bde 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java
@@ -50,12 +50,7 @@ public class PCLRenderingUtil {
/** Controls the dithering quality when rendering gray or color images. */
private float ditheringQuality = 0.5f;
- /**
- * Controls whether an RGB canvas is used when converting Java2D graphics to bitmaps.
- * This can be used to work around problems with Apache Batik, for example, but setting
- * this to true will increase memory consumption.
- */
- private boolean useColorCanvas;
+ private boolean useColor;
/**
* Controls whether the generation of PJL commands gets disabled.
@@ -142,12 +137,12 @@ public class PCLRenderingUtil {
return this.allTextAsBitmaps;
}
- /**
- * Indicates whether a color canvas is used when creating bitmap images.
- * @return true if a color canvas is used.
- */
- public boolean isColorCanvasEnabled() {
- return this.useColorCanvas;
+ public void setColorEnabled(boolean useColor) {
+ this.useColor = useColor;
+ }
+
+ public boolean isColorEnabled() {
+ return useColor;
}
/**
diff --git a/src/java/org/apache/fop/render/ps/Gradient.java b/src/java/org/apache/fop/render/ps/Gradient.java
new file mode 100644
index 000000000..558f3b952
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/Gradient.java
@@ -0,0 +1,85 @@
+/*
+ * 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.render.ps;
+
+import java.util.List;
+
+import org.apache.fop.render.gradient.Function;
+import org.apache.fop.render.gradient.Function.SubFunctionRenderer;
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
+import org.apache.fop.render.gradient.Pattern;
+import org.apache.fop.render.gradient.Shading;
+
+/**
+ * Helper class to draw gradients in PostScript.
+ */
+public final class Gradient {
+
+ private Gradient() { }
+
+ public static String outputPattern(Pattern pattern, DoubleFormatter doubleFormatter) {
+ StringBuilder p = new StringBuilder(64);
+ p.append("/Pattern setcolorspace\n");
+ p.append("<< \n/Type /Pattern \n");
+
+ p.append("/PatternType " + pattern.getPatternType() + " \n");
+
+ if (pattern.getShading() != null) {
+ p.append("/Shading ");
+ outputShading(p, pattern.getShading(), doubleFormatter);
+ p.append(" \n");
+ }
+ p.append(">> \n");
+ List<Double> matrix = pattern.getMatrix();
+ if (matrix == null) {
+ p.append("matrix ");
+ } else {
+ p.append("[ ");
+ for (double m : pattern.getMatrix()) {
+ p.append(doubleFormatter.formatDouble(m));
+ p.append(" ");
+ }
+ p.append("] ");
+ }
+ p.append("makepattern setcolor\n");
+
+ return p.toString();
+ }
+
+ private static void outputShading(StringBuilder out, Shading shading, final DoubleFormatter doubleFormatter) {
+ final Function function = shading.getFunction();
+ Shading.FunctionRenderer functionRenderer = new Shading.FunctionRenderer() {
+
+ public void outputFunction(StringBuilder out) {
+ SubFunctionRenderer subFunctionRenderer = new Function.SubFunctionRenderer() {
+
+ public void outputFunction(StringBuilder out, int functionIndex) {
+ Function subFunction = function.getFunctions().get(functionIndex);
+ assert subFunction.getFunctions().isEmpty();
+ subFunction.output(out, doubleFormatter, null);
+ }
+ };
+ function.output(out, doubleFormatter, subFunctionRenderer);
+ }
+ };
+ shading.output(out, doubleFormatter, functionRenderer);
+ }
+
+}