From eeb2adff569057e2b6deb089ba9dffb21fb3a00b Mon Sep 17 00:00:00 2001 From: Steve Coffman Date: Wed, 1 Aug 2001 22:12:54 +0000 Subject: [PATCH] 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 --- src/org/apache/fop/apps/AWTStarter.java | 11 +- .../apache/fop/apps/CommandLineStarter.java | 8 +- src/org/apache/fop/apps/Driver.java | 52 ++- src/org/apache/fop/apps/PrintStarter.java | 114 ++++--- src/org/apache/fop/apps/StreamRenderer.java | 296 ++++++++++++++++++ .../apache/fop/datatypes/IDReferences.java | 14 +- src/org/apache/fop/fo/FONode.java | 10 +- src/org/apache/fop/fo/FOText.java | 20 +- src/org/apache/fop/fo/FOTreeBuilder.java | 56 +++- src/org/apache/fop/fo/FObjMixed.java | 2 +- src/org/apache/fop/fo/flow/Block.java | 54 +++- .../fop/fo/pagination/PageSequence.java | 10 +- src/org/apache/fop/layout/AreaTree.java | 108 +++---- src/org/apache/fop/layout/Page.java | 23 +- src/org/apache/fop/pdf/PDFDocument.java | 221 ++++++++----- src/org/apache/fop/pdf/PDFPage.java | 14 +- src/org/apache/fop/pdf/PDFPages.java | 16 +- src/org/apache/fop/pdf/PDFRoot.java | 32 +- src/org/apache/fop/render/PrintRenderer.java | 31 +- src/org/apache/fop/render/Renderer.java | 11 +- .../apache/fop/render/awt/AWTRenderer.java | 81 +++-- .../apache/fop/render/mif/MIFRenderer.java | 57 ++-- .../apache/fop/render/pcl/PCLRenderer.java | 79 +++-- .../apache/fop/render/pdf/PDFRenderer.java | 79 ++--- src/org/apache/fop/render/ps/PSRenderer.java | 90 +++--- .../apache/fop/render/txt/TXTRenderer.java | 69 ++-- .../apache/fop/render/xml/XMLRenderer.java | 57 ++-- src/org/apache/fop/tools/TestConverter.java | 9 +- 28 files changed, 1095 insertions(+), 529 deletions(-) create mode 100644 src/org/apache/fop/apps/StreamRenderer.java 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). *

- * 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. *

* A third possibility may be used to build the FO Tree, namely @@ -80,9 +81,7 @@ import java.util.*; *

  * 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]));
  * 
*/ 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.

+ + StreamRenderer is created by Driver and called from + FOTreeBuilder when a PageSequence is created, + and AreaTree when a Page is formatted.

+*/ +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. Note 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.

