* 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.*;
// 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();
/**
* 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 {
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) {
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;
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
* 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
* <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 {
* 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);
/**
* 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();
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
*/
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();
}
}
* 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.
*/
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;
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) {
}
- 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) {
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;
return vec;
}
+ /* TODO: I'm totally not sure that this is necessary -Mark
void setCopies(int val) {
copies = val;
Vector copie = tree.getPages();
}
}
-
+ */
} // class PrintRenderer
} // class PrintCommandLine
--- /dev/null
+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;
+ }
+ }
+}
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;
node.setPosition(x, y);
}
+ public Enumeration getInvalidElements()
+ {
+ return idValidation.keys();
+ }
}
/**
* 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 {
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
/**
* 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;
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);
System.out.println("abnormal exit");
System.exit(0);
}
+ */
}
public void setUnderlined(boolean ul) {
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
}
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));
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;
/**
* 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 {
*/
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.
*
/**
* 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
*/
+ " be root, not "
+ fobj.getName()));
}
- } else {
+ } else if(!(fobj instanceof org.apache.fop.fo.pagination.PageSequence)){
currentFObj.addChild(fobj);
}
public void reset() {
currentFObj = null;
rootFObj = null;
+ streamRenderer = null;
}
public boolean hasData() {
}
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 {
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 {
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;
}
public Status layout(Area area) throws FOPException {
+ BlockArea blockArea;
+
// MessageHandler.error(" b:LAY[" + marker + "] ");
}
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());
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() {
* 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;
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());
* 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();
// handle the 'force-page-count'
forcePage(areaTree, firstAvailPageNumber);
+ currentPage = null;
+
MessageHandler.logln("");
}
// 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;
import java.util.Stack;
import java.util.Vector;
+/*
+ * Modified by Mark Lillywhite, mark-fop@inomial.com. No longer keeps
+ a list of pages in the tree, instead these are passed to the
+ StreamRenderer. No longer keeps it's own list of IDReferences;
+ these are handled by StreamRenderer. In many ways StreamRenderer
+ has taken over from AreaTree and possibly this might be a better
+ place to deal with things in the future..?<P>
+
+ Any extensions added to the AreaTree while generating a page
+ are given to the Page for the renderer to deal with.
+ */
+
public class AreaTree {
/**
*/
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;
}
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);
+ }
+
}
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;
private AreaContainer end;
private AreaTree areaTree;
-
+
+ private Vector rootExtensions;
+
private PageSequence pageSequence;
protected int pageNumber = 0;
markers = new Vector();
}
+ public IDReferences getIDReferences()
+ {
+ return areaTree.getIDReferences();
+ }
+
public void setPageSequence(PageSequence pageSequence) {
this.pageSequence = pageSequence;
}
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) {
* 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
*/
*/
protected Vector location = new Vector();
+ /** List of objects to write in the trailer */
+ private Vector trailerObjects = new Vector();
+
/**
* the counter for object numbering
*/
*/
protected PDFRoot root;
+ /** The root outline object */
+ private PDFOutline outlineRoot = null;
+
+ /** The /Pages object (mark-fop@inomial.com) */
+ private PDFPages pages;
+
/**
* the /Info object
*/
protected Hashtable xObjectsMap = new Hashtable();
/**
- * creates an empty PDF document
+ * creates an empty PDF document <p>
+ *
+ * The constructor creates a /Root and /Pages object to
+ * track the document but does not write these objects until
+ * the trailer is written. Note that the object ID of the
+ * pages object is determined now, and the xref table is
+ * updated later. This allows Pages to refer to their
+ * Parent before we write it out. This took me a long
+ * time to work out, and is so obvious now. Sigh.
+ * mark-fop@inomial.com. Maybe I should do a PDF course.
*/
public PDFDocument() {
/* create the /Root, /Info and /Resources objects */
- this.root = makeRoot();
- this.info = makeInfo();
+ this.pages = makePages();
+
+ // Create the Root object
+ this.root = makeRoot(pages);
+
+ // Create the Resources object
this.resources = makeResources();
+
+ // Make the /Info record
+ this.info = makeInfo();
}
/**
}
/**
- * 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
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
*
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
}
/**
- * 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;
}
/**
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);
*
* @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()) {
* 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
this.position += object.output(stream);
}
- /*
- * output the xref table and increment the character position
- * by the table's length
- */
- this.position += outputXref(stream);
-
- /* output the trailer and flush the Stream */
- outputTrailer(stream);
- stream.flush();
+ this.objects.clear();
}
/**
- * write the PDF header
+ * write the PDF header <P>
+ *
+ * This method must be called prior to formatting
+ * and outputting AreaTrees.
*
* @param stream the OutputStream to write the header to
* @return the number of bytes written
*/
- protected int outputHeader(OutputStream stream) throws IOException {
- int length = 0;
+ public void outputHeader(OutputStream stream)
+ throws IOException
+ {
+ this.position=0;
+
byte[] pdf = ("%PDF-" + this.pdfVersion + "\n").getBytes();
stream.write(pdf);
- length += pdf.length;
+ this.position += pdf.length;
// output a binary comment as recommended by the PDF spec (3.4.1)
byte[] bin = {
(byte)'\n'
};
stream.write(bin);
- length += bin.length;
+ this.position += bin.length;
- return length;
+ this.resources.setXObjects(xObjects);
}
/**
*
* @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());
* 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
* @param parent the /Pages object that is this page's parent
*/
public void setParent(PDFPages parent) {
- this.parent = parent;
+ this.parent = parent.referencePDF();
}
/**
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 "
// 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);
}
* @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();
}
*
* @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();
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;
}
/**
_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()
/**
* 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.
*/
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
*
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).
+ */
+ {
+ }
}
/**
* 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
*/
public void renderLeaderArea(LeaderArea area);
+ public void startRenderer(OutputStream outputStream)
+ throws IOException;
+
+ public void stopRenderer(OutputStream outputStream)
+ throws IOException;
+
}
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;
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.
* @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);
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 = "";
}
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;
}
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();
public void registerExtension(BridgeExtension be) {}
}
+
+ public void startRenderer(OutputStream outputStream)
+ throws IOException
+ {
+ }
+
+ public void stopRenderer(OutputStream outputStream)
+ throws IOException
+ {
+ render(0);
+ }
+
}
/**
* 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 {
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
*/
*/
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);
+ }
}
+
* 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 {
* @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
*
* }
*/
}
+ 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);
+ }
}
/**
* 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 {
*/
int prevWordWidth = 0;
- private PDFOutline rootOutline;
-
/**
* reusable word area string buffer to reduce memory usage
*/
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
*
}
+
/**
* Convert a char to a multibyte hex representation
*/
}
}
+ 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
*
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()) {
}
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 {
* DocumentProcessColors stuff (probably needs to be configurable, then maybe
* add a color to grayscale conversion for bitmaps to make output smaller (See
* PCLRenderer), font embedding, support different character encodings, try to
- * implement image transparency, positioning of images is wrong etc.
+ * implement image transparency, positioning of images is wrong etc. <P>
+ *
+ * Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
+ * Renderer interface. This PostScript renderer appears to be the
+ * most efficient at producing output.
*
* @author Jeremias Märki
*/
private float currGreen;
private float currBlue;
+ private FontInfo fontInfo;
+
protected Hashtable options;
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
*/
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;
}
/**
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);
+ }
}
/**
* 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 {
"\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
*/
*/
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
* }
*/
}
+ 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);
+ }
}
/**
* 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 {
this.producer = producer;
}
- /**
- * render the areas into XML
- *
- * @param areaTree the laid-out area tree
- * @param stream the OutputStream to give the XML to
- */
- public void render(AreaTree areaTree,
- OutputStream stream) throws IOException {
- MessageHandler.logln("rendering areas to XML");
- this.writer = new PrintWriter(stream);
- this.writer.write("<?xml version=\"1.0\"?>\n<!-- produced by "
- + this.producer + " -->\n");
- writeStartTag("<AreaTree>");
- Enumeration e = areaTree.getPages().elements();
- while (e.hasMoreElements()) {
- this.renderPage((Page)e.nextElement());
- }
- writeEndTag("</AreaTree>");
- this.writer.flush();
- MessageHandler.errorln("written out XML");
- }
+
+ public void render(Page page, OutputStream outputStream)
+ throws IOException
+ {
+ this.renderPage(page);
+ }
/**
* write out spaces to make indent
private boolean isCoarseXml() {
return ((Boolean)options.get("fineDetail")).booleanValue();
}
-
+
+ /**
+ Default start renderer method. This would
+ normally be overridden. (mark-fop@inomial.com).
+ */
+ public void startRenderer(OutputStream outputStream)
+ throws IOException
+ {
+ MessageHandler.logln("rendering areas to XML");
+ this.writer = new PrintWriter(outputStream);
+ this.writer.write( "<?xml version=\"1.0\"?>\n<!-- produced by " +
+ this.producer + " -->\n");
+ writeStartTag("<AreaTree>");
+ }
+
+ /**
+ Default stop renderer method. This would
+ normally be overridden. (mark-fop@inomial.com).
+ */
+ public void stopRenderer(OutputStream outputStream)
+ throws IOException
+ {
+ writeEndTag("</AreaTree>");
+ this.writer.flush();
+ MessageHandler.errorln("written out XML");
+ }
}
* 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;
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);
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) {