]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Adds Mark Lillywhite's performance and memory patch in all it's glory.
authorSteve Coffman <gears@apache.org>
Wed, 1 Aug 2001 22:12:54 +0000 (22:12 +0000)
committerSteve Coffman <gears@apache.org>
Wed, 1 Aug 2001 22:12:54 +0000 (22:12 +0000)
Unfortunately breaks marker support.
(AreaTree getNextPage and getPreviousPage return the current page)
XSL-FO with markers is not a good idea until it is fixed.

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

28 files changed:
src/org/apache/fop/apps/AWTStarter.java
src/org/apache/fop/apps/CommandLineStarter.java
src/org/apache/fop/apps/Driver.java
src/org/apache/fop/apps/PrintStarter.java
src/org/apache/fop/apps/StreamRenderer.java [new file with mode: 0644]
src/org/apache/fop/datatypes/IDReferences.java
src/org/apache/fop/fo/FONode.java
src/org/apache/fop/fo/FOText.java
src/org/apache/fop/fo/FOTreeBuilder.java
src/org/apache/fop/fo/FObjMixed.java
src/org/apache/fop/fo/flow/Block.java
src/org/apache/fop/fo/pagination/PageSequence.java
src/org/apache/fop/layout/AreaTree.java
src/org/apache/fop/layout/Page.java
src/org/apache/fop/pdf/PDFDocument.java
src/org/apache/fop/pdf/PDFPage.java
src/org/apache/fop/pdf/PDFPages.java
src/org/apache/fop/pdf/PDFRoot.java
src/org/apache/fop/render/PrintRenderer.java
src/org/apache/fop/render/Renderer.java
src/org/apache/fop/render/awt/AWTRenderer.java
src/org/apache/fop/render/mif/MIFRenderer.java
src/org/apache/fop/render/pcl/PCLRenderer.java
src/org/apache/fop/render/pdf/PDFRenderer.java
src/org/apache/fop/render/ps/PSRenderer.java
src/org/apache/fop/render/txt/TXTRenderer.java
src/org/apache/fop/render/xml/XMLRenderer.java
src/org/apache/fop/tools/TestConverter.java

index 96dc271aab2d4f316f7c9f909702fa57eff73ec1..1bf2b4c8603221c9bfd6cf10b76e63ca5ab263f5 100644 (file)
@@ -11,6 +11,7 @@ package org.apache.fop.apps;
  * Juergen Verwohlt: Juergen.Verwohlt@jCatalog.com,
  * Rainer Steinkuhle: Rainer.Steinkuhle@jCatalog.com,
  * Stanislav Gorkhover: Stanislav.Gorkhover@jCatalog.com
+ * Modified to use streaming API by Mark Lillywhite, mark-fop@inomial.com
  */
 import org.apache.fop.messaging.MessageHandler;
 import org.apache.fop.viewer.*;