+ */ + 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 { @@ -59,6 +68,22 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { */ protected Hashtable unknownFOs = new Hashtable(); + /** + * + * The class that handles formatting and rendering to a stream + * (mark-fop@inomial.com) + */ + private StreamRenderer streamRenderer; + + public FOTreeBuilder() + { + } + + public void setStreamRenderer(StreamRenderer streamRenderer) + { + this.streamRenderer = streamRenderer; + } + /** * add a mapping from element name to maker. * @@ -120,19 +145,39 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { /** * SAX Handler for the end of an element */ - public void endElement(String uri, String localName, String rawName) { + public void endElement(String uri, String localName, String rawName) + throws SAXException + { currentFObj.end(); - currentFObj = (FObj)currentFObj.getParent(); + + // + // mark-fop@inomial.com - tell the stream renderer to render + // this page-sequence + // + if(currentFObj instanceof PageSequence) + streamRenderer.render((PageSequence) currentFObj); + + currentFObj = (FObj)currentFObj.getParent(); } /** * SAX Handler for the start of the document */ - public void startDocument() { + public void startDocument() + throws SAXException + { rootFObj = null; // allows FOTreeBuilder to be reused MessageHandler.logln("building formatting object tree"); + streamRenderer.startRenderer(); } + public void endDocument() + throws SAXException + { + MessageHandler.logln("Parsing of document complete, stopping renderer"); + streamRenderer.stopRenderer(); + } + /** * SAX Handler for the start of an element */ @@ -182,7 +227,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { + " be root, not " + fobj.getName())); } - } else { + } else if(!(fobj instanceof org.apache.fop.fo.pagination.PageSequence)){ currentFObj.addChild(fobj); } @@ -203,6 +248,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { public void reset() { currentFObj = null; rootFObj = null; + streamRenderer = null; } public boolean hasData() { 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..?

+ + 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

+ * + * The constructor creates a /Root and /Pages object to + * track the document but does not write these objects until + * the trailer is written. Note that the object ID of the + * pages object is determined now, and the xref table is + * updated later. This allows Pages to refer to their + * Parent before we write it out. This took me a long + * time to work out, and is so obvious now. Sigh. + * mark-fop@inomial.com. Maybe I should do a PDF course. */ public PDFDocument() { /* create the /Root, /Info and /Resources objects */ - this.root = makeRoot(); - this.info = makeInfo(); + this.pages = makePages(); + + // Create the Root object + this.root = makeRoot(pages); + + // Create the Resources object this.resources = makeResources(); + + // Make the /Info record + this.info = makeInfo(); } /** @@ -149,30 +181,40 @@ public class PDFDocument { } /** - * make /Root object as next object - * - * @return the created /Root object + * Make a /Catalog (Root) object. This object is written in + * the trailer. */ - protected PDFRoot makeRoot() { + public PDFRoot makeRoot(PDFPages pages) + { - /* - * create a PDFRoot with the next object number and add to - * list of objects - */ - PDFRoot pdfRoot = new PDFRoot(++this.objectcount); - this.objects.addElement(pdfRoot); - - /* - * create a new /Pages object to be root of Pages hierarchy - * and add to list of objects + /* + * Make a /Pages object. This object is written in the trailer. */ - PDFPages rootPages = new PDFPages(++this.objectcount); - this.objects.addElement(rootPages); + PDFRoot pdfRoot = new PDFRoot(++this.objectcount, pages); + addTrailerObject(pdfRoot); + return pdfRoot; + } + + /** + * Make a /Pages object. This object is written in the trailer. + */ + + public PDFPages makePages() + { + PDFPages pdfPages = new PDFPages(++this.objectcount); + addTrailerObject(pdfPages); + return pdfPages; + } - /* inform the /Root object of the /Pages root */ - pdfRoot.setRootPages(rootPages); - return pdfRoot; - } + /** + * Make a /Resources object. This object is written in the trailer. + */ + public PDFResources makeResources() + { + PDFResources pdfResources = new PDFResources(++this.objectcount); + addTrailerObject(pdfResources); + return pdfResources; + } /** * make an /Info object @@ -193,22 +235,6 @@ public class PDFDocument { return pdfInfo; } - /** - * make a /Resources object - * - * @return the created /Resources object - */ - private PDFResources makeResources() { - - /* - * create a PDFResources with the next object number and add - * to list of objects - */ - PDFResources pdfResources = new PDFResources(++this.objectcount); - this.objects.addElement(pdfResources); - return pdfResources; - } - /** * Make a Type 0 sampled function * @@ -973,18 +999,32 @@ public class PDFDocument { goToReference = idReferences.createInternalLinkGoTo(destination, ++this.objectcount); - this.objects.addElement(idReferences.getPDFGoTo(destination)); + addTrailerObject(idReferences.getPDFGoTo(destination)); } } else { // id was not found, so create it idReferences.createNewId(destination); idReferences.addToIdValidationList(destination); goToReference = idReferences.createInternalLinkGoTo(destination, ++this.objectcount); - this.objects.addElement(idReferences.getPDFGoTo(destination)); + addTrailerObject(idReferences.getPDFGoTo(destination)); } return goToReference; } - + + public void addTrailerObject(PDFObject object) + { + this.trailerObjects.addElement(object); + } + + /** + Ensure there is room in the locations xref for the number of + objects that have been created. + */ + private void prepareLocations() + { + while(location.size() < objectcount) + location.addElement(locationPlaceholder); + } /** * make a stream object @@ -1023,14 +1063,18 @@ public class PDFDocument { } /** - * Make the root Outlines object + * Get the root Outlines object. This method does not write + * the outline to the PDF document, it simply creates a + * reference for later. */ - public PDFOutline makeOutlineRoot() { - PDFOutline obj = new PDFOutline(++this.objectcount, null, null); - this.objects.addElement(obj); - root.setRootOutline(obj); - - return obj; + public PDFOutline getOutlineRoot() { + if(outlineRoot != null) + return outlineRoot; + + outlineRoot = new PDFOutline(++this.objectcount, null, null); + addTrailerObject(outlineRoot); + root.setRootOutline(outlineRoot); + return outlineRoot; } /** @@ -1044,7 +1088,7 @@ public class PDFDocument { String goToRef = getGoToReference(destination); PDFOutline obj = new PDFOutline(++this.objectcount, label, goToRef); - // System.out.println("created new outline object"); + System.out.println("created new outline object"); if (parent != null) { parent.addOutline(obj); @@ -1068,15 +1112,10 @@ public class PDFDocument { * * @param writer the OutputStream to output the document to */ - public void output(OutputStream stream) throws IOException { - - /* - * output the header and increment the character position by - * the header's length - */ - this.position += outputHeader(stream); + public void output(OutputStream stream) throws IOException + { - this.resources.setXObjects(xObjects); + prepareLocations(); Enumeration en = this.objects.elements(); while (en.hasMoreElements()) { @@ -1087,7 +1126,8 @@ public class PDFDocument { * add the position of this object to the list of object * locations */ - this.location.addElement(new Integer(this.position)); + location.setElementAt( + new Integer(this.position),object.getNumber() - 1); /* * output the object and increment the character position @@ -1096,28 +1136,26 @@ public class PDFDocument { this.position += object.output(stream); } - /* - * output the xref table and increment the character position - * by the table's length - */ - this.position += outputXref(stream); - - /* output the trailer and flush the Stream */ - outputTrailer(stream); - stream.flush(); + this.objects.clear(); } /** - * write the PDF header + * write the PDF header

+ * + * 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. @@ -121,15 +128,6 @@ public abstract class PrintRenderer implements Renderer { */ public abstract void setProducer(String producer); - /** - * render the areas - * - * @param areaTree the laid-out area tree - * @param stream the OutputStream to write to - */ - public abstract void render(AreaTree areaTree, OutputStream stream) - throws IOException, FOPException; - /** * add a line to the current stream * @@ -596,5 +594,20 @@ public abstract class PrintRenderer implements Renderer { this.fontInfo = fontInfo; FontSetup.setup(fontInfo); } + + /** + Default start renderer method. This would + normally be overridden. (mark-fop@inomial.com). + */ + public void startRenderer(OutputStream outputStream) + throws IOException + { + } + /** + Default stop renderer method. This would + normally be overridden. (mark-fop@inomial.com). + */ + { + } } 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 { @@ -92,31 +98,6 @@ public class MIFRenderer implements Renderer { this.options = options; } - /** - * render the areas into MIF - * - * @param areaTree the laid-out area tree - * @param writer the PrintWriter to write the MIF with - */ - - public void render(AreaTree areaTree, - OutputStream stream) throws IOException, FOPException { - - MessageHandler.logln("rendering areas to MIF"); - // idReferences=areaTree.getIDReferences(); - // this.pdfResources = this.pdfDoc.getResources(); - // this.pdfDoc.setIDReferences(idReferences); - Enumeration e = areaTree.getPages().elements(); - while (e.hasMoreElements()) { - this.renderPage((Page)e.nextElement()); - } - - // MessageHandler.logln("writing out MIF"); - - this.mifDoc.output(stream); - stream.close(); - } - /** * set up the given FontInfo */ @@ -496,4 +477,30 @@ public class MIFRenderer implements Renderer { */ public void renderLeaderArea(LeaderArea area) {} + /** + Default start renderer method. This would + normally be overridden. (mark-fop@inomial.com). + */ + public void startRenderer(OutputStream outputStream) + throws IOException + { + MessageHandler.logln("rendering areas to MIF"); + } + + /** + Default stop renderer method. This would + normally be overridden. (mark-fop@inomial.com) + */ + public void stopRenderer(OutputStream outputStream) + throws IOException + { + MessageHandler.logln("writing out MIF"); + this.mifDoc.output(outputStream); + outputStream.flush(); + } + + public void render(Page page, OutputStream outputStream) { + this.renderPage(page); + } } + 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.

+ * + * 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("\n\n"); - writeStartTag(""); - Enumeration e = areaTree.getPages().elements(); - while (e.hasMoreElements()) { - this.renderPage((Page)e.nextElement()); - } - writeEndTag(""); - 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( "\n\n"); + writeStartTag(""); + } + + /** + Default stop renderer method. This would + normally be overridden. (mark-fop@inomial.com). + */ + public void stopRenderer(OutputStream outputStream) + throws IOException + { + writeEndTag(""); + 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) { -- 2.39.5