aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Coffman <gears@apache.org>2001-08-01 22:12:54 +0000
committerSteve Coffman <gears@apache.org>2001-08-01 22:12:54 +0000
commiteeb2adff569057e2b6deb089ba9dffb21fb3a00b (patch)
treec8968573d60b496aa17befb1afbec840ddc1b355
parenta5bc30d4a3977d60f0e70c2792bc4e3a37913a3d (diff)
downloadxmlgraphics-fop-eeb2adff569057e2b6deb089ba9dffb21fb3a00b.tar.gz
xmlgraphics-fop-eeb2adff569057e2b6deb089ba9dffb21fb3a00b.zip
Adds Mark Lillywhite's performance and memory patch in all it's glory.
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
-rw-r--r--src/org/apache/fop/apps/AWTStarter.java11
-rw-r--r--src/org/apache/fop/apps/CommandLineStarter.java8
-rw-r--r--src/org/apache/fop/apps/Driver.java52
-rw-r--r--src/org/apache/fop/apps/PrintStarter.java114
-rw-r--r--src/org/apache/fop/apps/StreamRenderer.java296
-rw-r--r--src/org/apache/fop/datatypes/IDReferences.java14
-rw-r--r--src/org/apache/fop/fo/FONode.java10
-rw-r--r--src/org/apache/fop/fo/FOText.java20
-rw-r--r--src/org/apache/fop/fo/FOTreeBuilder.java56
-rw-r--r--src/org/apache/fop/fo/FObjMixed.java2
-rw-r--r--src/org/apache/fop/fo/flow/Block.java54
-rw-r--r--src/org/apache/fop/fo/pagination/PageSequence.java10
-rw-r--r--src/org/apache/fop/layout/AreaTree.java108
-rw-r--r--src/org/apache/fop/layout/Page.java23
-rw-r--r--src/org/apache/fop/pdf/PDFDocument.java221
-rw-r--r--src/org/apache/fop/pdf/PDFPage.java14
-rw-r--r--src/org/apache/fop/pdf/PDFPages.java16
-rw-r--r--src/org/apache/fop/pdf/PDFRoot.java32
-rw-r--r--src/org/apache/fop/render/PrintRenderer.java31
-rw-r--r--src/org/apache/fop/render/Renderer.java11
-rw-r--r--src/org/apache/fop/render/awt/AWTRenderer.java81
-rw-r--r--src/org/apache/fop/render/mif/MIFRenderer.java57
-rwxr-xr-xsrc/org/apache/fop/render/pcl/PCLRenderer.java79
-rw-r--r--src/org/apache/fop/render/pdf/PDFRenderer.java79
-rw-r--r--src/org/apache/fop/render/ps/PSRenderer.java90
-rwxr-xr-xsrc/org/apache/fop/render/txt/TXTRenderer.java69
-rw-r--r--src/org/apache/fop/render/xml/XMLRenderer.java57
-rw-r--r--src/org/apache/fop/tools/TestConverter.java9
28 files changed, 1095 insertions, 529 deletions
diff --git a/src/org/apache/fop/apps/AWTStarter.java b/src/org/apache/fop/apps/AWTStarter.java
index 96dc271aa..1bf2b4c86 100644
--- a/src/org/apache/fop/apps/AWTStarter.java
+++ b/src/org/apache/fop/apps/AWTStarter.java
@@ -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();
diff --git a/src/org/apache/fop/apps/CommandLineStarter.java b/src/org/apache/fop/apps/CommandLineStarter.java
index 648365826..1f56c738e 100644
--- a/src/org/apache/fop/apps/CommandLineStarter.java
+++ b/src/org/apache/fop/apps/CommandLineStarter.java
@@ -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) {
diff --git a/src/org/apache/fop/apps/Driver.java b/src/org/apache/fop/apps/Driver.java
index 60c250176..4af65b07e 100644
--- a/src/org/apache/fop/apps/Driver.java
+++ b/src/org/apache/fop/apps/Driver.java
@@ -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();
}
}
diff --git a/src/org/apache/fop/apps/PrintStarter.java b/src/org/apache/fop/apps/PrintStarter.java
index c50172c4d..7cf2636e2 100644
--- a/src/org/apache/fop/apps/PrintStarter.java
+++ b/src/org/apache/fop/apps/PrintStarter.java
@@ -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
index 000000000..e36b6e599
--- /dev/null
+++ b/src/org/apache/fop/apps/StreamRenderer.java
@@ -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;
+ }
+ }
+}
diff --git a/src/org/apache/fop/datatypes/IDReferences.java b/src/org/apache/fop/datatypes/IDReferences.java
index 11b1b19b6..139cd4858 100644
--- a/src/org/apache/fop/datatypes/IDReferences.java
+++ b/src/org/apache/fop/datatypes/IDReferences.java
@@ -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();
+ }
}
diff --git a/src/org/apache/fop/fo/FONode.java b/src/org/apache/fop/fo/FONode.java
index 6c0213efe..6c6875899 100644
--- a/src/org/apache/fop/fo/FONode.java
+++ b/src/org/apache/fop/fo/FONode.java
@@ -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
diff --git a/src/org/apache/fop/fo/FOText.java b/src/org/apache/fop/fo/FOText.java
index 20095d988..05e5a660a 100644
--- a/src/org/apache/fop/fo/FOText.java
+++ b/src/org/apache/fop/fo/FOText.java
@@ -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));
diff --git a/src/org/apache/fop/fo/FOTreeBuilder.java b/src/org/apache/fop/fo/FOTreeBuilder.java
index c719a2b35..75ac802f0 100644
--- a/src/org/apache/fop/fo/FOTreeBuilder.java
+++ b/src/org/apache/fop/fo/FOTreeBuilder.java
@@ -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 {
@@ -60,6 +69,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.
*
* @param namespaceURI namespace URI of formatting object element
@@ -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() {
diff --git a/src/org/apache/fop/fo/FObjMixed.java b/src/org/apache/fop/fo/FObjMixed.java
index d69c68179..ddab909cc 100644
--- a/src/org/apache/fop/fo/FObjMixed.java
+++ b/src/org/apache/fop/fo/FObjMixed.java
@@ -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 {
diff --git a/src/org/apache/fop/fo/flow/Block.java b/src/org/apache/fop/fo/flow/Block.java
index 8b85f806a..9830d1665 100644
--- a/src/org/apache/fop/fo/flow/Block.java
+++ b/src/org/apache/fop/fo/flow/Block.java
@@ -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() {
diff --git a/src/org/apache/fop/fo/pagination/PageSequence.java b/src/org/apache/fop/fo/pagination/PageSequence.java
index 0c9beaca2..0020836e8 100644
--- a/src/org/apache/fop/fo/pagination/PageSequence.java
+++ b/src/org/apache/fop/fo/pagination/PageSequence.java
@@ -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("");
}
diff --git a/src/org/apache/fop/layout/AreaTree.java b/src/org/apache/fop/layout/AreaTree.java
index 6f2bb864a..d5a290124 100644
--- a/src/org/apache/fop/layout/AreaTree.java
+++ b/src/org/apache/fop/layout/AreaTree.java
@@ -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);
+ }
+
}
diff --git a/src/org/apache/fop/layout/Page.java b/src/org/apache/fop/layout/Page.java
index c83a86bec..61600a19a 100644
--- a/src/org/apache/fop/layout/Page.java
+++ b/src/org/apache/fop/layout/Page.java
@@ -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) {
diff --git a/src/org/apache/fop/pdf/PDFDocument.java b/src/org/apache/fop/pdf/PDFDocument.java
index e26c59982..a1a3515ff 100644
--- a/src/org/apache/fop/pdf/PDFDocument.java
+++ b/src/org/apache/fop/pdf/PDFDocument.java
@@ -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
@@ -194,22 +236,6 @@ public class PDFDocument {
}
/**
- * 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
*
* @param theDomain Vector objects of Double objects.
@@ -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());
diff --git a/src/org/apache/fop/pdf/PDFPage.java b/src/org/apache/fop/pdf/PDFPage.java
index 4f9ae9d24..8fce26098 100644
--- a/src/org/apache/fop/pdf/PDFPage.java
+++ b/src/org/apache/fop/pdf/PDFPage.java
@@ -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 "
diff --git a/src/org/apache/fop/pdf/PDFPages.java b/src/org/apache/fop/pdf/PDFPages.java
index cef40f1d3..a3b5b60aa 100644
--- a/src/org/apache/fop/pdf/PDFPages.java
+++ b/src/org/apache/fop/pdf/PDFPages.java
@@ -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();
diff --git a/src/org/apache/fop/pdf/PDFRoot.java b/src/org/apache/fop/pdf/PDFRoot.java
index 1850fbf94..2bae2b91b 100644
--- a/src/org/apache/fop/pdf/PDFRoot.java
+++ b/src/org/apache/fop/pdf/PDFRoot.java
@@ -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()
diff --git a/src/org/apache/fop/render/PrintRenderer.java b/src/org/apache/fop/render/PrintRenderer.java
index 10c5eb7d0..6c67420e6 100644
--- a/src/org/apache/fop/render/PrintRenderer.java
+++ b/src/org/apache/fop/render/PrintRenderer.java
@@ -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.
@@ -122,15 +129,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
*
* @param x1 the start x location in millipoints
@@ -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).
+ */
+ {
+ }
}
diff --git a/src/org/apache/fop/render/Renderer.java b/src/org/apache/fop/render/Renderer.java
index 0365a8511..c28ffc923 100644
--- a/src/org/apache/fop/render/Renderer.java
+++ b/src/org/apache/fop/render/Renderer.java
@@ -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;
+
}
diff --git a/src/org/apache/fop/render/awt/AWTRenderer.java b/src/org/apache/fop/render/awt/AWTRenderer.java
index ef2ef9f63..57595eb47 100644
--- a/src/org/apache/fop/render/awt/AWTRenderer.java
+++ b/src/org/apache/fop/render/awt/AWTRenderer.java
@@ -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);
+ }
+
}
diff --git a/src/org/apache/fop/render/mif/MIFRenderer.java b/src/org/apache/fop/render/mif/MIFRenderer.java
index 778c992d1..567d59359 100644
--- a/src/org/apache/fop/render/mif/MIFRenderer.java
+++ b/src/org/apache/fop/render/mif/MIFRenderer.java
@@ -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 {
@@ -93,31 +99,6 @@ public class MIFRenderer implements Renderer {
}
/**
- * 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
*/
public void setupFontInfo(FontInfo 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);
+ }
}
+
diff --git a/src/org/apache/fop/render/pcl/PCLRenderer.java b/src/org/apache/fop/render/pcl/PCLRenderer.java
index d47da9a59..9ccd4f39f 100755
--- a/src/org/apache/fop/render/pcl/PCLRenderer.java
+++ b/src/org/apache/fop/render/pcl/PCLRenderer.java
@@ -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);
+ }
}
diff --git a/src/org/apache/fop/render/pdf/PDFRenderer.java b/src/org/apache/fop/render/pdf/PDFRenderer.java
index 7d9e27381..4554ff7e5 100644
--- a/src/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/org/apache/fop/render/pdf/PDFRenderer.java
@@ -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 {
diff --git a/src/org/apache/fop/render/ps/PSRenderer.java b/src/org/apache/fop/render/ps/PSRenderer.java
index 16ed11fac..bbbe0e748 100644
--- a/src/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/org/apache/fop/render/ps/PSRenderer.java
@@ -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);
+ }
}
diff --git a/src/org/apache/fop/render/txt/TXTRenderer.java b/src/org/apache/fop/render/txt/TXTRenderer.java
index eaff0eb88..11b9f31f9 100755
--- a/src/org/apache/fop/render/txt/TXTRenderer.java
+++ b/src/org/apache/fop/render/txt/TXTRenderer.java
@@ -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);
+ }
}
diff --git a/src/org/apache/fop/render/xml/XMLRenderer.java b/src/org/apache/fop/render/xml/XMLRenderer.java
index b249f523f..6aeca10fd 100644
--- a/src/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/org/apache/fop/render/xml/XMLRenderer.java
@@ -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");
+ }
}
diff --git a/src/org/apache/fop/tools/TestConverter.java b/src/org/apache/fop/tools/TestConverter.java
index 3f4538d32..0e91530c3 100644
--- a/src/org/apache/fop/tools/TestConverter.java
+++ b/src/org/apache/fop/tools/TestConverter.java
@@ -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) {