@@ -111,15 +112,7 @@ public class AWTStarter extends CommandLineStarter {
 
             // build FO tree: time
             frame.progress(resource.getString("Build FO tree") + " ...");
-            driver.buildFOTree(parser, inputHandler.getInputSource());
-
-            // layout FO tree: time
-            frame.progress(resource.getString("Layout FO tree") + " ...");
-            driver.format();
-
-            // render: time
-            frame.progress(resource.getString("Render") + " ...");
-            driver.render();
+            driver.render(parser, inputHandler.getInputSource());
 
             frame.progress(resource.getString("Show"));
             frame.showPage();
index 6483658265dcec97ef5e99f59b808713d1a5b158..1f56c738e3a557dec06b4436c1f2d19d95e81716 100644 (file)
@@ -24,6 +24,8 @@ import org.apache.fop.configuration.Configuration;
 
 /**
  * super class for all classes which start Fop from the commandline
+ *
+ * Modified to use new streaming API by Mark Lillywhite, mark-fop@inomial.com
  */
 
 public class CommandLineStarter extends Starter {
@@ -59,11 +61,9 @@ public class CommandLineStarter extends Starter {
 
         try {
             driver.setRenderer(commandLineOptions.getRenderer());
-            driver.getRenderer().setOptions(commandLineOptions.getRendererOptions());
-            driver.buildFOTree(parser, inputHandler.getInputSource());
-            driver.format();
             driver.setOutputStream(new FileOutputStream(commandLineOptions.getOutputFile()));
-            driver.render();
+            driver.getRenderer().setOptions(commandLineOptions.getRendererOptions());
+            driver.render(parser, inputHandler.getInputSource());
             System.exit(0);
         } catch (Exception e) {
             if (e instanceof FOPException) {
index 60c250176c6ac334ee274b57ed445e1c73810665..4af65b07ed8775a08309385c2a45db25c2096527 100644 (file)
@@ -11,7 +11,6 @@ package org.apache.fop.apps;
 import org.apache.fop.fo.FOTreeBuilder;
 import org.apache.fop.fo.ElementMapping;
 import org.apache.fop.layout.AreaTree;
-import org.apache.fop.layout.FontInfo;
 import org.apache.fop.render.Renderer;
 import org.apache.fop.messaging.MessageHandler;
 import org.apache.fop.configuration.ConfigurationReader;
@@ -19,6 +18,8 @@ import org.apache.fop.configuration.Configuration;
 import org.apache.fop.tools.DocumentInputSource;
 import org.apache.fop.tools.DocumentReader;
 
+import org.apache.fop.render.pdf.PDFRenderer;
+
 import org.apache.fop.system.BufferManager;
 
 // DOM
@@ -64,9 +65,9 @@ import java.util.*;
  * instantiate the class itself. The advantage of the latter is it
  * enables runtime determination of Renderer and ElementMapping(s).
  * <P>
- * Once the Driver is set up, the buildFOTree method
+ * Once the Driver is set up, the render method
  * is called. Depending on whether DOM or SAX is being used, the
- * invocation of the method is either buildFOTree(Document) or
+ * invocation of the method is either render(Document) or
  * buildFOTree(Parser, InputSource) respectively.
  * <P>
  * A third possibility may be used to build the FO Tree, namely
@@ -80,9 +81,7 @@ import java.util.*;
  * <PRE>
  * Driver driver = new Driver();
  * driver.setRenderer(new org.apache.fop.render.awt.AWTRenderer(translator));
- * driver.buildFOTree(parser, fileInputSource(args[0]));
- * driver.format();
- * driver.render();
+ * driver.render(parser, fileInputSource(args[0]));
  * </PRE>
  */
 public class Driver {
@@ -408,9 +407,10 @@ public class Driver {
      * Build the formatting object tree using the given SAX Parser and
      * SAX InputSource
      */
-    public synchronized void buildFOTree(XMLReader parser, InputSource source)
+    public synchronized void render(XMLReader parser, InputSource source)
             throws FOPException {
-
+        StreamRenderer streamRenderer = new StreamRenderer(_stream, _renderer);
+        _treeBuilder.setStreamRenderer(streamRenderer);
         parser.setContentHandler(_treeBuilder);
         try {
             parser.parse(source);
@@ -428,8 +428,11 @@ public class Driver {
     /**
      * Build the formatting object tree using the given DOM Document
      */
-    public synchronized void buildFOTree(Document document)
+    public synchronized void render(Document document)
             throws FOPException {
+        StreamRenderer streamRenderer = new StreamRenderer(_stream, _renderer);
+        _treeBuilder.setStreamRenderer(streamRenderer);
+        
         try {
             DocumentInputSource source = new DocumentInputSource(document);
             DocumentReader reader = new DocumentReader();
@@ -470,27 +473,7 @@ public class Driver {
         this._bufferManager.addBufferFile(bufferFile);
     }
 
-    /**
-     * format the formatting object tree into an area tree
-     */
-    public synchronized void format() throws FOPException {
-        FontInfo fontInfo = new FontInfo();
-        _renderer.setupFontInfo(fontInfo);
-
-        _areaTree = new AreaTree();
-        _areaTree.setFontInfo(fontInfo);
-
-        _treeBuilder.format(_areaTree);
-    }
-
-    /**
-     * render the area tree to the output form
-     */
-    public synchronized void render() throws IOException, FOPException {
-        _renderer.render(_areaTree, _stream);
-    }
-
-    /**
+    /** 
      * Runs the formatting and renderering process using the previously set
      * inputsource and outputstream
      */
@@ -498,21 +481,22 @@ public class Driver {
         if (_renderer == null) {
             setRenderer(RENDER_PDF);
         }
+        
         if (_source == null) {
             throw new FOPException("InputSource is not set.");
         }
+        
         if (_reader == null) {
             if (!(_source instanceof DocumentInputSource)) {
                 _reader = ConfigurationReader.createParser();
             }
         }
+        
         if (_source instanceof DocumentInputSource) {
-            buildFOTree(((DocumentInputSource)_source).getDocument());
+            render(((DocumentInputSource)_source).getDocument());
         } else {
-            buildFOTree(_reader, _source);
+            render(_reader, _source);
         }
-        format();
-        render();
     }
 
 }
index c50172c4d4a881e0893bd397c4acf63fdb07b345..7cf2636e2fe0f75f91eefff14b54bff66f166426 100644 (file)
@@ -11,6 +11,11 @@ package org.apache.fop.apps;
  * originally contributed by
  * Stanislav Gorkhover: stanislav.gorkhover@jcatalog.com
  * jCatalog Software AG
+ * 
+ * Updated by Mark Lillywhite, mark-fop@inomial.com. Modified to
+ * handle the print job better, added -Ddialog option, removed 
+ * (apparently) redundant copies code, generally cleaned up, and
+ * added interfaces to the new Render API.
  */
 
 
@@ -61,13 +66,20 @@ public class PrintStarter extends CommandLineStarter {
 
         setParserFeatures(parser);
 
-        PrintRenderer renderer = new PrintRenderer();
-
+        PrinterJob pj = PrinterJob.getPrinterJob();
+        if(System.getProperty("dialog") != null)
+          if(!pj.printDialog())
+            throw new FOPException("Printing cancelled by operator");
+            
+        PrintRenderer renderer = new PrintRenderer(pj);
+        int copies = getIntProperty("copies", 1);
+        pj.setCopies(copies);
+        
+        //renderer.setCopies(copies);
+        
         try {
             driver.setRenderer(renderer);
-            driver.buildFOTree(parser, inputHandler.getInputSource());
-            driver.format();
-            driver.render();
+            driver.render(parser, inputHandler.getInputSource());
         } catch (Exception e) {
             if (e instanceof FOPException) {
                 throw (FOPException)e;
@@ -75,38 +87,42 @@ public class PrintStarter extends CommandLineStarter {
             throw new FOPException(e);
         }
 
-        int copies = PrintRenderer.getIntProperty("copies", 1);
-        renderer.setCopies(copies);
-
-        PrinterJob pj = PrinterJob.getPrinterJob();
-        pj.setPageable(renderer);
-
-        pj.setCopies(copies);
-        try {
-            pj.print();
-        } catch (PrinterException pe) {
-            pe.printStackTrace();
+        System.exit(0);
+      }
+        int getIntProperty(String name, int def) {
+          String propValue = System.getProperty(name);
+          if(propValue != null) {
+            try {
+              return Integer.parseInt(propValue);
+            } catch (Exception e) {
+              return def;
+            }
+          } else {
+            return def;
         }
     }
 
+    class PrintRenderer extends AWTRenderer {
 
-    static class PrintRenderer extends AWTRenderer {
-
-        static int EVEN_AND_ALL = 0;
-        static int EVEN = 1;
-        static int ODD = 2;
-
-        int startNumber;
-        int endNumber;
-        int mode = EVEN_AND_ALL;
-        int copies = 1;
+        private static final int EVEN_AND_ALL = 0;
+        private static final int EVEN = 1;
+        private static final int ODD = 2;
 
-        PrintRenderer() {
+        private int startNumber;
+        private int endNumber;
+        private int mode = EVEN_AND_ALL;
+        private int copies = 1;
+        private PrinterJob printerJob;
+        
+        PrintRenderer(PrinterJob printerJob) {
             super(null);
 
+            this.printerJob = printerJob;
             startNumber = getIntProperty("start", 1) - 1;
             endNumber = getIntProperty("end", -1);
 
+            printerJob.setPageable(this);
+
             mode = EVEN_AND_ALL;
             String str = System.getProperty("even");
             if (str != null) {
@@ -118,30 +134,29 @@ public class PrintStarter extends CommandLineStarter {
         }
 
 
-        static int getIntProperty(String name, int def) {
-            String propValue = System.getProperty(name);
-            if (propValue != null) {
-                try {
-                    return Integer.parseInt(propValue);
-                } catch (Exception e) {
-                    return def;
-                }
-            } else {
-                return def;
-            }
-        }
-
-        public void render(AreaTree areaTree,
-                           OutputStream stream) throws IOException {
-            tree = areaTree;
-            if (endNumber == -1) {
-                endNumber = tree.getPages().size();
-            }
 
+       public void stopRenderer(OutputStream outputStream)
+         throws IOException
+       {
+          super.stopRenderer(outputStream);
+          
+          if(endNumber == -1)
+            endNumber = getPageCount();
+            
             Vector numbers = getInvalidPageNumbers();
             for (int i = numbers.size() - 1; i > -1; i--)
-                tree.getPages().removeElementAt(Integer.parseInt((String)numbers.elementAt(i)));
+                removePage(Integer.parseInt((String)numbers.elementAt(i)));
 
+            try {
+              printerJob.print();
+            }
+            catch (PrinterException e)
+            {
+              e.printStackTrace();
+              throw new IOException(
+                 "Unable to print: " + e.getClass().getName() +
+                 ": " + e.getMessage());
+            }
         }
 
         public void renderPage(Page page) {
@@ -154,7 +169,7 @@ public class PrintStarter extends CommandLineStarter {
         private Vector getInvalidPageNumbers() {
 
             Vector vec = new Vector();
-            int max = tree.getPages().size();
+            int max = getPageCount();
             boolean isValid;
             for (int i = 0; i < max; i++) {
                 isValid = true;
@@ -174,6 +189,7 @@ public class PrintStarter extends CommandLineStarter {
             return vec;
         }
 
+        /* TODO: I'm totally not sure that this is necessary -Mark
         void setCopies(int val) {
             copies = val;
             Vector copie = tree.getPages();
@@ -182,7 +198,7 @@ public class PrintStarter extends CommandLineStarter {
             }
 
         }
-
+      */
     }    // class PrintRenderer
 }        // class PrintCommandLine
 
diff --git a/src/org/apache/fop/apps/StreamRenderer.java b/src/org/apache/fop/apps/StreamRenderer.java
new file mode 100644 (file)
index 0000000..e36b6e5
--- /dev/null
@@ -0,0 +1,296 @@
+package org.apache.fop.apps;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+
+import org.xml.sax.SAXException;
+
+import org.apache.fop.layout.FontInfo;
+import org.apache.fop.layout.Page;
+import org.apache.fop.render.Renderer;
+import org.apache.fop.layout.AreaTree;
+import org.apache.fop.datatypes.IDReferences;
+import org.apache.fop.extensions.ExtensionObj;
+import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.messaging.MessageHandler;
+
+/**
+  This class acts as a bridge between the XML:FO parser
+  and the formatting/rendering classes. It will queue
+  PageSequences up until all the IDs required by them
+  are satisfied, at which time it will render the
+  pages.<P>
+
+  StreamRenderer is created by Driver and called from
+  FOTreeBuilder when a PageSequence is created,
+  and AreaTree when a Page is formatted.<P>
+*/
+public class StreamRenderer extends Object
+{
+  private static final boolean MEM_PROFILE_WITH_GC = false;
+
+  /**
+    Somewhere to get our stats from.
+  */
+  private Runtime runtime = Runtime.getRuntime();
+
+  /**
+    Keep track of the number of pages rendered.
+  */
+  int pageCount = 0;
+
+  /**
+    Keep track of heap memory allocated,
+    for statistical purposes.
+  */
+  private long initialMemory;
+
+  /**
+    Keep track of time used by renderer.
+  */
+  private long startTime;
+
+  /**
+    The stream to which this rendering is to be
+    written to. <B>Note</B> that some renderers
+    do not render to a stream, and that this
+    member can therefore be null.
+  */
+  private OutputStream outputStream;
+
+  /**
+    The renderer being used.
+  */
+  private Renderer renderer;
+
+  /**
+    The FontInfo for this renderer.
+  */
+  private FontInfo fontInfo = new FontInfo();
+
+  /**
+    The list of pages waiting to be renderered.
+  */
+  private Vector renderQueue = new Vector();
+
+  /**
+    The current set of IDReferences, passed to the
+    areatrees and pages. This is used by the AreaTree
+    as a single map of all IDs.
+  */
+  private IDReferences idReferences = new IDReferences();
+
+  public StreamRenderer(OutputStream outputStream, Renderer renderer)
+  {
+    this.outputStream = outputStream;
+    this.renderer = renderer;
+  }
+
+  public IDReferences getIDReferences()
+  {
+    return idReferences;
+  }
+
+  public void startRenderer()
+    throws SAXException
+  {
+    pageCount = 0;
+
+    if (MEM_PROFILE_WITH_GC)
+      System.gc();             // This takes time but gives better results
+
+    initialMemory = runtime.totalMemory() - runtime.freeMemory();
+    startTime = System.currentTimeMillis();
+
+    try {
+      renderer.setupFontInfo(fontInfo);
+      renderer.startRenderer(outputStream);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+  }
+
+  public void stopRenderer()
+    throws SAXException
+  {
+    /*
+      Force the processing of any more queue elements,
+      even if they are not resolved.
+    */
+    try {
+      processQueue(true);
+      renderer.stopRenderer(outputStream);
+    }
+    catch (FOPException e)
+    {
+      throw new SAXException(e);
+    }
+    catch (IOException e)
+    {
+      throw new SAXException(e);
+    }
+
+    if (MEM_PROFILE_WITH_GC)
+      System.gc();             // This takes time but gives better results
+
+    long memoryNow = runtime.totalMemory() - runtime.freeMemory();
+    long memoryUsed = (memoryNow - initialMemory) / 1024L;
+
+    MessageHandler.logln("Initial heap size: " + (initialMemory/1024L) + "Kb");
+    MessageHandler.logln("Current heap size: " + (memoryNow/1024L) + "Kb");
+    MessageHandler.logln("Total memory used: " + memoryUsed + "Kb");
+
+    if (!MEM_PROFILE_WITH_GC)
+    {
+      MessageHandler.logln("  Memory use is indicative; no GC was performed");
+      MessageHandler.logln("  These figures should not be used comparatively");
+    }
+
+    long timeUsed = System.currentTimeMillis() - startTime;
+
+    MessageHandler.logln("Total time used: " + timeUsed + "ms");
+    MessageHandler.logln("Pages rendererd: " + pageCount);
+    MessageHandler.logln("Avg render time: " + (timeUsed / pageCount) + "ms/page");
+  }
+
+  /**
+    Format the PageSequence. The PageSequence
+    formats Pages and adds them to the AreaTree,
+    which subsequently calls the StreamRenderer
+    instance (this) again to render the page.
+    At this time the page might be printed
+    or it might be queued. A page might not
+    be renderable immediately if the IDReferences
+    are not all valid. In this case we defer
+    the rendering until they are all valid.
+  */
+  public void render(PageSequence pageSequence)
+    throws SAXException
+  {
+    AreaTree a = new AreaTree(this);
+    a.setFontInfo(fontInfo);
+
+    try {
+      pageSequence.format(a);
+    }
+    catch (FOPException e)
+    {
+      throw new SAXException(e);
+    }
+  }
+
+  public synchronized void queuePage(Page page)
+    throws FOPException, IOException
+  {
+    /*
+      Try to optimise on the common case that there are
+      no pages pending and that all ID references are
+      valid on the current pages. This short-cuts the
+      pipeline and renders the area immediately.
+    */
+    if ((renderQueue.size() == 0) && idReferences.isEveryIdValid())
+      renderer.render(page, outputStream);
+    else
+      addToRenderQueue(page);
+    
+    pageCount++;
+  }
+
+  private synchronized void addToRenderQueue(Page page)
+    throws FOPException, IOException
+  {
+    RenderQueueEntry entry = new RenderQueueEntry(page);
+    renderQueue.addElement(entry);
+
+    /*
+      The just-added entry could (possibly) resolve the
+      waiting entries, so we try to process the queue
+      now to see.
+    */
+    processQueue(false);
+  }
+
+  /**
+    Try to process the queue from the first entry forward.
+    If an entry can't be processed, then the queue can't
+    move forward, so return.
+  */
+  private synchronized void processQueue(boolean force)
+    throws FOPException, IOException
+  {
+    while (renderQueue.size() > 0)
+    {
+      RenderQueueEntry entry = (RenderQueueEntry) renderQueue.elementAt(0);
+      if ((!force) && (!entry.isResolved()))
+        break;
+      
+      renderer.render(entry.getPage(), outputStream);
+      
+      /* TODO
+       Enumeration rootEnumeration =
+         entry.getAreaTree().getExtensions().elements();
+       while (rootEnumeration.hasMoreElements())
+         renderTree.addExtension((ExtensionObj) rootEnumeration.nextElement());
+      */
+
+      renderQueue.removeElementAt(0);
+    }
+  }
+
+  /**
+    A RenderQueueEntry consists of the Page to be queued,
+    plus a list of outstanding ID references that need to be
+    resolved before the Page can be renderered.<P>
+  */
+  class RenderQueueEntry extends Object
+  {
+    /*
+      The Page that has outstanding ID references.
+    */
+    private Page page;
+
+    /*
+      A list of ID references (names).
+    */
+    private Vector unresolvedIdReferences = new Vector();
+
+    public RenderQueueEntry(Page page)
+    {
+      this.page = page;
+
+      Enumeration e = idReferences.getInvalidElements();
+      while (e.hasMoreElements())
+       unresolvedIdReferences.addElement(e.nextElement());
+    }
+
+    public Page getPage()
+    {
+      return page;
+    }
+
+    /**
+      See if the outstanding references are resolved
+      in the current copy of IDReferences.
+    */
+    public boolean isResolved()
+    {
+      if ((unresolvedIdReferences.size() == 0) || idReferences.isEveryIdValid())
+       return true;
+
+      //
+      // See if any of the unresolved references are still unresolved.
+      //
+      Enumeration e = unresolvedIdReferences.elements();
+      while (e.hasMoreElements())
+       if (!idReferences.doesIDExist((String) e.nextElement()))
+         return false;
+
+      unresolvedIdReferences.removeAllElements();
+      return true;
+    }
+  }
+}
index 11b1b19b6b8de76b7d6ba1475a8b88848116b81f..139cd485827fead2bd90829c8928b678e01798f3 100644 (file)
@@ -19,7 +19,15 @@ import java.util.NoSuchElementException;
 import org.apache.fop.layout.Area;
 import org.apache.fop.apps.FOPException;
 
-
+/**
+  IDReferences contains a map of IDs and the objects to which
+  they refer. It also contains a list of references to IDs which
+  have yet to be encountered.
+  
+  Modified by Mark Lillywhite mark-fop@inomial.com. Added
+  getInvalidElements() so that StreamRenderer cna tell what
+  hasn't been determined yet.
+  */
 public class IDReferences {
     private Hashtable idReferences, idValidation;
 
@@ -278,4 +286,8 @@ public class IDReferences {
         node.setPosition(x, y);
     }
 
+    public Enumeration getInvalidElements()
+    {
+      return idValidation.keys();
+    }
 }
index 6c0213efe8b6a04198969220d68d33ddf25795ed..6c6875899c0ec2ad8996677a513ee404b29c0d1a 100644 (file)
@@ -22,6 +22,13 @@ import java.util.Hashtable;
 
 /**
  * base class for nodes in the formatting object tree
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com. Made
+ * Vector a protected member. (/me things this should be
+ * a private member with an API for adding children;
+ * this woudl save a lot of memory because the Vector
+ * would not have to be instantiated unless the node had
+ * children).
  */
 abstract public class FONode {
 
@@ -31,8 +38,7 @@ abstract public class FONode {
 
     public BufferManager bufferManager;
 
-    public Vector children =
-        new Vector();    // made public for searching for id's
+    protected Vector children = new Vector();    // made public for searching for id's
 
     /**
      * value of marker before layout begins
index 20095d988e045fa7501f35ed92cc19ce688957f6..05e5a660a9978197ed97597033c27342a6c5e75c 100644 (file)
@@ -20,10 +20,16 @@ import org.apache.fop.system.BufferManager;
 
 /**
  * a text node in the formatting object tree
+ *
+ * Modified by Mark Lillywhite, mark-fop@inomial.com.
+ * Unfortunately the BufferManager implementatation holds
+ * onto references to the character data in this object
+ * longer than the lifetime of the object itself, causing
+ * excessive memory consumption and OOM errors.
  */
 public class FOText extends FONode {
 
-    // protected char[] ca;
+    protected char[] ca;
     protected int start;
     protected int length;
 
@@ -46,10 +52,12 @@ public class FOText extends FONode {
     public FOText(char[] chars, int s, int e, FObj parent) {
         super(parent);
         this.start = 0;
-        char ca[] = new char[e - s];
+        this.ca = new char[e - s];
         for (int i = s; i < e; i++)
             ca[i - s] = chars[i];
         this.length = e - s;
+        
+        /* ML - remove refs to BufferManager
         this.bufferManager = parent.bufferManager;
         if (this.bufferManager != null) {
             bufferManager.writeBuffer((Object)this, ca);
@@ -57,6 +65,7 @@ public class FOText extends FONode {
             System.out.println("abnormal exit");
             System.exit(0);
         }
+        */
     }
 
     public void setUnderlined(boolean ul) {
@@ -73,7 +82,9 @@ public class FOText extends FONode {
 
 
     public boolean willCreateArea() {
-        char ca[] = this.bufferManager.readBuffer((Object)this);
+        // ML - remove refs to BufferManager
+        //char ca[] = this.bufferManager.readBuffer((Object)this);
+        
         this.whiteSpaceCollapse =
             this.parent.properties.get("white-space-collapse").getEnum();
         if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE
@@ -92,7 +103,8 @@ public class FOText extends FONode {
     }
 
     public Status layout(Area area) throws FOPException {
-        char ca[] = this.bufferManager.readBuffer((Object)this);
+        // ML - remove refs to BufferManager
+        // char ca[] = this.bufferManager.readBuffer((Object)this);
         if (!(area instanceof BlockArea)) {
             MessageHandler.errorln("WARNING: text outside block area"
                                    + new String(ca, start, length));
index c719a2b350214bf9eb84334a6021fa86cf628ed8..75ac802f06fbfc130ca9ed5d639e30be8c0e7957 100644 (file)
@@ -11,9 +11,10 @@ package org.apache.fop.fo;
 import org.apache.fop.layout.AreaTree;
 import org.apache.fop.messaging.MessageHandler;
 import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.StreamRenderer;
 import org.apache.fop.fo.pagination.Root;
 import org.apache.fop.system.BufferManager;
-
+import org.apache.fop.fo.pagination.PageSequence;
 
 // SAX
 import org.xml.sax.helpers.DefaultHandler;
@@ -28,6 +29,14 @@ import java.io.IOException;
 
 /**
  * SAX Handler that builds the formatting object tree.
+ * 
+ * Modified by Mark Lillywhite mark-fop@inomial.com. Now uses
+ * StreamRenderer to automagically render the document as
+ * soon as it receives a page-sequence end-tag. Also,
+ * calls methods to set up and shut down the renderer at
+ * the beginning and end of the FO document. Finally,
+ * supresses adding the PageSequence object to the Root,
+ * since it is parsed immediately.
  */
 public class FOTreeBuilder extends DefaultHandler implements TreeBuilder {
 
@@ -59,6 +68,22 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder {
      */
     protected Hashtable unknownFOs = new Hashtable();
 
+    /**
+     *
+     * The class that handles formatting and rendering to a stream
+     * (mark-fop@inomial.com)
+     */
+     private StreamRenderer streamRenderer;
+     
+     public FOTreeBuilder()
+     {
+     }
+     
+     public void setStreamRenderer(StreamRenderer streamRenderer)
+     {
+       this.streamRenderer = streamRenderer;
+     }
+      
     /**
      * add a mapping from element name to maker.
      *
@@ -120,19 +145,39 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder {
     /**
      * SAX Handler for the end of an element
      */
-    public void endElement(String uri, String localName, String rawName) {
+    public void endElement(String uri, String localName, String rawName)
+      throws SAXException
+    {
         currentFObj.end();
-        currentFObj = (FObj)currentFObj.getParent();
+        
+        //
+        // mark-fop@inomial.com - tell the stream renderer to render
+        // this page-sequence
+        //
+        if(currentFObj instanceof PageSequence)
+          streamRenderer.render((PageSequence) currentFObj);
+        
+          currentFObj = (FObj)currentFObj.getParent();
     }
 
     /**
      * SAX Handler for the start of the document
      */
-    public void startDocument() {
+    public void startDocument()
+      throws SAXException
+    {
         rootFObj = null;    // allows FOTreeBuilder to be reused
         MessageHandler.logln("building formatting object tree");
+        streamRenderer.startRenderer();
     }
 
+    public void endDocument()
+      throws SAXException
+    {
+      MessageHandler.logln("Parsing of document complete, stopping renderer");
+      streamRenderer.stopRenderer();
+    }
+      
     /**
      * SAX Handler for the start of an element
      */
@@ -182,7 +227,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder {
                                                         + " be root, not "
                                                         + fobj.getName()));
             }
-        } else {
+        } else if(!(fobj instanceof org.apache.fop.fo.pagination.PageSequence)){
             currentFObj.addChild(fobj);
         }
 
@@ -203,6 +248,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder {
     public void reset() {
         currentFObj = null;
         rootFObj = null;
+        streamRenderer = null;
     }
 
     public boolean hasData() {
index d69c681796da629b2c6e96c66c658ccf00ed389c..ddab909cc1eab7d8ce00f3e475a921d6aa139ae6 100644 (file)
@@ -33,7 +33,7 @@ public class FObjMixed extends FObj {
     }
 
     protected void addCharacters(char data[], int start, int length) {
-        children.addElement(new FOText(data, start, length, this));
+        addChild(new FOText(data, start, length, this));
     }
 
     public Status layout(Area area) throws FOPException {
index 8b85f806aaedd77562f3ef8b21a94b43adcdd386..9830d1665741a9413e5dbc938574e39ade644b00 100644 (file)
@@ -15,6 +15,21 @@ import org.apache.fop.layout.*;
 import org.apache.fop.datatypes.*;
 import org.apache.fop.apps.FOPException;
 
+ /*
+   Modified by Mark Lillywhite mark-fop@inomial.com. The changes
+   here are based on memory profiling and do not change functionality.
+   Essentially, the Block object had a pointer to a BlockArea object
+   that it created. The BlockArea was not referenced after the Block
+   was finished except to determine the size of the BlockArea, however
+   a reference to the BlockArea was maintained and this caused a lot of
+   GC problems, and was a major reason for FOP memory leaks. So,
+   the reference to BlockArea was made local, the required information
+   is now stored (instead of a reference to the complex BlockArea object)
+   and it appears that there are a lot of changes in this file, in fact
+   there are only a few sematic changes; mostly I just got rid of 
+   "this." from blockArea since BlockArea is now local.
+   */
+   
 public class Block extends FObjMixed {
 
     public static class Maker extends FObj.Maker {
@@ -43,11 +58,12 @@ public class Block extends FObjMixed {
     int blockWidows;
     int blockOrphans;
 
+    int areaHeight = 0;
+    int contentWidth = 0;
+    
     String id;
     int span;
 
-    BlockArea blockArea;
-
     // this may be helpful on other FOs too
     boolean anythingLaidOut = false;
 
@@ -58,6 +74,8 @@ public class Block extends FObjMixed {
     }
 
     public Status layout(Area area) throws FOPException {
+        BlockArea blockArea;
+
         // MessageHandler.error(" b:LAY[" + marker + "] ");
 
 
@@ -157,23 +175,23 @@ public class Block extends FObjMixed {
         }
 
         int spaceLeft = area.spaceLeft();
-        this.blockArea =
+        blockArea =
             new BlockArea(propMgr.getFontState(area.getFontInfo()),
                           area.getAllocationWidth(), area.spaceLeft(),
                           startIndent, endIndent, textIndent, align,
                           alignLast, lineHeight);
-        this.blockArea.setGeneratedBy(this);
+        blockArea.setGeneratedBy(this);
         this.areasGenerated++;
         if (this.areasGenerated == 1)
-            this.blockArea.isFirst(true);
+            blockArea.isFirst(true);
             // for normal areas this should be the only pair
-        this.blockArea.addLineagePair(this, this.areasGenerated);
+        blockArea.addLineagePair(this, this.areasGenerated);
 
         // markers
         if (this.hasMarkers())
-            this.blockArea.addMarkers(this.getMarkers());
+            blockArea.addMarkers(this.getMarkers());
 
-        this.blockArea.setParent(area);    // BasicLink needs it
+        blockArea.setParent(area);    // BasicLink needs it
         blockArea.setPage(area.getPage());
         blockArea.setBackgroundColor(backgroundColor);
         blockArea.setBorderAndPadding(propMgr.getBorderAndPadding());
@@ -250,37 +268,41 @@ public class Block extends FObjMixed {
         if (area instanceof BlockArea) {
             area.start();
         }
-
+        // This is not needed any more and it consumes a LOT
+        // of memory. So we release it for the GC.
+        areaHeight= blockArea.getHeight();
+        contentWidth= blockArea.getContentWidth();
+        
         // no break if last in area tree, or trailing in context
         // area
         int breakAfterStatus = propMgr.checkBreakAfter(area);
         if (breakAfterStatus != Status.OK) {
             this.marker = BREAK_AFTER;
+            blockArea = null; //Faster GC - BlockArea is big
             return new Status(breakAfterStatus);
         }
 
         if (keepWithNext != 0) {
+               blockArea = null; // Faster GC - BlockArea is big
             return new Status(Status.KEEP_WITH_NEXT);
         }
 
         // MessageHandler.error(" b:OK" + marker + " ");
-        this.blockArea.isLast(true);
+        blockArea.isLast(true);
+        blockArea = null; // Faster GC - BlockArea is big
         return new Status(Status.OK);
     }
 
     public int getAreaHeight() {
-        return blockArea.getHeight();
+        return areaHeight;
     }
 
 
     /**
      * Return the content width of the boxes generated by this FO.
      */
-    public int getContentWidth() {
-        if (blockArea != null)
-            return blockArea.getContentWidth();    // getAllocationWidth()??
-        else
-            return 0;                              // not laid out yet
+    public int getContentWidth() {       
+        return contentWidth;    // getAllocationWidth()??
     }
 
     public int getSpan() {
index 0c9beaca2602d78749a97e2e5657e3a726e2cb5a..0020836e8a14b35e9f3f6c6e0e70fc00b024d6d2 100644 (file)
@@ -4,6 +4,11 @@
  * For details on use and redistribution please refer to the
  * LICENSE file included with these sources.
  */
+/*
+ Modified by Mark Lillywhite mark-fop@inomial.com. Does not add
+ itself to the root any more. Does not hang onto currentPage
+ pointer, which caused GC issues.
+ */
 
 package org.apache.fop.fo.pagination;
 
@@ -137,7 +142,7 @@ public class PageSequence extends FObj {
 
         if (parent.getName().equals("fo:root")) {
             this.root = (Root)parent;
-            this.root.addPageSequence(this);
+            // this.root.addPageSequence(this);
         } else {
             throw new FOPException("page-sequence must be child of root, not "
                                    + parent.getName());
@@ -206,6 +211,7 @@ public class PageSequence extends FObj {
      * Runs the formatting of this page sequence into the given area tree
      */
     public void format(AreaTree areaTree) throws FOPException {
+
         Status status = new Status(Status.OK);
 
         this.layoutMasterSet.resetPageMasters();
@@ -301,6 +307,8 @@ public class PageSequence extends FObj {
         // handle the 'force-page-count'
         forcePage(areaTree, firstAvailPageNumber);
 
+        currentPage = null;
+        
         MessageHandler.logln("");
     }
 
index 6f2bb864ac45316c9971f152d8ceab31e563f1a7..d5a2901248eea812983a5a22fed5cf00f6a1b8a6 100644 (file)
@@ -9,6 +9,7 @@ package org.apache.fop.layout;
 
 // FOP
 import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.StreamRenderer;
 import org.apache.fop.fo.flow.StaticContent;
 import org.apache.fop.svg.*;
 import org.apache.fop.render.Renderer;
@@ -23,6 +24,18 @@ import java.util.Enumeration;
 import java.util.Stack;
 import java.util.Vector;
 
+/*
+ * Modified by Mark Lillywhite, mark-fop@inomial.com. No longer keeps
+   a list of pages in the tree, instead these are passed to the
+   StreamRenderer. No longer keeps it's own list of IDReferences;
+   these are handled by StreamRenderer. In many ways StreamRenderer
+   has taken over from AreaTree and possibly this might be a better
+   place to deal with things in the future..?<P>
+   
+   Any extensions added to the AreaTree while generating a page
+   are given to the Page for the renderer to deal with.
+  */
 public class AreaTree {
 
     /**
@@ -31,17 +44,18 @@ public class AreaTree {
      */
     FontInfo fontInfo;
 
-    /* list of all the pages */
-    Vector pageList = new Vector();
-
     /**
      * List of root extension objects
      */
-    Vector rootExtensions = new Vector();
-
-
-    IDReferences idReferences = new IDReferences();
-
+    Vector rootExtensions = null;
+    
+    private StreamRenderer streamRenderer;
+    
+    public AreaTree(StreamRenderer streamRenderer)
+    {
+       this.streamRenderer = streamRenderer;
+    }
+    
     public void setFontInfo(FontInfo fontInfo) {
         this.fontInfo = fontInfo;
     }
@@ -50,65 +64,39 @@ public class AreaTree {
         return this.fontInfo;
     }
 
-    public void addPage(Page page) {
-        this.pageList.addElement(page);
-    }
-
-    public Vector getPages() {
-        return this.pageList;
-    }
-
-    public IDReferences getIDReferences() {
-        return idReferences;
-    }
-
-    public void addExtension(ExtensionObj obj) {
-        rootExtensions.addElement(obj);
+    public Page getNextPage(Page current, boolean isWithinPageSequence,
+                            boolean isFirstCall) {
+        return current;
     }
 
-    public Vector getExtensions() {
-        return rootExtensions;
+    public Page getPreviousPage(Page current, boolean isWithinPageSequence,
+                                boolean isFirstCall) {  
+        return current;
     }
 
-    public Page getNextPage(Page current, boolean isWithinPageSequence,
-                            boolean isFirstCall) {
-        Page nextPage = null;
-        int pageIndex = 0;
-        if (isFirstCall)
-            pageIndex = pageList.size();
-        else
-            pageIndex = pageList.indexOf(current);
-        if ((pageIndex + 1) < pageList.size()) {
-            nextPage = (Page)pageList.elementAt(pageIndex + 1);
-            if (isWithinPageSequence
-                    &&!nextPage.getPageSequence().equals(current.getPageSequence())) {
-                nextPage = null;
-            }
+    public void addPage(Page page)
+    throws FOPException
+    {
+        try {
+          page.setExtensions(rootExtensions);
+          rootExtensions = null;
+          streamRenderer.queuePage(page);
+        }
+        catch (IOException e)
+        {
+           throw new FOPException(e);
         }
-        return nextPage;
     }
 
-    public Page getPreviousPage(Page current, boolean isWithinPageSequence,
-                                boolean isFirstCall) {
-        Page previousPage = null;
-        int pageIndex = 0;
-        if (isFirstCall)
-            pageIndex = pageList.size();
-        else
-            pageIndex = pageList.indexOf(current);
-        // System.out.println("Page index = " + pageIndex);
-        if ((pageIndex - 1) >= 0) {
-            previousPage = (Page)pageList.elementAt(pageIndex - 1);
-            PageSequence currentPS = current.getPageSequence();
-            // System.out.println("Current PS = '" + currentPS + "'");
-            PageSequence previousPS = previousPage.getPageSequence();
-            // System.out.println("Previous PS = '" + previousPS + "'");
-            if (isWithinPageSequence &&!previousPS.equals(currentPS)) {
-                // System.out.println("Outside page sequence");
-                previousPage = null;
-            }
-        }
-        return previousPage;
+    public IDReferences getIDReferences() {
+        return streamRenderer.getIDReferences();
     }
 
+    public void addExtension(ExtensionObj obj)
+    {
+      if(rootExtensions ==null)
+        rootExtensions = new Vector();
+      rootExtensions.addElement(obj);
+    }
+    
 }
index c83a86bece071cf15a624d58eda8c54617c5be5c..61600a19a15bf67712e96bbe1abb847610fc5be3 100644 (file)
@@ -12,12 +12,18 @@ import org.apache.fop.render.Renderer;
 import org.apache.fop.fo.flow.*;
 import org.apache.fop.fo.*;
 import org.apache.fop.apps.*;
+import org.apache.fop.datatypes.IDReferences;
 import org.apache.fop.fo.pagination.PageSequence;
 
 // Java
 import java.util.Vector;
 import java.util.Enumeration;
 
+/*Modified by Mark Lillywhite mark-fop@inomial.com. Added getIDReferences.
+  This is just a convenience method for renderers who no longer have access
+  to the AreaTree when rendering.
+  */
+
 public class Page {
 
     private int height;
@@ -30,7 +36,9 @@ public class Page {
     private AreaContainer end;
 
     private AreaTree areaTree;
-
+    
+    private Vector rootExtensions;
+    
     private PageSequence pageSequence;
 
     protected int pageNumber = 0;
@@ -51,6 +59,11 @@ public class Page {
         markers = new Vector();
     }
 
+    public IDReferences getIDReferences()
+    {
+      return areaTree.getIDReferences();
+    }
+    
     public void setPageSequence(PageSequence pageSequence) {
         this.pageSequence = pageSequence;
     }
@@ -172,6 +185,14 @@ public class Page {
         return footnotes;
     }
 
+    public Vector getExtensions() {
+      return rootExtensions;
+    }
+
+    public void setExtensions(Vector extensions) {
+      this.rootExtensions = extensions;
+    }
+    
     public void setPendingFootnotes(Vector v) {
         footnotes = v;
         if (footnotes != null) {
index e26c59982265014e5818fb47757477a19dc20e8b..a1a3515fff4b330f344ef0dd4e3b155bec31a2b5 100644 (file)
@@ -43,9 +43,16 @@ import java.awt.Rectangle;
  * positions within the document. For this reason the PDF document must
  * keep track of the character position of each object.  The document
  * also keeps direct track of the /Root, /Info and /Resources objects.
+ *
+ * Modified by Mark Lillywhite, mark-fop@inomial.com. The changes
+ * involve: ability to output pages one-at-a-time in a streaming
+ * fashion (rather than storing them all for output at the end);
+ * ability to write the /Pages object after writing the rest
+ * of the document; ability to write to a stream and flush
+ * the object list; enhanced trailer output; cleanups.
  */
 public class PDFDocument {
-
+    private static final Integer locationPlaceholder = new Integer(0);
     /**
      * the version of PDF supported
      */
@@ -61,6 +68,9 @@ public class PDFDocument {
      */
     protected Vector location = new Vector();
 
+    /** List of objects to write in the trailer */
+      private Vector trailerObjects = new Vector();
+
     /**
      * the counter for object numbering
      */
@@ -81,6 +91,12 @@ public class PDFDocument {
      */
     protected PDFRoot root;
 
+    /** The root outline object */
+    private PDFOutline outlineRoot = null;
+    
+    /** The /Pages object (mark-fop@inomial.com) */
+    private PDFPages pages;
+    
     /**
      * the /Info object
      */
@@ -129,14 +145,30 @@ public class PDFDocument {
     protected Hashtable xObjectsMap = new Hashtable();
 
     /**
-     * creates an empty PDF document
+     * creates an empty PDF document <p>
+     * 
+     * The constructor creates a /Root and /Pages object to
+     * track the document but does not write these objects until
+     * the trailer is written. Note that the object ID of the
+     * pages object is determined now, and the xref table is
+     * updated later. This allows Pages to refer to their
+     * Parent before we write it out. This took me a long
+     * time to work out, and is so obvious now. Sigh.
+     * mark-fop@inomial.com. Maybe I should do a PDF course.
      */
     public PDFDocument() {
 
         /* create the /Root, /Info and /Resources objects */
-        this.root = makeRoot();
-        this.info = makeInfo();
+        this.pages = makePages();
+        
+        // Create the Root object
+        this.root = makeRoot(pages);
+        
+        // Create the Resources object
         this.resources = makeResources();
+        
+        // Make the /Info record
+        this.info = makeInfo();
     }
 
     /**
@@ -149,30 +181,40 @@ public class PDFDocument {
     }
 
     /**
-     * make /Root object as next object
-     *
-     * @return the created /Root object
+     * Make a /Catalog (Root) object. This object is written in
+     * the trailer.
      */
-    protected PDFRoot makeRoot() {
+    public PDFRoot makeRoot(PDFPages pages)
+    {
 
-        /*
-         * create a PDFRoot with the next object number and add to
-         * list of objects
-         */
-        PDFRoot pdfRoot = new PDFRoot(++this.objectcount);
-        this.objects.addElement(pdfRoot);
-
-        /*
-         * create a new /Pages object to be root of Pages hierarchy
-         * and add to list of objects
+         /*
+         * Make a /Pages object. This object is written in the trailer.
          */
-        PDFPages rootPages = new PDFPages(++this.objectcount);
-        this.objects.addElement(rootPages);
+         PDFRoot pdfRoot = new PDFRoot(++this.objectcount, pages);
+         addTrailerObject(pdfRoot);
+         return pdfRoot;
+    }       
+    /**
+     * Make a /Pages object. This object is written in the trailer.
+     */
+     
+     public PDFPages makePages()
+     {
+        PDFPages pdfPages = new PDFPages(++this.objectcount);
+        addTrailerObject(pdfPages);
+        return pdfPages;
+     }
 
-        /* inform the /Root object of the /Pages root */
-        pdfRoot.setRootPages(rootPages);
-        return pdfRoot;
-    }
+    /**
+     * Make a /Resources object. This object is written in the trailer.
+     */
+     public PDFResources makeResources()
+     {
+       PDFResources pdfResources = new PDFResources(++this.objectcount);
+       addTrailerObject(pdfResources);
+       return pdfResources;
+     }
 
     /**
      * make an /Info object
@@ -193,22 +235,6 @@ public class PDFDocument {
         return pdfInfo;
     }
 
-    /**
-     * make a /Resources object
-     *
-     * @return the created /Resources object
-     */
-    private PDFResources makeResources() {
-
-        /*
-         * create a PDFResources with the next object number and add
-         * to list of objects
-         */
-        PDFResources pdfResources = new PDFResources(++this.objectcount);
-        this.objects.addElement(pdfResources);
-        return pdfResources;
-    }
-
     /**
      * Make a Type 0 sampled function
      *
@@ -973,18 +999,32 @@ public class PDFDocument {
                 goToReference =
                     idReferences.createInternalLinkGoTo(destination,
                                                         ++this.objectcount);
-                this.objects.addElement(idReferences.getPDFGoTo(destination));
+                addTrailerObject(idReferences.getPDFGoTo(destination));
             }
         } else {        // id was not found, so create it
             idReferences.createNewId(destination);
             idReferences.addToIdValidationList(destination);
             goToReference = idReferences.createInternalLinkGoTo(destination,
                     ++this.objectcount);
-            this.objects.addElement(idReferences.getPDFGoTo(destination));
+            addTrailerObject(idReferences.getPDFGoTo(destination));
         }
         return goToReference;
     }
-
+    
+    public void addTrailerObject(PDFObject object)
+    {
+      this.trailerObjects.addElement(object);
+    }
+    
+    /**
+      Ensure there is room in the locations xref for the number of
+      objects that have been created.
+     */
+    private void prepareLocations()
+    {
+      while(location.size() < objectcount)
+        location.addElement(locationPlaceholder);
+    }
 
     /**
      * make a stream object
@@ -1023,14 +1063,18 @@ public class PDFDocument {
     }
 
     /**
-     * Make the root Outlines object
+     * Get the root Outlines object. This method does not write
+     * the outline to the PDF document, it simply creates a
+     * reference for later.
      */
-    public PDFOutline makeOutlineRoot() {
-        PDFOutline obj = new PDFOutline(++this.objectcount, null, null);
-        this.objects.addElement(obj);
-        root.setRootOutline(obj);
-
-        return obj;
+    public PDFOutline getOutlineRoot() {
+      if(outlineRoot != null)
+        return outlineRoot;
+      
+      outlineRoot = new PDFOutline(++this.objectcount, null, null);
+      addTrailerObject(outlineRoot);
+      root.setRootOutline(outlineRoot);
+      return outlineRoot;
     }
 
     /**
@@ -1044,7 +1088,7 @@ public class PDFDocument {
         String goToRef = getGoToReference(destination);
 
         PDFOutline obj = new PDFOutline(++this.objectcount, label, goToRef);
-        // System.out.println("created new outline object");
+        System.out.println("created new outline object");
 
         if (parent != null) {
             parent.addOutline(obj);
@@ -1068,15 +1112,10 @@ public class PDFDocument {
      *
      * @param writer the OutputStream to output the document to
      */
-    public void output(OutputStream stream) throws IOException {
-
-        /*
-         * output the header and increment the character position by
-         * the header's length
-         */
-        this.position += outputHeader(stream);
+    public void output(OutputStream stream) throws IOException
+    {
 
-        this.resources.setXObjects(xObjects);
+        prepareLocations();
 
         Enumeration en = this.objects.elements();
         while (en.hasMoreElements()) {
@@ -1087,7 +1126,8 @@ public class PDFDocument {
              * add the position of this object to the list of object
              * locations
              */
-            this.location.addElement(new Integer(this.position));
+            location.setElementAt(
+             new Integer(this.position),object.getNumber() - 1);
 
             /*
              * output the object and increment the character position
@@ -1096,28 +1136,26 @@ public class PDFDocument {
             this.position += object.output(stream);
         }
 
-        /*
-         * output the xref table and increment the character position
-         * by the table's length
-         */
-        this.position += outputXref(stream);
-
-        /* output the trailer and flush the Stream */
-        outputTrailer(stream);
-        stream.flush();
+        this.objects.clear();
     }
 
     /**
-     * write the PDF header
+     * write the PDF header <P>
+     *
+     * This method must be called prior to formatting
+     * and outputting AreaTrees.
      *
      * @param stream the OutputStream to write the header to
      * @return the number of bytes written
      */
-    protected int outputHeader(OutputStream stream) throws IOException {
-        int length = 0;
+    public void outputHeader(OutputStream stream)
+    throws IOException
+    {
+        this.position=0;
+        
         byte[] pdf = ("%PDF-" + this.pdfVersion + "\n").getBytes();
         stream.write(pdf);
-        length += pdf.length;
+        this.position += pdf.length;
 
         // output a binary comment as recommended by the PDF spec (3.4.1)
         byte[] bin = {
@@ -1125,9 +1163,9 @@ public class PDFDocument {
             (byte)'\n'
         };
         stream.write(bin);
-        length += bin.length;
+        this.position += bin.length;
 
-        return length;
+        this.resources.setXObjects(xObjects);
     }
 
     /**
@@ -1135,14 +1173,33 @@ public class PDFDocument {
      *
      * @param stream the OutputStream to write the trailer to
      */
-    protected void outputTrailer(OutputStream stream) throws IOException {
-
+    public void outputTrailer(OutputStream stream)
+    throws IOException
+    {
+       output(stream);
+       Enumeration e = trailerObjects.elements();
+       while(e.hasMoreElements())
+       {
+         PDFObject o = (PDFObject) e.nextElement();
+         this.location.setElementAt(
+           new Integer(this.position), o.getNumber() - 1);
+         this.position += o.output(stream);
+       }
+        /* output the xref table and increment the character position
+          by the table's length */
+        this.position += outputXref(stream);
+        
         /* construct the trailer */
-        String pdf = "trailer\n<<\n/Size " + (this.objectcount + 1)
-                     + "\n/Root " + this.root.number + " "
-                     + this.root.generation + " R\n/Info " + this.info.number
-                     + " " + this.info.generation + " R\n>>\nstartxref\n"
-                     + this.xref + "\n%%EOF\n";
+        String pdf = 
+         "trailer\n" +
+         "<<\n" +
+         "/Size " + (this.objectcount + 1) + "\n" +
+         "/Root " + this.root.number + " " + this.root.generation + " R\n" +
+         "/Info " + this.info.number + " " + this.info.generation + " R\n" +
+         ">>\n" +
+         "startxref\n" +
+         this.xref + "\n" +
+         "%%EOF\n";
 
         /* write the trailer */
         stream.write(pdf.getBytes());
index 4f9ae9d24c42b81e8913aa5bf07e510caab6a609..8fce260985e54ce05d125948f2a9a2314d1ed470 100644 (file)
@@ -14,13 +14,19 @@ package org.apache.fop.pdf;
  * specifies the dimensions of the page and references a /Resources
  * object, a contents stream and the page's parent in the page
  * hierarchy.
+ * 
+ * Modified by Mark Lillywhite, mark-fop@inomial.com. The Parent
+ * object was being referred to by reference, but all that we
+ * ever used from the Parent was it's PDF object ID, and according
+ * to the memory profile this was causing OOM issues. So, we store
+ * only the object ID of the parent, rather than the parent itself.
  */
 public class PDFPage extends PDFObject {
 
     /**
-     * the page's parent, a /Pages object
+     * the page's parent, a PDF reference object
      */
-    protected PDFPages parent;
+    protected String parent;
 
     /**
      * the page's /Resource object
@@ -77,7 +83,7 @@ public class PDFPage extends PDFObject {
      * @param parent the /Pages object that is this page's parent
      */
     public void setParent(PDFPages parent) {
-        this.parent = parent;
+        this.parent = parent.referencePDF();
     }
 
     /**
@@ -112,7 +118,7 @@ public class PDFPage extends PDFObject {
 
         sb = sb.append(this.number + " " + this.generation + " obj\n"
                        + "<< /Type /Page\n" + "/Parent "
-                       + this.parent.referencePDF() + "\n"
+                       + this.parent + "\n"
                        + "/MediaBox [ 0 0 " + this.pagewidth + " "
                        + this.pageheight + " ]\n" + "/Resources "
                        + this.resources.referencePDF() + "\n" + "/Contents "
index cef40f1d39e3367bfcc904be393dc2d0779766d3..a3b5b60aade2ec4484fed236f69c75ad01a8ad6a 100644 (file)
@@ -34,13 +34,16 @@ public class PDFPages extends PDFObject {
     // private PDFPages parent;
 
     /**
-     * create a /Pages object.
+     * create a /Pages object. NOTE: The PDFPages
+     * object must be created before the PDF document is
+     * generated, but it is not written to the stream immediately.
+     * It must aslo be allocated an object ID (so that the kids
+     * can refer to the parent) so that the XRef table needs to
+     * be updated before this object is written.
      *
      * @param number the object's number
      */
     public PDFPages(int number) {
-
-        /* generic creation of object */
         super(number);
     }
 
@@ -50,7 +53,7 @@ public class PDFPages extends PDFObject {
      * @param page the PDFPage to add.
      */
     public void addPage(PDFPage page) {
-        this.kids.addElement(page);
+        this.kids.addElement(page.referencePDF());
         page.setParent(this);
         this.incrementCount();
     }
@@ -77,12 +80,13 @@ public class PDFPages extends PDFObject {
      *
      * @return the PDF string
      */
-    public byte[] toPDF() {
+    public byte[] toPDF()
+    {
         StringBuffer p = new StringBuffer(this.number + " " + this.generation
                                           + " obj\n<< /Type /Pages\n/Count "
                                           + this.getCount() + "\n/Kids [");
         for (int i = 0; i < kids.size(); i++) {
-            p = p.append(((PDFObject)kids.elementAt(i)).referencePDF() + " ");
+            p = p.append(kids.elementAt(i) + " ");
         }
         p = p.append("] >>\nendobj\n");
         return p.toString().getBytes();
index 1850fbf949c413b76a3dd72334729e969aa35d70..2bae2b91b10989bb40b31d8de071c3530f6a99d9 100644 (file)
@@ -27,12 +27,26 @@ public class PDFRoot extends PDFObject {
     private PDFOutline _outline;
 
     /**
-     * create a Root (/Catalog) object
+     * create a Root (/Catalog) object. NOTE: The PDFRoot
+     * object must be created before the PDF document is
+     * generated, but it is not assigned an object ID until
+     * it is about to be written (immediately before the xref
+     * table as part of the trsailer). (mark-fop@inomial.com)
      *
      * @param number the object's number
      */
-    public PDFRoot(int number) {
+    public PDFRoot(int number, PDFPages pages) {
         super(number);
+        setRootPages(pages);
+    }
+    
+    /**
+     * Before the root is written to the document stream,
+     * make sure it's object number is set. Package-private access
+     * only; outsiders should not be fiddling with this stuff.
+     */
+    void setNumber(int number) {
+      this.number = number;
     }
 
     /**
@@ -57,13 +71,23 @@ public class PDFRoot extends PDFObject {
         _outline = outline;
     }
 
+   public PDFOutline getRootOutline()
+   {
+       return _outline;
+   }
+
 
     /**
-     * represent the object as PDF
+     * represent the object as PDF.
+     *
+     * @throws IllegalStateException if the setNumber() method has
+     * not been called.
      *
      * @return the PDF string
      */
-    public byte[] toPDF() {
+    public byte[] toPDF()
+      throws IllegalStateException
+    {
         StringBuffer p = new StringBuffer(this.number + " " + this.generation
                                           + " obj\n<< /Type /Catalog\n/Pages "
                                           + this.rootPages.referencePDF()
index 10c5eb7d019bf15099390ecf1b2c5146d818c50a..6c67420e68501d82deef32570873c9a8bb4c4baa 100644 (file)
@@ -38,6 +38,13 @@ import java.util.Enumeration;
 
 /**
  * Abstract base class of "Print" type renderers.
+ * 
+ * Modified by Mark Lillywhite mark-fop@inomial.com. Removed
+ * the render(AreaTree, OutputStream) method, and added
+ * no-op concrete implementation of startRenderer() and
+ * stopRenderer(). The method render(Page, OutputStream)
+ * is not mentioned in this class but it is inherited from
+ * the Renderer interface.
  */
 public abstract class PrintRenderer implements Renderer {
     // vvv These are not currently referenced by the PrintRenderer, but are common to PCL and PDF renderers - so declare here.
@@ -121,15 +128,6 @@ public abstract class PrintRenderer implements Renderer {
      */
     public abstract void setProducer(String producer);
 
-    /**
-     * render the areas
-     * 
-     * @param areaTree the laid-out area tree
-     * @param stream the OutputStream to write to
-     */
-    public abstract void render(AreaTree areaTree, OutputStream stream) 
-            throws IOException, FOPException;
-
     /**
      * add a line to the current stream
      * 
@@ -596,5 +594,20 @@ public abstract class PrintRenderer implements Renderer {
         this.fontInfo = fontInfo;
         FontSetup.setup(fontInfo);
     } 
+    
+    /**
+      Default start renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+    }
+    /**
+     Default stop renderer method. This would
+     normally be overridden. (mark-fop@inomial.com).
+    */
+    {
+    }
 
 }
index 0365a8511ae6863e05755fe10ef6fbed046adbb3..c28ffc9236598d113b10cad322d5a042ab34876a 100644 (file)
@@ -89,8 +89,9 @@ public interface Renderer {
     /**
      * render the given area tree to the given stream
      */
-    public void render(AreaTree areaTree, 
-                       OutputStream stream) throws IOException, FOPException;
+    //public void render(AreaTree areaTree, OutputStream stream) throws IOException, FOPException;
+    public void render(Page page, OutputStream stream)
+      throws IOException, FOPException;
 
     /**
      * render the given area container
@@ -157,4 +158,10 @@ public interface Renderer {
      */
     public void renderLeaderArea(LeaderArea area);
 
+    public void startRenderer(OutputStream outputStream)
+      throws IOException;
+    
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException;
+
 }
index ef2ef9f63bb27344f9cff9295543eafac31d33fa..57595eb4708d35563037a7fda1193616f1277b2b 100644 (file)
@@ -52,13 +52,20 @@ import java.text.*;
 
 import org.apache.fop.render.Renderer;
 
+/**
+  Modified by Mark Lillywhite mark-fop@inomial.com. Did lots of
+  cleaning up and made the class implement the new Renderer
+  interface. This class could also do with a general audit,
+  and I suspect it's not swing-thread-safe either.
+*/
+
 public class AWTRenderer implements Renderer, Printable, Pageable {
 
     protected int pageWidth = 0;
     protected int pageHeight = 0;
     protected double scaleFactor = 100.0;
     protected int pageNumber = 0;
-    protected AreaTree tree;
+    protected Vector pageList = new Vector();
     protected ProgressListener progressListener = null;
     protected Translator res = null;
 
@@ -66,6 +73,8 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
     protected Hashtable fontStyles = new Hashtable();
     protected Color saveColor = null;
 
+    protected IDReferences idReferences = null;
+    
     /**
      * Image Object and Graphics Object. The Graphics Object is the Graphics
      * object that is contained withing the Image Object.
@@ -322,35 +331,37 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
      * @return the number of pages
      */
     public int getPageCount() {
-        if (tree == null) {
-            return 0;
-        }
+        return pageList.size();
+    }
 
-        return tree.getPages().size();
+    public void removePage(int page) {
+      pageList.removeElementAt(page);
     }
 
-    public void render(int aPageNumber) {
-        if (tree != null) {
-            try {
-                render(tree, aPageNumber);
-            } catch (IOException e) {
-                e.printStackTrace();
-                // This exception can't occur because we are not dealing with
-                // any files.
-            }
-        }
+    public void render(int aPageNumber)
+    {
+      if(aPageNumber >= pageList.size())
+        return;
+        
+      try{
+       render((Page) pageList.elementAt(aPageNumber));
+      } catch(IOException e){
+        e.printStackTrace();
+        // This exception can't occur because we are not dealing with
+        // any files
+      }      
     }
 
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException {
-        tree = areaTree;
-        render(areaTree, 0);
+    public void render(Page page, OutputStream stream)
+      throws IOException
+    {
+      pageList.addElement(page);
     }
 
-    public void render(AreaTree areaTree,
-                       int aPageNumber) throws IOException {
-        tree = areaTree;
-        Page page = (Page)areaTree.getPages().elementAt(aPageNumber);
+    public void render(Page page)
+      throws IOException
+    {
+        idReferences = page.getIDReferences();   
 
         pageWidth = (int)((float)page.getWidth() / 1000f + .5);
         pageHeight = (int)((float)page.getHeight() / 1000f + .5);
@@ -693,7 +704,7 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
         String s;    // = area.getText();
         if (area.getPageNumberID()
                 != null) {    // this text is a page number, so resolve it
-            s = tree.getIDReferences().getPageNumber(area.getPageNumberID());
+            s = idReferences.getPageNumber(area.getPageNumberID());
             if (s == null) {
                 s = "";
             }
@@ -872,14 +883,14 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
 
     public int print(Graphics g, PageFormat pageFormat,
                      int pageIndex) throws PrinterException {
-        if (pageIndex >= tree.getPages().size())
+        if (pageIndex >= pageList.size())
             return NO_SUCH_PAGE;
 
         Graphics2D oldGraphics = graphics;
         int oldPageNumber = pageNumber;
 
         graphics = (Graphics2D)g;
-        Page aPage = (Page)tree.getPages().elementAt(pageIndex);
+        Page aPage = (Page)pageList.elementAt(pageIndex);
         renderPage(aPage);
         graphics = oldGraphics;
 
@@ -887,15 +898,15 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
     }
 
     public int getNumberOfPages() {
-        return tree.getPages().size();
+        return pageList.size();
     }
 
     public PageFormat getPageFormat(int pageIndex)
             throws IndexOutOfBoundsException {
-        if (pageIndex >= tree.getPages().size())
+        if (pageIndex >= pageList.size())
             return null;
 
-        Page page = (Page)tree.getPages().elementAt(pageIndex);
+        Page page = (Page)pageList.elementAt(pageIndex);
         PageFormat pageFormat = new PageFormat();
         Paper paper = new Paper();
 
@@ -1078,4 +1089,16 @@ public class AWTRenderer implements Renderer, Printable, Pageable {
         public void registerExtension(BridgeExtension be) {}
 
     }
+    
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+    }
+    
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+       render(0);
+    }
+    
 }
index 778c992d1cf007ed05cad04bd75902ca173365d7..567d5935904076bce149d1191a7040d05ca6dfde 100644 (file)
@@ -40,6 +40,12 @@ import java.util.Hashtable;
 
 /**
  * Renderer that renders areas to MIF
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com. Updated to
+ * collect all the Pages and print them out at the end. This means
+ * that the MIF renderer does not stream, but on the other hand
+ * it should still work. I don't have an MIF view to test it with,
+ * you see.
  */
 public class MIFRenderer implements Renderer {
 
@@ -92,31 +98,6 @@ public class MIFRenderer implements Renderer {
         this.options = options;
     }
 
-    /**
-     * render the areas into MIF
-     *
-     * @param areaTree the laid-out area tree
-     * @param writer the PrintWriter to write the MIF with
-     */
-
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException, FOPException {
-
-        MessageHandler.logln("rendering areas to MIF");
-        // idReferences=areaTree.getIDReferences();
-        // this.pdfResources = this.pdfDoc.getResources();
-        // this.pdfDoc.setIDReferences(idReferences);
-        Enumeration e = areaTree.getPages().elements();
-        while (e.hasMoreElements()) {
-            this.renderPage((Page)e.nextElement());
-        }
-
-        // MessageHandler.logln("writing out MIF");
-
-        this.mifDoc.output(stream);
-        stream.close();
-    }
-
     /**
      * set up the given FontInfo
      */
@@ -496,4 +477,30 @@ public class MIFRenderer implements Renderer {
      */
     public void renderLeaderArea(LeaderArea area) {}
 
+    /**
+      Default start renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+       MessageHandler.logln("rendering areas to MIF");
+    }
+    
+    /**
+      Default stop renderer method. This would
+      normally be overridden. (mark-fop@inomial.com)
+    */
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+       MessageHandler.logln("writing out MIF");
+       this.mifDoc.output(outputStream);
+       outputStream.flush();
+    }
+
+    public void render(Page page, OutputStream outputStream) {
+       this.renderPage(page);
+    }
 }
+
index d47da9a59320189c5c540ae0022566f3effc938e..9ccd4f39ff4024bd89c6d84bbc4edbcbe090c13c 100755 (executable)
@@ -35,6 +35,8 @@ import java.util.Enumeration;
  * Renderer that renders areas to PCL
  * Created by Arthur E Welch III while at M&I EastPoint Technology
  * Donated by EastPoint to the Apache FOP project March 2, 2001.
+ * Modified by Mark Lillywhite mark-fop@inomial.com to use the
+ * new Renderer interface.
  */
 public class PCLRenderer extends PrintRenderer {
 
@@ -79,50 +81,7 @@ public class PCLRenderer extends PrintRenderer {
      * @param producer string indicating application producing PCL
      */
     public void setProducer(String producer) {}
-
-    /**
-     * render the areas into PCL
-     *
-     * @param areaTree the laid-out area tree
-     * @param stream the Outputstream to write the PCL to
-     */
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException, FOPException {
-        MessageHandler.logln("rendering areas to PCL");
-        idReferences = areaTree.getIDReferences();
-        // this.pdfResources = this.pdfDoc.getResources();
-        // this.pdfDoc.setIDReferences(idReferences);
-        Enumeration e = areaTree.getPages().elements();
-
-        currentStream = new PCLStream(stream);
-
-        // Set orientation.
-        if (orientation > -1)
-            currentStream.add("\033&l" + orientation + "O");
-        else
-            currentStream.add("\033&l0O");
-        if (orientation == 1 || orientation == 3)
-            xoffset = -144;
-        else
-            xoffset = -180;
-
-        // Reset the margins.
-        currentStream.add("\033" + "9\033&l0E");
-
-
-        while (e.hasMoreElements()) {
-            this.renderPage((Page)e.nextElement());
-        }
-        if (!idReferences.isEveryIdValid()) {
-            // throw new FOPException("The following id's were referenced but not found: "+idReferences.getInvalidIds()+"\n");
-            MessageHandler.errorln("Warning: The following id's were referenced but not found: "
-                                   + idReferences.getInvalidIds() + "\n");
-        }
-
-        MessageHandler.logln("writing out PCL");
-        stream.flush();
-    }
-
     /**
      * add a line to the current stream
      *
@@ -749,5 +708,37 @@ public class PCLRenderer extends PrintRenderer {
          * }
          */
     }
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {        MessageHandler.logln("rendering areas to PCL");
+        currentStream = new PCLStream(outputStream);
+
+        // Set orientation.
+        if (orientation > -1)
+            currentStream.add("\033&l" + orientation + "O");
+        else
+            currentStream.add("\033&l0O");
+        if (orientation == 1 || orientation == 3)
+            xoffset = -144;
+        else
+            xoffset = -180;
+
+        // Reset the margins.
+        currentStream.add("\033" + "9\033&l0E");
+    }
+    
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        MessageHandler.logln("writing out PCL");
+        outputStream.flush();
+    }
+
+    public void render(Page page, OutputStream outputStream)
+      throws IOException
+    {
+        idReferences = page.getIDReferences();
+           this.renderPage(page);
+    }
 
 }
index 7d9e2738106a841c9eab982ecce93de4f0f85ee6..4554ff7e51f3bedfa4b099e8e74b21e30c261b66 100644 (file)
@@ -51,6 +51,15 @@ import java.awt.Dimension;
 
 /**
  * Renderer that renders areas to PDF
+ *
+ * Modified by Mark Lillywhite, mark-fop@inomial.com to use the
+ * new Renderer interface. The PDF renderer is by far the trickiest
+ * renderer and the best supported by FOP. It also required some
+ * reworking in the way that Pages, Catalogs and the Root object
+ * were written to the stream. The output document should now still
+ * be a 100% compatible PDF document, but hte order of the document
+ * writing is significantly different. See also the changes
+ * to PDFPage, PDFPages and PDFRoot.
  */
 public class PDFRenderer extends PrintRenderer {
 
@@ -103,8 +112,6 @@ public class PDFRenderer extends PrintRenderer {
      */
     int prevWordWidth = 0;
 
-    private PDFOutline rootOutline;
-
     /**
      * reusable word area string buffer to reduce memory usage
      */
@@ -138,37 +145,18 @@ public class PDFRenderer extends PrintRenderer {
         this.pdfDoc.setProducer(producer);
     }
 
-    /**
-     * render the areas into PDF
-     *
-     * @param areaTree the laid-out area tree
-     * @param stream the OutputStream to write the PDF to
-     */
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException, FOPException {
-        MessageHandler.logln("rendering areas to PDF");
-        idReferences = areaTree.getIDReferences();
-        this.pdfResources = this.pdfDoc.getResources();
-        this.pdfDoc.setIDReferences(idReferences);
-        Enumeration e = areaTree.getPages().elements();
-        while (e.hasMoreElements()) {
-            this.renderPage((Page)e.nextElement());
-        }
-
-        if (!idReferences.isEveryIdValid()) {
-            // throw new FOPException("The following id's were referenced but not found: "+idReferences.getInvalidIds()+"\n");
-            MessageHandler.errorln("WARNING: The following id's were referenced but not found: "
-                                   + idReferences.getInvalidIds() + "\n");
-
-        }
-        renderRootExtensions(areaTree);
-
-        FontSetup.addToResources(this.pdfDoc, fontInfo);
-
-        MessageHandler.logln("writing out PDF");
-        this.pdfDoc.output(stream);
+    public void startRenderer(OutputStream stream)
+      throws IOException
+    {
+       pdfDoc.outputHeader(stream);
     }
 
+    public void stopRenderer(OutputStream stream)
+      throws IOException
+    {
+       pdfDoc.outputTrailer(stream);
+    }
+    
     /**
      * add a line to the current stream
      *
@@ -622,6 +610,7 @@ public class PDFRenderer extends PrintRenderer {
     }
 
 
+
     /**
      * Convert a char to a multibyte hex representation
      */
@@ -681,6 +670,24 @@ public class PDFRenderer extends PrintRenderer {
         }
     }
 
+    public void render(Page page, OutputStream outputStream)
+      throws FOPException, IOException
+    {
+      // MessageHandler.logln("rendering single page to PDF");
+      this.idReferences = page.getIDReferences();
+      this.pdfResources = this.pdfDoc.getResources();
+      this.pdfDoc.setIDReferences(idReferences);
+      this.renderPage(page);
+
+      FontSetup.addToResources(this.pdfDoc, fontInfo);
+
+      // TODO: this needs to be implemented
+      renderRootExtensions(page);
+
+      // MessageHandler.logln("writing out PDF");
+      this.pdfDoc.output(outputStream);
+    }
+
     /**
      * render page into PDF
      *
@@ -778,8 +785,8 @@ public class PDFRenderer extends PrintRenderer {
         return rs;
     }
 
-    protected void renderRootExtensions(AreaTree areaTree) {
-        Vector v = areaTree.getExtensions();
+    protected void renderRootExtensions(Page page) {
+        Vector v = page.getExtensions();
         if (v != null) {
             Enumeration e = v.elements();
             while (e.hasMoreElements()) {
@@ -793,14 +800,12 @@ public class PDFRenderer extends PrintRenderer {
     }
 
     private void renderOutline(Outline outline) {
-        if (rootOutline == null) {
-            rootOutline = this.pdfDoc.makeOutlineRoot();
-        }
+        PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
         PDFOutline pdfOutline = null;
         Outline parent = outline.getParentOutline();
         if (parent == null) {
             pdfOutline =
-                this.pdfDoc.makeOutline(rootOutline,
+                this.pdfDoc.makeOutline(outlineRoot,
                                         outline.getLabel().toString(),
                                         outline.getInternalDestination());
         } else {
index 16ed11fac42bba0be65c5e54348c7e5f6584c5f4..bbbe0e748ea0bf887935d7c8f4f555d11d23bb80 100644 (file)
@@ -68,7 +68,11 @@ import java.awt.Dimension;
  * DocumentProcessColors stuff (probably needs to be configurable, then maybe
  * add a color to grayscale conversion for bitmaps to make output smaller (See
  * PCLRenderer), font embedding, support different character encodings, try to
- * implement image transparency, positioning of images is wrong etc.
+ * implement image transparency, positioning of images is wrong etc. <P>
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
+ * Renderer interface. This PostScript renderer appears to be the
+ * most efficient at producing output.
  *
  * @author Jeremias Märki
  */
@@ -100,6 +104,8 @@ public class PSRenderer implements Renderer {
     private float currGreen;
     private float currBlue;
 
+    private FontInfo fontInfo;
+
     protected Hashtable options;
 
 
@@ -120,45 +126,6 @@ public class PSRenderer implements Renderer {
         this.options = options;
     }
 
-
-    /**
-     * render the areas into PostScript
-     *
-     * @param areaTree the laid-out area tree
-     * @param stream the OutputStream to give the PostScript to
-     */
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException {
-        MessageHandler.logln("rendering areas to PostScript");
-        this.out = new PSStream(stream);
-        write("%!PS-Adobe-3.0");
-        write("%%Creator: " + this.producer);
-        write("%%Pages: " + areaTree.getPages().size());
-        write("%%DocumentProcessColors: Black");
-        write("%%DocumentSuppliedResources: procset FOPFonts");
-        write("%%EndComments");
-        write("%%BeginDefaults");
-        write("%%EndDefaults");
-        write("%%BeginProlog");
-        write("%%EndProlog");
-        write("%%BeginSetup");
-        writeFontDict(areaTree.getFontInfo());
-        write("%%EndSetup");
-        write("FOPFonts begin");
-
-        comment("% --- AreaTree begin");
-        Enumeration e = areaTree.getPages().elements();
-        while (e.hasMoreElements()) {
-            this.renderPage((Page)e.nextElement());
-        }
-        comment("% --- AreaTree end");
-        write("%%Trailer");
-        write("%%EOF");
-        this.out.flush();
-        MessageHandler.logln("written out PostScript");
-    }
-
-
     /**
      * write out a command
      */
@@ -285,6 +252,7 @@ public class PSRenderer implements Renderer {
     public void setupFontInfo(FontInfo fontInfo) {
         /* use PDF's font setup to get PDF metrics */
         org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
+        this.fontInfo = fontInfo;
     }
 
     /**
@@ -1054,4 +1022,46 @@ public class PSRenderer implements Renderer {
         public void registerExtension(BridgeExtension be) {}
 
     }
+
+    /**
+      Default start renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */ 
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        MessageHandler.logln("rendering areas to PostScript");
+     
+        this.out = new PSStream(outputStream);
+        write("%!PS-Adobe-3.0");
+        write("%%Creator: "+this.producer);
+        write("%%DocumentProcessColors: Black");
+        write("%%DocumentSuppliedResources: procset FOPFonts");
+        write("%%EndComments");
+        write("%%BeginDefaults");
+        write("%%EndDefaults");
+        write("%%BeginProlog");
+        write("%%EndProlog");
+        write("%%BeginSetup");
+        writeFontDict(fontInfo);
+        write("%%EndSetup");
+        write("FOPFonts begin");
+    }
+    
+    /**
+      Default stop renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        write("%%Trailer");
+        write("%%EOF");
+        this.out.flush();
+        MessageHandler.logln("written out PostScript");
+    }
+
+    public void render(Page page, OutputStream outputStream) {
+      this.renderPage(page);
+     }
 }
index eaff0eb88a8e5ca6957abb62e496770e828b97e1..11b9f31f99955fa336afd17d34a667e37868a6c9 100755 (executable)
@@ -35,6 +35,9 @@ import java.util.Hashtable;
 
 /**
  * Renderer that renders areas to plain text
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com to use the new
+ * Renderer interface.
  */
 public class TXTRenderer extends PrintRenderer {
 
@@ -71,7 +74,7 @@ public class TXTRenderer extends PrintRenderer {
         "\f";                        // Every page except the last one will end with this string.
     public boolean suppressGraphics =
         false;    // If true then graphics/decorations will not be rendered - text only.
-
+    boolean firstPage = false;
     /**
      * options
      */
@@ -93,39 +96,6 @@ public class TXTRenderer extends PrintRenderer {
      */
     public void setProducer(String producer) {}
 
-    /**
-     * render the areas into text
-     *
-     * @param areaTree the laid-out area tree
-     * @param writer the PrintWriter to write the PDF with
-     */
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException, FOPException {
-        MessageHandler.logln("rendering areas to TEXT");
-        idReferences = areaTree.getIDReferences();
-        Enumeration e = areaTree.getPages().elements();
-        currentStream = new PCLStream(stream);
-
-        boolean first = true;
-
-        while (e.hasMoreElements()) {
-            if (first)
-                first = false;
-            else
-                currentStream.add(pageEnding);
-            this.renderPage((Page)e.nextElement());
-        }
-        currentStream.add(lineEnding);
-        if (!idReferences.isEveryIdValid()) {
-            // throw new FOPException("The following id's were referenced but not found: "+idReferences.getInvalidIds()+"\n");
-            MessageHandler.errorln("Warning: The following id's were referenced but not found: "
-                                   + idReferences.getInvalidIds() + "\n");
-        }
-
-        MessageHandler.logln("writing out TEXT");
-        stream.flush();
-    }
-
     void addStr(int row, int col, String str, boolean ischar) {
         if (debug)
             System.out.println("TXTRenderer.addStr(" + row + ", " + col
@@ -1702,5 +1672,36 @@ public class TXTRenderer extends PrintRenderer {
          * }
          */
     }
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+      MessageHandler.logln("rendering areas to TEXT");
+      currentStream = new PCLStream(outputStream);
+      firstPage=true;
+    }
+    
+   /**
+     * In Mark's patch, this is endRenderer
+     * However, I couldn't see how it builds that way, so
+     * i changed it. - Steve gears@apache.org
+     */
+
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        MessageHandler.logln("writing out TEXT");
+        outputStream.flush();
+    }
 
+    public void render(Page page, OutputStream outputStream)
+    {
+      idReferences = page.getIDReferences();
+
+      if ( firstPage )
+       firstPage = false;
+      else
+       currentStream.add(pageEnding);
+      this.renderPage(page);
+      currentStream.add(lineEnding);
+     }
 }
index b249f523f38fd6c3910946a8ba73fea26cd4abcc..6aeca10fd96a82e32458193c047cb822875e76d8 100644 (file)
@@ -26,6 +26,9 @@ import java.util.Hashtable;
 
 /**
  * Renderer that renders areas to XML for debugging purposes.
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com to use the
+ * new renderer interface. Not 100% certain that this is correct.
  */
 public class XMLRenderer implements Renderer {
 
@@ -67,27 +70,12 @@ public class XMLRenderer implements Renderer {
         this.producer = producer;
     }
 
-    /**
-     * render the areas into XML
-     *
-     * @param areaTree the laid-out area tree
-     * @param stream the OutputStream to give the XML to
-     */
-    public void render(AreaTree areaTree,
-                       OutputStream stream) throws IOException {
-        MessageHandler.logln("rendering areas to XML");
-        this.writer = new PrintWriter(stream);
-        this.writer.write("<?xml version=\"1.0\"?>\n<!-- produced by "
-                          + this.producer + " -->\n");
-        writeStartTag("<AreaTree>");
-        Enumeration e = areaTree.getPages().elements();
-        while (e.hasMoreElements()) {
-            this.renderPage((Page)e.nextElement());
-        }
-        writeEndTag("</AreaTree>");
-        this.writer.flush();
-        MessageHandler.errorln("written out XML");
-    }
+    
+    public void render(Page page, OutputStream outputStream)
+      throws IOException
+    {
+      this.renderPage(page);
+     }
 
     /**
      * write out spaces to make indent
@@ -439,5 +427,30 @@ public class XMLRenderer implements Renderer {
     private boolean isCoarseXml() {
         return ((Boolean)options.get("fineDetail")).booleanValue();
     }
-
+    
+    /**
+      Default start renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */
+    public void startRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        MessageHandler.logln("rendering areas to XML");
+        this.writer = new PrintWriter(outputStream);
+        this.writer.write( "<?xml version=\"1.0\"?>\n<!-- produced by " +
+                           this.producer + " -->\n");
+        writeStartTag("<AreaTree>");
+    }
+    
+    /**
+      Default stop renderer method. This would
+      normally be overridden. (mark-fop@inomial.com).
+    */
+    public void stopRenderer(OutputStream outputStream)
+      throws IOException
+    {
+        writeEndTag("</AreaTree>");
+        this.writer.flush();
+        MessageHandler.errorln("written out XML");
+     }
 }
index 3f4538d326609554808f0a0320c8c523c146ae0f..0e91530c33c1f245ffbed7f632d933ff337c636d 100644 (file)
@@ -30,6 +30,9 @@ import org.xml.sax.SAXParseException;
  * The area tree can be used for automatic comparisons between different
  * versions of FOP or the pdf can be view for manual checking and
  * pdf rendering.
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com to use the new Driver
+ * interface.
  */
 public class TestConverter {
     boolean failOnly = false;
@@ -221,9 +224,7 @@ public class TestConverter {
             rendererOptions.put("fineDetail", new Boolean(false));
             driver.getRenderer().setOptions(rendererOptions);
             driver.getRenderer().setProducer("Testsuite Converter");
-
-            driver.buildFOTree(parser, inputHandler.getInputSource());
-            driver.format();
+            
             String outname = xmlFile.getName();
             if (outname.endsWith(".xml")) {
                 outname = outname.substring(0, outname.length() - 4);
@@ -231,7 +232,7 @@ public class TestConverter {
             driver.setOutputStream(new FileOutputStream(new File(destdir,
                     outname + (outputPDF ? ".pdf" : ".at.xml"))));
             // System.out.println("ddir:" + destdir + " on:" + outname + ".pdf");
-            driver.render();
+            driver.render(parser, inputHandler.getInputSource());
 
             // check difference
             if (compare != null) {