aboutsummaryrefslogtreecommitdiffstats
path: root/src/org/apache/fop/apps
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 /src/org/apache/fop/apps
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
Diffstat (limited to 'src/org/apache/fop/apps')
-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
5 files changed, 385 insertions, 96 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;
+ }
+ }
+}