diff options
Diffstat (limited to 'src/java/org/apache')
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 & 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 & 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 & 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); + } + +} |