]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Initial work on enhancing the extension facilities to register special extensions...
authorJeremias Maerki <jeremias@apache.org>
Fri, 2 Sep 2005 14:26:31 +0000 (14:26 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 2 Sep 2005 14:26:31 +0000 (14:26 +0000)
Extension Elements that provide ExtensionAttachments are not added as such to the child element list of a formatting object since they work a little differently and should free memory as soon as possible.

The PostScript extension described in http://wiki.apache.org/xmlgraphics-fop/ExtensionsForPostScript are now fully implemented but under the namespace URI "http://xmlgraphics.apache.org/fop/postscript", not "http://xml.apache.org/fop/extensions". I'll need to look at namespaces again separately, later.
The new PSExtensionElementMapping is currently hard-coded into FOTreeBuilder as are the other ElementMapping classes.

OffDocumentItem is now an interface, AbstractOffDocumentItem now providing the base functionality of the former OffDocumentItem class.
Lots of clean-up and javadocs while working through this, for example: FObj.childNodes is not public anymore. Instead a special method on fo.flow.InstreamForeignObject provides the child in the one case childNodes was directly accessed. PropertyMaker access in FObj is also done through a method now, propertyListTable is now private.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@267209 13f79535-47bb-0310-9956-ffa450edef68

21 files changed:
src/java/org/apache/fop/area/AbstractOffDocumentItem.java [new file with mode: 0644]
src/java/org/apache/fop/area/AreaTreeHandler.java
src/java/org/apache/fop/area/BookmarkData.java
src/java/org/apache/fop/area/OffDocumentExtensionAttachment.java [new file with mode: 0644]
src/java/org/apache/fop/area/OffDocumentItem.java
src/java/org/apache/fop/fo/FOElementMapping.java
src/java/org/apache/fop/fo/FONode.java
src/java/org/apache/fop/fo/FOTreeBuilder.java
src/java/org/apache/fop/fo/FObj.java
src/java/org/apache/fop/fo/PropertyList.java
src/java/org/apache/fop/fo/extensions/ExtensionAttachment.java [new file with mode: 0644]
src/java/org/apache/fop/fo/flow/InstreamForeignObject.java
src/java/org/apache/fop/fo/pagination/Declarations.java
src/java/org/apache/fop/fo/pagination/SimplePageMaster.java
src/java/org/apache/fop/layoutmgr/inline/InstreamForeignObjectLM.java
src/java/org/apache/fop/render/ps/PSRenderer.java
src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/extensions/PSSetupCode.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java [new file with mode: 0644]

diff --git a/src/java/org/apache/fop/area/AbstractOffDocumentItem.java b/src/java/org/apache/fop/area/AbstractOffDocumentItem.java
new file mode 100644 (file)
index 0000000..4f56b02
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.area;
+
+/**
+ * Abstract base class for objects that are processed by the renderer outside
+ * of the actual document.
+ * This object can be handled by the renderer according to these
+ * possibilities: IMMEDIATELY, AFTER_PAGE, START_OF_DOC or END_OF_DOC.
+ */
+public abstract class AbstractOffDocumentItem implements OffDocumentItem {
+
+    /**
+     * Process this extension immediately when
+     * being handled by the area tree.
+     */
+    public static final int IMMEDIATELY = 0;
+
+    /**
+     * Process this extension after the next page is rendered
+     * or prepared when being handled by the area tree.
+     */
+    public static final int AFTER_PAGE = 1;
+
+    /**
+     * Process this extension at the end of the document once
+     * all pages have been fully rendered.
+     */
+    public static final int END_OF_DOC = 2;
+
+    /**
+     * Process this extension at the start of the document right
+     * before the first page-sequence is processed.
+     */
+    public static final int START_OF_DOC = 2;
+
+    
+    /** Indicates in what phase the item should be processed. */
+    protected int whenToProcess = IMMEDIATELY;
+    
+    /**
+     * Get an indicator of when this item should be processed
+     * @return int constant (IMMEDIATELY, AFTER_PAGE, START_OF_DOC, END_OF_DOC)
+     */
+    public int getWhenToProcess() {
+        return whenToProcess;
+    }
+
+    /**
+     * Return a human-readable name for this ODI (for error messages, etc.)
+     * @return String name of ODI
+     */
+    public abstract String getName();
+}
index 47b5c163468ba20a0077c71d4fb1d189ff5e2dfe..183ca88965f7225559ea360844d3580a9b52c8ac 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 /* $Id$ */
+
 package org.apache.fop.area;
 
 // Java
@@ -37,6 +38,7 @@ import org.apache.fop.apps.FOPException;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.FormattingResults;
 import org.apache.fop.fo.FOEventHandler;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.Root;
 import org.apache.fop.fo.pagination.bookmarks.BookmarkTree;
@@ -250,6 +252,26 @@ public class AreaTreeHandler extends FOEventHandler {
         }
     }
 
