diff options
author | Steve Coffman <gears@apache.org> | 2001-08-01 22:12:54 +0000 |
---|---|---|
committer | Steve Coffman <gears@apache.org> | 2001-08-01 22:12:54 +0000 |
commit | eeb2adff569057e2b6deb089ba9dffb21fb3a00b (patch) | |
tree | c8968573d60b496aa17befb1afbec840ddc1b355 /src/org/apache/fop/apps | |
parent | a5bc30d4a3977d60f0e70c2792bc4e3a37913a3d (diff) | |
download | xmlgraphics-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.java | 11 | ||||
-rw-r--r-- | src/org/apache/fop/apps/CommandLineStarter.java | 8 | ||||
-rw-r--r-- | src/org/apache/fop/apps/Driver.java | 52 | ||||
-rw-r--r-- | src/org/apache/fop/apps/PrintStarter.java | 114 | ||||
-rw-r--r-- | src/org/apache/fop/apps/StreamRenderer.java | 296 |
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; + } + } +} |