+    /** @see org.apache.fop.fo.FOEventHandler */
+    public void startPageSequence(PageSequence pageSequence) {
+        rootFObj = pageSequence.getRoot();
+
+        //extension attachments from fo:root
+        wrapAndAddExtensionAttachments(rootFObj.getExtensionAttachments());
+        //extension attachments from fo:declarations
+        if (rootFObj.getDeclarations() != null) {
+            wrapAndAddExtensionAttachments(rootFObj.getDeclarations().getExtensionAttachments());
+        }
+    }
+    
+    private void wrapAndAddExtensionAttachments(List list) {
+        Iterator i = list.iterator();
+        while (i.hasNext()) {
+            ExtensionAttachment attachment = (ExtensionAttachment)i.next();
+            addOffDocumentItem(new OffDocumentExtensionAttachment(attachment));
+        }
+    }
+    
     /**
      * End the PageSequence.
      * The PageSequence formats Pages and adds them to the AreaTree.
@@ -264,8 +286,6 @@ public class AreaTreeHandler extends FOEventHandler {
             log.debug("Current heap size: " + (memoryNow / 1024L) + "Kb");
         }
 
-        rootFObj = pageSequence.getRoot();
-
         // If no main flow, nothing to layout!
         if (pageSequence.getMainFlow() != null) {
             PageSequenceLayoutManager pageSLM;
@@ -275,6 +295,11 @@ public class AreaTreeHandler extends FOEventHandler {
         }
     }
 
+    /**
+     * Called by the PageSequenceLayoutManager when it is finished with a page-sequence.
+     * @param pageSequence the page-sequence just finished
+     * @param pageCount The number of pages generated for the page-sequence
+     */
     public void notifyPageSequenceFinished(PageSequence pageSequence,
                                            int pageCount) {
         this.results.haveFormattedPageSequence(pageSequence, 
index 06c72d57b6aded6e90bd6bf77cd29e76141bf6f1..92f008d97e4b8ec7eed7bb3898aa3e0e0db8e918 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ import org.apache.fop.fo.pagination.bookmarks.Bookmark;
  * its child bookmark-items, or a bookmark-item and the child
  * child bookmark-items under it.
  */
-public class BookmarkData extends OffDocumentItem implements Resolvable {
+public class BookmarkData extends AbstractOffDocumentItem implements Resolvable {
     private ArrayList subData = new ArrayList();
 
     // bookmark-title for this fo:bookmark
diff --git a/src/java/org/apache/fop/area/OffDocumentExtensionAttachment.java b/src/java/org/apache/fop/area/OffDocumentExtensionAttachment.java
new file mode 100644 (file)
index 0000000..4797fba
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.area;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * This class wraps ExtensionAttachments which cannot be transported inside the area tree but
+ * need to be handled in the AreaTreeHandler. These attachments are schedules for processing
+ * before the first page-sequence, i.e. at the start of the document.
+ */
+public class OffDocumentExtensionAttachment implements OffDocumentItem {
+
+    private ExtensionAttachment attachment;
+    
+    /**
+     * Main constructor
+     * @param attachment the extension attachment to wrap.
+     */
+    public OffDocumentExtensionAttachment(ExtensionAttachment attachment) {
+        this.attachment = attachment;
+    }
+    
+    /** @return the extension attachment. */
+    public ExtensionAttachment getAttachment() {
+        return this.attachment;
+    }
+
+    /** @see org.apache.fop.area.OffDocumentItem#getWhenToProcess() */
+    public int getWhenToProcess() {
+        return OffDocumentItem.IMMEDIATELY;
+    }
+
+    /** @see org.apache.fop.area.OffDocumentItem#getName() */
+    public String getName() {
+        return attachment.getCategory();
+    }
+    
+}
index 72f3b2e378a9d73c4b4df0f4aa6eb79fe8b0ec22..6bac6800dc5ce238b441910b3c2b8b1099affc8c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 package org.apache.fop.area;
 
 /**
- * Abstract base class for objects that are processed by the renderer outside
+ * Interface for objects that are processed by the renderer outside
  * of the actual document.
- * This object can be handled by the renderer according to three
- * possibilities, IMMEDIATELY, AFTER_PAGE, or END_OF_DOC.
+ * An object implementing this interface can be handled by the renderer according to these
+ * possibilities: IMMEDIATELY, AFTER_PAGE or END_OF_DOC.
  */
-public abstract class OffDocumentItem {
+public interface OffDocumentItem {
 
     /**
      * Process this extension immediately when
      * being handled by the area tree.
      */
-    public static final int IMMEDIATELY = 0;
+    int IMMEDIATELY = 0;
 
     /**
      * Process this extension after the next page is rendered
      * or prepared when being handled by the area tree.
      */
-    public static final int AFTER_PAGE = 1;
+    int AFTER_PAGE = 1;
 
     /**
      * Process this extension at the end of the document once
      * all pages have been fully rendered.
      */
-    public static final int END_OF_DOC = 2;
+    int END_OF_DOC = 2;
 
-
-    protected int whenToProcess = IMMEDIATELY;
     
     /**
      * Get an indicator of when this item should be processed
      * @return int constant (IMMEDIATELY, AFTER_PAGE, END_OF_DOC)
      */
-    public int getWhenToProcess() {
-        return whenToProcess;
-    }
+    int getWhenToProcess();
 
     /**
      * Return a human-readable name for this ODI (for error messages, etc.)
      * @return String name of ODI
      */
-    public abstract String getName();
+    String getName();
+    
 }
index cca493cdcf67dc603005d8801744a31e7dd13392..bf376de2ac3d0f3c80748cd41018386b876b97ae 100644 (file)
@@ -25,7 +25,9 @@ import java.util.HashMap;
  * Element mapping class for all XSL-FO elements.
  */
 public class FOElementMapping extends ElementMapping {
-    public static String URI = "http://www.w3.org/1999/XSL/Format";
+    
+    /** The XSL-FO namespace URI */
+    public static final String URI = "http://www.w3.org/1999/XSL/Format";
 
     /**
      * Basic constructor; inititializes the namespace URI for the fo: namespace
index 0e00b7006c416c7e08a2dbf7e766b9565cc6a31c..86011004e78e3d41919d5cd704ab07c4c9746587 100644 (file)
@@ -29,17 +29,19 @@ import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
 import org.apache.fop.fo.extensions.svg.SVGElementMapping;
 import org.apache.fop.fo.pagination.Root;
 import org.apache.fop.util.CharUtilities;
 
 /**
- * base class for nodes in the XML tree
+ * Base class for nodes in the XML tree
  */
 public abstract class FONode implements Cloneable {
 
-    protected static String FO_URI = FOElementMapping.URI;
+    /** the XSL-FO namespace URI */
+    protected static final String FO_URI = FOElementMapping.URI;
 
     /** Parent FO node */
     protected FONode parent;
@@ -50,10 +52,12 @@ public abstract class FONode implements Cloneable {
      *   information
      */
     public Locator locator;
+    //TODO Make private or protected and access via getLocator()
 
     /** Logger for fo-tree related messages **/
-    private static Log log = LogFactory.getLog(FONode.class);
-
+    protected static Log log = LogFactory.getLog(FONode.class);
+    //TODO Remove getLogger() method!
+    
     /**
      * Main constructor.
      * @param parent parent of this node
@@ -68,9 +72,10 @@ public abstract class FONode implements Cloneable {
      * @param parent the intended parent of the clone
      * @param removeChildren if true, clean the list of child nodes
      * @return the cloned FO node
+     * @throws FOPException if there's a problem while cloning the node
      */
     public FONode clone(FONode parent, boolean removeChildren)
-        throws FOPException {
+                throws FOPException {
         FONode foNode = (FONode) clone();
         foNode.parent = parent;
         parent.addChildNode(foNode);
@@ -86,8 +91,9 @@ public abstract class FONode implements Cloneable {
     protected Object clone() {
         try {
             return super.clone();
-        } catch (CloneNotSupportedException e) { }
-        return null;
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
     }
 
     /**
@@ -100,6 +106,11 @@ public abstract class FONode implements Cloneable {
         }
     }
 
+    /** @return the location information for this element or null, if not available  */
+    public Locator getLocator() {
+        return this.locator;
+    }
+    
     /**
      * Recursively goes up the FOTree hierarchy until the fo:root is found,
      * which returns the parent FOEventHandler.
@@ -132,11 +143,15 @@ public abstract class FONode implements Cloneable {
      * @param elementName element name (e.g., "fo:block")
      * @param locator Locator object (ignored by default)
      * @param attlist Collection of attributes passed to us from the parser.
+     * @param parent the property list of the parent node
      * @throws FOPException for errors or inconsistencies in the attributes
     */
     public void processNode(String elementName, Locator locator, 
             Attributes attlist, PropertyList parent) throws FOPException {
-        log.debug("name = " + elementName);
+        if (log.isDebugEnabled()) {
+            log.debug("Unhandled element: " + elementName 
+                    + (locator != null ? " at " + getLocatorString(locator) : ""));
+        }
     }
 
     /**
@@ -146,8 +161,10 @@ public abstract class FONode implements Cloneable {
      * @param foEventHandler The FOEventHandler where the PropertyListMaker 
      *              instance can be found.
      * @return A new property list.
+     * @throws FOPException if there's a problem during processing
      */
-    protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) throws FOPException {
+    protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) 
+                throws FOPException {
         return null;
     }
 
@@ -156,12 +173,15 @@ public abstract class FONode implements Cloneable {
      * incoming node is valid for the this (parent) node (e.g., checking to
      * see that fo:table is not an immediate child of fo:root)
      * called within FObj constructor
+     * @param loc location in the FO source file
      * @param namespaceURI namespace of incoming node
      * @param localName (e.g. "table" for "fo:table")
      * @throws ValidationException if incoming node not valid for parent
      */
     protected void validateChildNode(Locator loc, String namespaceURI, String localName) 
-        throws ValidationException {}
+            throws ValidationException {
+        //nop
+    }
 
     /**
      * Adds characters (does nothing here)
@@ -179,25 +199,31 @@ public abstract class FONode implements Cloneable {
     }
 
     /**
-    *
-    */
+     * Called after processNode() is called. Subclasses can do additional processing.
+     * @throws FOPException if there's a problem during processing
+     */
     protected void startOfNode() throws FOPException {
         // do nothing by default
    }
 
     /**
-     *  Primarily used for making final content model validation checks
-     *  and/or informing the FOEventHandler that the end of this FO
-     *  has been reached.
+     * Primarily used for making final content model validation checks
+     * and/or informing the FOEventHandler that the end of this FO
+     * has been reached.
+     * @throws FOPException if there's a problem during processing
      */
     protected void endOfNode() throws FOPException {
         // do nothing by default
     }
 
     /**
+     * Adds a node as a child of this node. The default implementation of this method
+     * just ignores any child node being added.
      * @param child child node to be added to the childNodes of this node
+     * @throws FOPException if there's a problem during processing
      */
     protected void addChildNode(FONode child) throws FOPException {
+        // do nothing by default
     }
 
     /**
@@ -260,9 +286,10 @@ public abstract class FONode implements Cloneable {
             return "fox:" + localName;
         } else if (namespaceURI.equals(SVGElementMapping.URI)) {
             return "svg:" + localName;
-        } else
-            return "(Namespace URI: \"" + namespaceURI + "\", " +
-                "Local Name: \"" + localName + "\")";
+        } else {
+            return "(Namespace URI: \"" + namespaceURI + "\", "
+                    + "Local Name: \"" + localName + "\")";
+        }
     }
 
     /**
@@ -270,11 +297,12 @@ public abstract class FONode implements Cloneable {
      * (e.g., not specifying either an internal- or an external-destination
      * property for an FO:link)
      * @param problem text to display that indicates the problem
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void attributeError(String problem) 
-        throws ValidationException {
-        throw new ValidationException(errorText(locator) + getName() + ", " + 
-            problem, locator);
+                throws ValidationException {
+        throw new ValidationException(errorText(locator) + getName() 
+                + ", " + problem, locator);
     }
 
     /**
@@ -283,7 +311,7 @@ public abstract class FONode implements Cloneable {
      * @param problem text to display that indicates the problem
      */
     protected void attributeWarning(String problem) {
-        getLogger().warn(errorText(locator) + getName() + ", " + problem);
+        log.warn(errorText(locator) + getName() + ", " + problem);
     }
 
     /**
@@ -292,11 +320,12 @@ public abstract class FONode implements Cloneable {
      * @param loc org.xml.sax.Locator object of the error (*not* parent node)
      * @param nsURI namespace URI of incoming invalid node
      * @param lName local name (i.e., no prefix) of incoming node 
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void tooManyNodesError(Locator loc, String nsURI, String lName) 
-        throws ValidationException {
-        throw new ValidationException(errorText(loc) + "For " + getName() 
-            ", only one " + getNodeString(nsURI, lName) + " may be declared.", 
+                throws ValidationException {
+        throw new ValidationException(errorText(loc) + "For " + getName() 
+            ", only one " + getNodeString(nsURI, lName) + " may be declared.", 
             loc);
     }
 
@@ -306,11 +335,12 @@ public abstract class FONode implements Cloneable {
      * This overrloaded method helps make the caller code better self-documenting
      * @param loc org.xml.sax.Locator object of the error (*not* parent node)
      * @param offendingNode incoming node that would cause a duplication.
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void tooManyNodesError(Locator loc, String offendingNode) 
-        throws ValidationException {
-        throw new ValidationException(errorText(loc) + "For " + getName() 
-            ", only one " + offendingNode + " may be declared.", loc);
+                throws ValidationException {
+        throw new ValidationException(errorText(loc) + "For " + getName() 
+            ", only one " + offendingNode + " may be declared.", loc);
     }
 
     /**
@@ -319,6 +349,7 @@ public abstract class FONode implements Cloneable {
      * @param loc org.xml.sax.Locator object of the error (*not* parent node)
      * @param tooLateNode string name of node that should be earlier in document
      * @param tooEarlyNode string name of node that should be later in document
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void nodesOutOfOrderError(Locator loc, String tooLateNode, 
         String tooEarlyNode) throws ValidationException {
@@ -332,9 +363,10 @@ public abstract class FONode implements Cloneable {
      * @param loc org.xml.sax.Locator object of the error (*not* parent node)
      * @param nsURI namespace URI of incoming invalid node
      * @param lName local name (i.e., no prefix) of incoming node 
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void invalidChildError(Locator loc, String nsURI, String lName) 
-        throws ValidationException {
+                throws ValidationException {
         invalidChildError(loc, nsURI, lName, null);
     }
     
@@ -345,62 +377,71 @@ public abstract class FONode implements Cloneable {
      * @param nsURI namespace URI of incoming invalid node
      * @param lName local name (i.e., no prefix) of incoming node
      * @param ruleViolated text explanation of problem
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void invalidChildError(Locator loc, String nsURI, String lName,
-        String ruleViolated)
-        throws ValidationException {
-        throw new ValidationException(errorText(loc) + getNodeString(nsURI, lName) 
-            " is not a valid child element of " + getName() 
+                String ruleViolated)
+                throws ValidationException {
+        throw new ValidationException(errorText(loc) + getNodeString(nsURI, lName) 
+            " is not a valid child element of " + getName() 
             + ((ruleViolated != null) ? ": " + ruleViolated : "."), loc);
     }
 
     /**
      * Helper function to throw an error caused by missing mandatory child elements.
      * E.g., fo:layout-master-set not having any page-master child element.
-     * @param contentModel The XSL Content Model for the fo: object or a similar description indicating the necessary child elements.
+     * @param contentModel The XSL Content Model for the fo: object or a similar description 
+     *                     indicating the necessary child elements.
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void missingChildElementError(String contentModel)
-        throws ValidationException {
-        throw new ValidationException(errorText(locator) + getName() 
-            " is missing child elements. \nRequired Content Model: " 
+                throws ValidationException {
+        throw new ValidationException(errorText(locator) + getName() 
+            " is missing child elements. \nRequired Content Model: " 
             + contentModel, locator);
     }
 
     /**
      * Helper function to throw an error caused by missing mandatory properties
      * @param propertyName the name of the missing property.
+     * @throws ValidationException the validation error provoked by the method call
      */
     protected void missingPropertyError(String propertyName)
-        throws ValidationException {
-        throw new ValidationException(errorText(locator) + getName() +
-            " is missing required \"" + propertyName + "\" property.", locator);
+                throws ValidationException {
+        throw new ValidationException(errorText(locator) + getName()
+            " is missing required \"" + propertyName + "\" property.", locator);
     }
 
     /**
-     * Helper function to return "Error (line#/column#)" string for
+     * Helper function to return "Error(line#/column#)" string for
      * above exception messages
      * @param loc org.xml.sax.Locator object
      * @return String opening error text
      */
     protected static String errorText(Locator loc) {
-        if (loc == null) {
-            return "Error(Unknown location): ";
-        } else {
-            return "Error(" + loc.getLineNumber() + "/" + loc.getColumnNumber() + "): ";
-        }
+        return "Error(" + getLocatorString(loc) + "): ";
     }
 
     /**
-     * Helper function to return "Warning (line#/column#)" string for
+     * Helper function to return "Warning(line#/column#)" string for
      * warning messages
      * @param loc org.xml.sax.Locator object
      * @return String opening warning text
      */
     protected static String warningText(Locator loc) {
+        return "Warning(" + getLocatorString(loc) + "): ";
+    }
+    
+    /**
+     * Helper function to format a Locator instance.
+     * @param loc org.xml.sax.Locator object
+     * @return String the formatted text
+     */
+    protected static String getLocatorString(Locator loc) {
         if (loc == null) {
-            return "Warning(Unknown location): ";
+            return "Unknown location";
         } else {
-            return "Warning(" + loc.getLineNumber() + "/" + loc.getColumnNumber() + "): ";
+            return loc.getLineNumber() + "/" + loc.getColumnNumber();
         }
     }
 
@@ -429,5 +470,16 @@ public abstract class FONode implements Cloneable {
         return Constants.FO_UNKNOWN_NODE;
     }
 
+    /**
+     * This method is overridden by extension elements and allows the extension element
+     * to return a pass-through attachment which the parent formatting objects should simply
+     * carry with them but otherwise ignore. This mechanism is used to pass non-standard 
+     * information from the FO tree through to the layout engine and the renderers. 
+     * @return the extension attachment if one is created by the extension element, null otherwise.
+     */
+    public ExtensionAttachment getExtensionAttachment() {
+        return null;
+    }
+    
 }
 
index 0aa4f4758b5c0d9978881526086ad39bbc2e519b..58942036fe3a6d963055a00981b17efaeba6c5ed 100644 (file)
@@ -135,6 +135,7 @@ public class FOTreeBuilder extends DefaultHandler {
         addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping");
         addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping");
         addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping");
+        addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping");
 
         // add mappings from available services
         Iterator providers = Service.providers(ElementMapping.class);
index 3e82c291151d7b75365382b933ac309af34b1b74..c96c4ecc756ac4aeeeac11afd586b163d5c7bec9 100644 (file)
@@ -18,6 +18,7 @@
 
 package org.apache.fop.fo;
 
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
@@ -26,6 +27,7 @@ import java.util.Set;
 
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.properties.PropertyMaker;
 import org.xml.sax.Attributes;
@@ -35,11 +37,16 @@ import org.xml.sax.Locator;
  * Base class for representation of formatting objects and their processing.
  */
 public abstract class FObj extends FONode implements Constants {
-    public static PropertyMaker[] propertyListTable = null;
+    
+    /** the list of property makers */
+    private static PropertyMaker[] propertyListTable = null;
     
     /** The immediate child nodes of this node. */
-    public List childNodes = null;
+    protected List childNodes = null;
 
+    /** The list of extension attachments, null if none */
+    private List extensionAttachments = null;
+    
     /** Used to indicate if this FO is either an Out Of Line FO (see rec)
         or a descendant of one.  Used during validateChildNode() FO 
         validation.
@@ -64,7 +71,7 @@ public abstract class FObj extends FONode implements Constants {
         
         // determine if isOutOfLineFODescendant should be set
         if (parent != null && parent instanceof FObj) {
-            if (((FObj)parent).getIsOutOfLineFODescendant() == true) {
+            if (((FObj)parent).getIsOutOfLineFODescendant()) {
                 isOutOfLineFODescendant = true;
             } else {
                 int foID = getNameId();
@@ -97,6 +104,15 @@ public abstract class FObj extends FONode implements Constants {
         }
         return fobj;
     }
+    
+    /**
+     * Returns the PropertyMaker for a given property ID.
+     * @param propId the property ID
+     * @return the requested Property Maker
+     */
+    public static PropertyMaker getPropertyMakerFor(int propId) {
+        return propertyListTable[propId];
+    }
 
     /**
      * @see org.apache.fop.fo.FONode#processNode
@@ -111,6 +127,7 @@ public abstract class FObj extends FONode implements Constants {
 
     /**
      * Create a default property list for this element. 
+     * @see org.apache.fop.fo.FONode
      */
     protected PropertyList createPropertyList(PropertyList parent, 
                     FOEventHandler foEventHandler) throws FOPException {
@@ -122,7 +139,7 @@ public abstract class FObj extends FONode implements Constants {
      * Must be overridden in all FObj subclasses that have properties
      * applying to it.
      * @param pList the PropertyList where the properties can be found.
-     * @throws FOPException
+     * @throws FOPException if there is a problem binding the values
      */
     public void bind(PropertyList pList) throws FOPException {
     }
@@ -132,16 +149,18 @@ public abstract class FObj extends FONode implements Constants {
      * Most formatting objects can have an id that can be referenced.
      * This methods checks that the id isn't already used by another
      * fo and sets the id attribute of this object.
+     * @param id ID to check
+     * @throws ValidationException if the ID is already defined elsewhere
      */
-     protected void checkId(String id) throws ValidationException {
+    protected void checkId(String id) throws ValidationException {
         if (!id.equals("")) {
             Set idrefs = getFOEventHandler().getIDReferences();
             if (!idrefs.contains(id)) {
                 idrefs.add(id);
             } else {
-                throw new ValidationException("Property id \"" + id 
-                        "\" previously used; id values must be unique" +
-                        " in document.", locator);
+                throw new ValidationException("Property id \"" + id 
+                        + "\" previously used; id values must be unique"
+                        " in document.", locator);
             }
         }
     }
@@ -158,14 +177,20 @@ public abstract class FObj extends FONode implements Constants {
      * @see org.apache.fop.fo.FONode#addChildNode(FONode)
      */
     protected void addChildNode(FONode child) throws FOPException {
-        if (PropertySets.canHaveMarkers(getNameId()) && 
-            child.getNameId() == FO_MARKER) {
-                addMarker((Marker) child);
+        if (PropertySets.canHaveMarkers(getNameId()) && child.getNameId() == FO_MARKER) {
+            addMarker((Marker)child);
         } else {
-            if (childNodes == null) {
-                childNodes = new java.util.ArrayList();
+            ExtensionAttachment attachment = child.getExtensionAttachment();
+            if (attachment != null) {
+                //This removes the element from the normal children, so no layout manager
+                //is being created for them as they are only additional information.
+                addExtensionAttachment(attachment);
+            } else {
+                if (childNodes == null) {
+                    childNodes = new java.util.ArrayList();
+                }
+                childNodes.add(child);
             }
-            childNodes.add(child);
         }
     }
 
@@ -194,6 +219,7 @@ public abstract class FObj extends FONode implements Constants {
      * Assign the size of a layout dimension to the key. 
      * @param key the Layout dimension, from PercentBase.
      * @param dimension The layout length.
+     * TODO Remove when possible!
      */
     public void setLayoutDimension(PercentBase.LayoutDimension key, int dimension) {
         if (layoutDimension == null) {
@@ -206,6 +232,7 @@ public abstract class FObj extends FONode implements Constants {
      * Assign the size of a layout dimension to the key. 
      * @param key the Layout dimension, from PercentBase.
      * @param dimension The layout length.
+     * TODO Remove when possible!
      */
     public void setLayoutDimension(PercentBase.LayoutDimension key, float dimension) {
         if (layoutDimension == null) {
@@ -218,6 +245,7 @@ public abstract class FObj extends FONode implements Constants {
      * Return the size associated with the key.
      * @param key The layout dimension key.
      * @return the length.
+     * TODO Remove when possible!
      */
     public Number getLayoutDimension(PercentBase.LayoutDimension key) {
         if (layoutDimension != null) {
@@ -349,14 +377,14 @@ public abstract class FObj extends FONode implements Constants {
      * @return true if a member, false if not
      */
     protected boolean isBlockItem(String nsURI, String lName) {
-        return (FO_URI.equals(nsURI) && 
-            (lName.equals("block") 
-            || lName.equals("table") 
-            || lName.equals("table-and-caption") 
-            || lName.equals("block-container")
-            || lName.equals("list-block") 
-            || lName.equals("float")
-            || isNeutralItem(nsURI, lName)));
+        return (FO_URI.equals(nsURI) 
+                && (lName.equals("block") 
+                        || lName.equals("table") 
+                        || lName.equals("table-and-caption") 
+                        || lName.equals("block-container")
+                        || lName.equals("list-block") 
+                        || lName.equals("float")
+                        || isNeutralItem(nsURI, lName)));
     }
 
     /**
@@ -368,21 +396,22 @@ public abstract class FObj extends FONode implements Constants {
      * @return true if a member, false if not
      */
     protected boolean isInlineItem(String nsURI, String lName) {
-        return (FO_URI.equals(nsURI) && 
-            (lName.equals("bidi-override") 
-            || lName.equals("character") 
-            || lName.equals("external-graphic") 
-            || lName.equals("instream-foreign-object")
-            || lName.equals("inline") 
-            || lName.equals("inline-container")
-            || lName.equals("leader") 
-            || lName.equals("page-number") 
-            || lName.equals("page-number-citation")
-            || lName.equals("basic-link")
-            || (lName.equals("multi-toggle")
-                && (getNameId() == FO_MULTI_CASE || findAncestor(FO_MULTI_CASE) > 0))
-            || (lName.equals("footnote") && !isOutOfLineFODescendant)
-            || isNeutralItem(nsURI, lName)));
+        return (FO_URI.equals(nsURI) 
+                && (lName.equals("bidi-override") 
+                        || lName.equals("character") 
+                        || lName.equals("external-graphic") 
+                        || lName.equals("instream-foreign-object")
+                        || lName.equals("inline") 
+                        || lName.equals("inline-container")
+                        || lName.equals("leader") 
+                        || lName.equals("page-number") 
+                        || lName.equals("page-number-citation")
+                        || lName.equals("basic-link")
+                        || (lName.equals("multi-toggle")
+                                && (getNameId() == FO_MULTI_CASE 
+                                        || findAncestor(FO_MULTI_CASE) > 0))
+                        || (lName.equals("footnote") && !isOutOfLineFODescendant)
+                        || isNeutralItem(nsURI, lName)));
     }
 
     /**
@@ -406,12 +435,12 @@ public abstract class FObj extends FONode implements Constants {
      * @return true if a member, false if not
      */
     protected boolean isNeutralItem(String nsURI, String lName) {
-        return (FO_URI.equals(nsURI) && 
-            (lName.equals("multi-switch") 
-            || lName.equals("multi-properties")
-            || lName.equals("wrapper") 
-            || (!isOutOfLineFODescendant && lName.equals("float"))
-            || lName.equals("retrieve-marker")));
+        return (FO_URI.equals(nsURI) 
+                && (lName.equals("multi-switch") 
+                        || lName.equals("multi-properties")
+                        || lName.equals("wrapper") 
+                        || (!isOutOfLineFODescendant && lName.equals("float"))
+                        || lName.equals("retrieve-marker")));
     }
     
     /**
@@ -433,5 +462,33 @@ public abstract class FObj extends FONode implements Constants {
         }
         return -1;
     }
+    
+    /**
+     * Add a new extension attachment to this FObj. See org.apache.fop.fo.FONode for details.
+     * @param attachment the attachment to add.
+     */
+    public void addExtensionAttachment(ExtensionAttachment attachment) {
+        if (attachment == null) {
+            throw new NullPointerException("Parameter attachment must not be null");
+        }
+        if (extensionAttachments == null) {
+            extensionAttachments = new java.util.ArrayList();
+        }
+        if (log.isDebugEnabled()) {
+            getLogger().debug("ExtensionAttachment of category " + attachment.getCategory() 
+                    + " added to " + getName() + ": " + attachment);
+        }
+        extensionAttachments.add(attachment);
+    }
+    
+    /** @return the extension attachments of this FObj. */
+    public List getExtensionAttachments() {
+        if (extensionAttachments == null) {
+            return Collections.EMPTY_LIST;
+        } else {
+            return extensionAttachments;
+        }
+    }
+    
 }
 
index 64553c156fdbad9f5281739c8678bda26ee90ee8..09545055a209c6ad6c20958cca093184b204f31d 100644 (file)
@@ -452,7 +452,7 @@ abstract public class PropertyList {
         if (propId < 1 || propId > Constants.PROPERTY_COUNT) {
             return null;
         } else {
-            return FObj.propertyListTable[propId];
+            return FObj.getPropertyMakerFor(propId);
         }
     }
 
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionAttachment.java b/src/java/org/apache/fop/fo/extensions/ExtensionAttachment.java
new file mode 100644 (file)
index 0000000..2a611f7
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo.extensions;
+
+/**
+ * This interface is implemented by objects that are returned by special extension element
+ * through the FONode.getExtensionAttachment() method. Such objects are carried in the FO tree
+ * and made available to the layout managers that support processing extension attachments or
+ * support passing them on to the area tree where they can be picked up by renderers.
+ * <p>
+ * NOTE: Classes which implement this interface need to be Serializable!
+ */
+public interface ExtensionAttachment {
+
+    /**
+     * This method returns a category URI that allows a processor (layout manager or renderer)
+     * to determine if it supports this object.
+     * @return the category URI
+     */
+    String getCategory();
+    
+}
index 62d9371e4c6289c83b83d1ffc23b369c1c6266bd..cfe90d07f5c7a71a108676163407785c49e15af0 100644 (file)
@@ -327,4 +327,10 @@ public class InstreamForeignObject extends FObj {
     protected void addChildNode(FONode child) throws FOPException {
         super.addChildNode(child);
     }
+
+    /** @return the XMLObj child node of the instream-foreign-object. */
+    public XMLObj getChildXMLObj() {
+        return (XMLObj) childNodes.get(0);
+    }
+    
 }
index 110f8e2a802e316e059eecc73ba92d140e9c7f4e..57f1fd8251b358c5210910b49c5c54e66e4fd67b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ package org.apache.fop.fo.pagination;
 
 // Java
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.xml.sax.Locator;
@@ -30,8 +29,6 @@ import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.ValidationException;
-import org.apache.fop.fo.XMLObj;
-
 
 /**
  * Declarations formatting object.
@@ -43,7 +40,6 @@ import org.apache.fop.fo.XMLObj;
 public class Declarations extends FObj {
 
     private Map colorProfiles = null;
-    private List external = null;
 
     /**
      * @param parent FONode that is the parent of this object
@@ -62,11 +58,11 @@ public class Declarations extends FObj {
 
     /**
      * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
-        XSL 1.0: (color-profile)+ (and non-XSL NS nodes)
-        FOP/XSL 1.1: (color-profile)* (and non-XSL NS nodes)
+     * XSL 1.0: (color-profile)+ (and non-XSL NS nodes)
+     * FOP/XSL 1.1: (color-profile)* (and non-XSL NS nodes)
      */
     protected void validateChildNode(Locator loc, String nsURI, String localName) 
-        throws ValidationException {
+                throws ValidationException {
         if (FO_URI.equals(nsURI)) {
             if (!localName.equals("color-profile")) {   
                 invalidChildError(loc, nsURI, localName);
@@ -75,8 +71,9 @@ public class Declarations extends FObj {
     }
 
     /**
-     * At the end of this element sort out the child into
-     * a hashmap of color profiles and a list of external xml.
+     * At the end of this element sort out the children into
+     * a hashmap of color profiles and a list of extension attachments.
+     * @see org.apache.fop.fo.FONode#endOfNode()
      */
     protected void endOfNode() throws FOPException {
         if (childNodes != null) {
@@ -97,13 +94,9 @@ public class Declarations extends FObj {
                     } else {
                         getLogger().warn("color-profile-name required for color profile");
                     }
-                } else if (node instanceof XMLObj) {
-                    if (external == null) {
-                        external = new java.util.ArrayList();
-                    }
-                    external.add(node);
                 } else {
-                    getLogger().warn("invalid element " + node.getName() + " inside declarations");
+                    getLogger().debug("Ignoring element " + node.getName() 
+                            + " inside fo:declarations.");
                 }
             }
         }
index ee7bcce1c60ca1e757d9b927c1b2b91b6105760b..47e7a1e5345cd28489df3d6f263faca93902cca9 100644 (file)
@@ -106,8 +106,8 @@ public class SimplePageMaster extends FObj {
      */
     protected void endOfNode() throws FOPException {
         if (!hasRegionBody) {
-            missingChildElementError("(region-body, region-before?," +
-                " region-after?, region-start?, region-end?)");
+            missingChildElementError(
+                    "(region-body, region-before?, region-after?, region-start?, region-end?)");
         }
     }
 
@@ -182,8 +182,12 @@ public class SimplePageMaster extends FObj {
     /**
      * @see org.apache.fop.fo.FONode#addChildNode(FONode)
      */
-    protected void addChildNode(FONode child) {
-        addRegion((Region)child);
+    protected void addChildNode(FONode child) throws FOPException {
+        if (child instanceof Region) {
+            addRegion((Region)child);
+        } else {
+            super.addChildNode(child);
+        }
     }
 
     /**
@@ -229,45 +233,32 @@ public class SimplePageMaster extends FObj {
         return false;
     }
 
-    /**
-     * Return the Common Margin Properties-Block.
-     */
+    /** @return the Common Margin Properties-Block. */
     public CommonMarginBlock getCommonMarginBlock() {
         return commonMarginBlock;
     }
 
-    /**
-     * Return "master-name" property.
-     */
+    /** @return "master-name" property. */
     public String getMasterName() {
         return masterName;
     }
 
-    /**
-     * Return the "page-width" property.
-     */
+    /** @return the "page-width" property. */
     public Length getPageWidth() {
         return pageWidth;
     }
 
-    /**
-     * Return the "page-height" property.
-     */
+    /** @return the "page-height" property. */
     public Length getPageHeight() {
         return pageHeight;
     }
-
     
-    /**
-     * Return the "writing-mode" property.
-     */
+    /** @return the "writing-mode" property. */
     public int getWritingMode() {
         return writingMode;
     }
     
-    /**
-     * Return the "reference-orientation" property.
-     */
+    /** @return the "reference-orientation" property. */
     public int getReferenceOrientation() {
         return referenceOrientation.getValue();
     }
index c7dea09c84aae4ff7fe139fb9c6f48cc5aee1d27..995bfc6c2695efbfee57516be03aab995ff60bf3 100644 (file)
@@ -57,7 +57,7 @@ public class InstreamForeignObjectLM extends LeafNodeLayoutManager {
      * @return the viewport inline area
      */
     private Viewport getInlineArea() {
-        XMLObj child = (XMLObj) fobj.childNodes.get(0);
+        XMLObj child = (XMLObj) fobj.getChildXMLObj();
 
         // viewport size is determined by block-progression-dimension
         // and inline-progression-dimension
index 3abb69dfd389427a0d62a40a29a2448683ed0b84..f674942bb8a0f156167a2e487742c1bbc0e7ad7e 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.render.ps;
 import java.awt.Color;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.io.LineNumberReader;
 import java.io.OutputStream;
 import java.util.Iterator;
 import java.util.List;
@@ -34,6 +35,9 @@ import org.apache.fop.apps.FOPException;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockViewport;
 import org.apache.fop.area.CTM;
+import org.apache.fop.area.LineArea;
+import org.apache.fop.area.OffDocumentExtensionAttachment;
+import org.apache.fop.area.OffDocumentItem;
 import org.apache.fop.area.PageViewport;
 import org.apache.fop.area.RegionViewport;
 import org.apache.fop.area.Trait;
@@ -46,6 +50,7 @@ import org.apache.fop.area.inline.TextArea;
 import org.apache.fop.datatypes.ColorType;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fonts.FontSetup;
 import org.apache.fop.fonts.Typeface;
 import org.apache.fop.image.EPSImage;
@@ -54,6 +59,7 @@ import org.apache.fop.image.ImageFactory;
 import org.apache.fop.image.XMLImage;
 import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.ps.extensions.PSSetupCode;
 import org.apache.fop.util.CharUtilities;
 
 import org.w3c.dom.Document;
@@ -93,7 +99,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
     private boolean ioTrouble = false;
 
     private boolean inTextMode = false;
+    private boolean firstPageSequenceReceived = false;
 
+    /** Used to temporarily store PSSetupCode instance until they can be written. */
+    private List setupCodeList;
+    
     /**
      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
      */
@@ -568,17 +578,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
         gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS);
         gen.writeDSCComment(DSCConstants.END_DEFAULTS);
 
-        //Prolog
-        gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
-        PSProcSets.writeFOPStdProcSet(gen);
-        PSProcSets.writeFOPEPSProcSet(gen);
-        gen.writeDSCComment(DSCConstants.END_PROLOG);
-
-        //Setup
-        gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
-        PSProcSets.writeFontDict(gen, fontInfo);
-        gen.writeln("FOPFonts begin");
-        gen.writeDSCComment(DSCConstants.END_SETUP);
+        //Prolog and Setup written right before the first page-sequence, see startPageSequence()
     }
 
     /**
@@ -592,6 +592,76 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
         gen.flush();
     }
 
+    /** @see org.apache.fop.render.Renderer */
+    public void processOffDocumentItem(OffDocumentItem oDI) {
+        log.debug("Handling OffDocumentItem: " + oDI.getName());
+        if (oDI instanceof OffDocumentExtensionAttachment) {
+            ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)oDI).getAttachment();
+            if (PSSetupCode.CATEGORY.equals(attachment.getCategory())) {
+                PSSetupCode setupCode = (PSSetupCode)attachment;
+                if (setupCodeList == null) {
+                    setupCodeList = new java.util.ArrayList();
+                }
+                setupCodeList.add(setupCode);
+            }
+        }
+        super.processOffDocumentItem(oDI);
+    }
+    
+    /** @see org.apache.fop.render.Renderer#startPageSequence(org.apache.fop.area.LineArea) */
+    public void startPageSequence(LineArea seqTitle) {
+        super.startPageSequence(seqTitle);
+        if (!firstPageSequenceReceived) {
+            //Do this only once, as soon as we have all the content for the Setup section!
+            try {
+                //Prolog
+                gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
+                PSProcSets.writeFOPStdProcSet(gen);
+                PSProcSets.writeFOPEPSProcSet(gen);
+                gen.writeDSCComment(DSCConstants.END_PROLOG);
+
+                //Setup
+                gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
+                writeSetupCodeList(setupCodeList, "SetupCode");
+                PSProcSets.writeFontDict(gen, fontInfo);
+                gen.writeln("FOPFonts begin");
+                gen.writeDSCComment(DSCConstants.END_SETUP);
+            } catch (IOException ioe) {
+                handleIOTrouble(ioe);
+            }
+            
+            firstPageSequenceReceived = true;
+        }
+    }
+    
+    /**
+     * Formats and writes a List of PSSetupCode instances to the output stream.
+     * @param setupCodeList a List of PSSetupCode instances
+     * @param type the type of code section
+     */
+    private void writeSetupCodeList(List setupCodeList, String type) throws IOException {
+        if (setupCodeList != null) {
+            Iterator i = setupCodeList.iterator();
+            while (i.hasNext()) {
+                PSSetupCode setupCode = (PSSetupCode)i.next();
+                gen.commentln("%FOPBegin" + type + ": (" 
+                        + (setupCode.getName() != null ? setupCode.getName() : "") 
+                        + ")");
+                LineNumberReader reader = new LineNumberReader(
+                        new java.io.StringReader(setupCode.getContent()));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (line.length() > 0) {
+                        gen.writeln(line.trim());
+                    }
+                }
+                gen.commentln("%FOPEnd" + type);
+                i.remove();
+            }
+        }
+    }
+
     /**
      * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
      */
@@ -641,7 +711,23 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
         }
         gen.writeDSCComment(DSCConstants.PAGE_RESOURCES, 
                 new Object[] {PSGenerator.ATEND});
+        gen.commentln("%FOPSimplePageMaster: " + page.getSPM().getMasterName());
         gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);
+        
+        //Handle PSSetupCode instances on simple-page-master
+        if (page.getSPM().getExtensionAttachments().size() > 0) {
+            List list = new java.util.ArrayList();
+            //Extract all PSSetupCode instances from the attachment list on the s-p-m
+            Iterator i = page.getSPM().getExtensionAttachments().iterator();
+            while (i.hasNext()) {
+                ExtensionAttachment attachment = (ExtensionAttachment)i.next();
+                if (PSSetupCode.CATEGORY.equals(attachment.getCategory())) {
+                    list.add(attachment);
+                }
+            }
+            writeSetupCodeList(list, "PageSetupCode");
+        }
+        
         if (rotate) {
             gen.writeln(Math.round(pspageheight) + " 0 translate");
             gen.writeln("90 rotate");
@@ -652,7 +738,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
                 + Math.round(pspageheight) + "]");
         gen.writeln("/ImagingBBox null");
         gen.writeln(">> setpagedevice");
-        //gen.writeln("0.001 0.001 scale");
         concatMatrix(1, 0, 0, -1, 0, pageheight / 1000f);
 
         gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
diff --git a/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java b/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java
new file mode 100644 (file)
index 0000000..789fea2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.extensions;
+
+// FOP
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+/**
+ * Base class for the PostScript-specific extension elements.
+ */
+public abstract class AbstractPSExtensionObject extends FONode {
+
+    private PSSetupCode setupCode = new PSSetupCode();
+    
+    /**
+     * @see org.apache.fop.fo.FONode#FONode(FONode)
+     */
+    public AbstractPSExtensionObject(FONode parent) {
+        super(parent);
+    }
+
+    /**
+     * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
+     * here, blocks XSL FO's from having non-FO parents.
+     */
+    protected void validateChildNode(Locator loc, String nsURI, String localName) 
+                throws ValidationException {
+        if (FO_URI.equals(nsURI)) {
+            invalidChildError(loc, nsURI, localName);
+        }
+    }
+
+    /** @see org.apache.fop.fo.FONode */
+    protected void addCharacters(char[] data, int start, int length,
+                                 PropertyList pList, Locator locator) {
+        if (setupCode.getContent() != null) {
+            StringBuffer sb = new StringBuffer(setupCode.getContent());
+            sb.append(data, start, length - start);
+            setupCode.setContent(sb.toString());
+        } else {
+            setupCode.setContent(new String(data, start, length - start));
+        }
+    }
+
+    /** @see org.apache.fop.fo.XMLObj#getNameSpace() */
+    public String getNameSpace() {
+        return PSExtensionElementMapping.NAMESPACE;
+    }
+    
+    /** @see org.apache.fop.fo.FONode#processNode */
+    public void processNode(String elementName, Locator locator, 
+                            Attributes attlist, PropertyList propertyList)
+                                throws FOPException {
+        String name = attlist.getValue("name");
+        if (name != null && name.length() > 0) {
+            setupCode.setName(name);
+        }
+    }
+
+    /** @see org.apache.fop.fo.FONode#endOfNode() */
+    protected void endOfNode() throws FOPException {
+        super.endOfNode();
+        String s = setupCode.getContent(); 
+        if (s == null || s.length() == 0) {
+            missingChildElementError("#PCDATA");
+        }
+    }
+    
+    /** @see org.apache.fop.fo.FONode#getExtensionAttachment() */
+    public ExtensionAttachment getExtensionAttachment() {
+        return this.setupCode;
+    }
+}
+
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java
new file mode 100644 (file)
index 0000000..3903d7b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.extensions;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ElementMapping;
+
+/**
+ * This class provides the element mapping for the PostScript-specific extensions.
+ */
+public class PSExtensionElementMapping extends ElementMapping {
+
+    /** Namespace for the extension */
+    public static final String NAMESPACE = "http://xmlgraphics.apache.org/fop/postscript"; 
+
+    /** Main constructor */
+    public PSExtensionElementMapping() {
+        this.namespaceURI = NAMESPACE;
+    }
+
+    /** @see org.apache.fop.fo.ElementMapping#initialize() */
+    protected void initialize() {
+        if (foObjs == null) {
+            foObjs = new java.util.HashMap();
+            foObjs.put("ps-setup-code", new PSSetupCodeMaker());
+            foObjs.put("ps-page-setup-code", new PSPageSetupCodeMaker());
+        }
+    }
+
+    static class PSSetupCodeMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PSSetupCodeElement(parent);
+        }
+    }
+
+    static class PSPageSetupCodeMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PSPageSetupCodeElement(parent);
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java
new file mode 100644 (file)
index 0000000..7d3eed4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ValidationException;
+
+/**
+ * Extension element for fox:ps-page-setup-code. 
+ */
+public class PSPageSetupCodeElement extends AbstractPSExtensionObject {
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    public PSPageSetupCodeElement(FONode parent) {
+        super(parent);
+    }
+
+    /** @see org.apache.fop.fo.FONode#startOfNode() */
+    protected void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+            throw new ValidationException(getName() + " must be a child of fo:simple-page-master.");
+        }
+    }
+    
+    /** @see org.apache.fop.fo.FONode#getName() */
+    public String getName() {
+        return "fox:ps-page-setup-code";
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSSetupCode.java b/src/java/org/apache/fop/render/ps/extensions/PSSetupCode.java
new file mode 100644 (file)
index 0000000..846946f
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.extensions;
+
+import java.io.Serializable;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * This is the pass-through value object for the PostScript extension.
+ */
+public class PSSetupCode implements ExtensionAttachment, Serializable {
+
+    /** The category URI for this extension attachment. */
+    public static final String CATEGORY = "apache:fop:extensions:postscript";
+    
+    private String name;
+    private String content;
+
+    /**
+     * No-argument contructor.
+     */
+    public PSSetupCode() {
+        //nop
+    }
+    
+    /**
+     * Default constructor.
+     * @param name the name of the setup code object, may be null
+     * @param content the content of the setup code object
+     */
+    public PSSetupCode(String name, String content) {
+        this.name = name;
+        this.content = content;
+    }
+    
+    /** @return the content */
+    public String getContent() {
+        return content;
+    }
+    
+    /**
+     * Sets the content for the setup code object.
+     * @param content The content to set.
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+    
+    /** @return the name */
+    public String getName() {
+        return name;
+    }
+    
+    /**
+     * Sets the name of the setup code object.
+     * @param name The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /** @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory() */
+    public String getCategory() {
+        return CATEGORY;
+    }
+    
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        return "PSSetupCode(name=" + getName() + ")";
+    }
+}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java
new file mode 100644 (file)
index 0000000..bbf3580
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ValidationException;
+
+/**
+ * Extension element for fox:ps-setup-code. 
+ */
+public class PSSetupCodeElement extends AbstractPSExtensionObject {
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    public PSSetupCodeElement(FONode parent) {
+        super(parent);
+    }
+    
+    /** @see org.apache.fop.fo.FONode#startOfNode() */
+    protected void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            throw new ValidationException(getName() + " must be a child of fo:declarations.");
+        }
+    }
+    
+    /** @see org.apache.fop.fo.FONode#getName() */
+    public String getName() {
+        return "fox:ps-setup-code";
+    }
+    
+}