blob: 91d8d4e22ba39b484bc8d9ce7e816dd603f8f2c1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.area;
// Java
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.xml.sax.SAXException;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererEventProducer;
/**
* This uses the AreaTreeModel to store the pages
* Each page is either rendered if ready or prepared
* for later rendering.
* Once a page is rendered it is cleared to release the
* contents but the PageViewport is retained. So even
* though the pages are stored the contents are discarded.
*/
public class RenderPagesModel extends AreaTreeModel {
/**
* The renderer that will render the pages.
*/
protected Renderer renderer;
/**
* Pages that have been prepared but not rendered yet.
*/
protected List<PageViewport> prepared = new java.util.ArrayList<PageViewport>();
private List<OffDocumentItem> pendingODI = new java.util.ArrayList<OffDocumentItem>();
private List<OffDocumentItem> endDocODI = new java.util.ArrayList<OffDocumentItem>();
/**
* Create a new render pages model with the given renderer.
* @param userAgent FOUserAgent object for process
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @param fontInfo FontInfo object
* @param stream OutputStream
* @throws FOPException if the renderer cannot be properly initialized
*/
public RenderPagesModel(FOUserAgent userAgent, String outputFormat,
FontInfo fontInfo, OutputStream stream) throws FOPException {
super();
this.renderer = userAgent.getRendererFactory().createRenderer(
userAgent, outputFormat);
try {
renderer.setupFontInfo(fontInfo);
// check that the "any,normal,400" font exists
if (!fontInfo.isSetupValid()) {
throw new FOPException(
"No default font defined by OutputConverter");
}
renderer.startRenderer(stream);
} catch (IOException e) {
throw new FOPException(e);
}
}
@Override
public void setDocumentLocale(Locale locale) {
renderer.setDocumentLocale(locale);
}
/** {@inheritDoc} */
@Override
public void startPageSequence(PageSequence pageSequence) {
super.startPageSequence(pageSequence);
if (renderer.supportsOutOfOrder()) {
renderer.startPageSequence(getCurrentPageSequence());
}
}
/**
* Add a page to the render page model.
* If the page is finished it can be rendered immediately.
* If the page needs resolving then if the renderer supports
* out of order rendering it can prepare the page. Otherwise
* the page is added to a queue.
* @param page the page to add to the model
*/
@Override
public void addPage(PageViewport page) {
super.addPage(page);
// for links the renderer needs to prepare the page
// it is more appropriate to do this after queued pages but
// it will mean that the renderer has not prepared a page that
// could be referenced
boolean ready = renderer.supportsOutOfOrder() && page.isResolved();
if (ready) {
if (!renderer.supportsOutOfOrder() && page.getPageSequence().isFirstPage(page)) {
renderer.startPageSequence(getCurrentPageSequence());
}
try {
renderer.renderPage(page);
} catch (RuntimeException re) {
String err = "Error while rendering page " + page.getPageNumberString();
log.error(err, re);
throw re;
} catch (IOException ioe) {
RendererEventProducer eventProducer = RendererEventProducer.Provider.get(
renderer.getUserAgent().getEventBroadcaster());
eventProducer.ioError(this, ioe);
} catch (FOPException e) {
//TODO use error handler to handle this FOPException or propagate exception
String err = "Error while rendering page " + page.getPageNumberString();
log.error(err, e);
throw new IllegalStateException("Fatal error occurred. Cannot continue. "
+ e.getClass().getName() + ": " + err);
}
page.clear();
} else {
preparePage(page);
}
// check prepared pages
boolean cont = checkPreparedPages(page, false);
if (cont) {
processOffDocumentItems(pendingODI);
pendingODI.clear();
}
}
/**
* Check prepared pages
*
* @param newPageViewport the new page being added
* @param renderUnresolved render pages with unresolved idref's
* (done at end-of-document processing)
* @return true if the current page should be rendered
* false if the renderer doesn't support out of order
* rendering and there are pending pages
*/
protected boolean checkPreparedPages(PageViewport newPageViewport,
boolean renderUnresolved) {
for (Iterator iter = prepared.iterator(); iter.hasNext();) {
PageViewport pageViewport = (PageViewport)iter.next();
if (pageViewport.isResolved() || renderUnresolved) {
if (!renderer.supportsOutOfOrder()
&& pageViewport.getPageSequence().isFirstPage(pageViewport)) {
renderer.startPageSequence(pageViewport.getPageSequence());
}
renderPage(pageViewport);
pageViewport.clear();
iter.remove();
} else {
// if keeping order then stop at first page not resolved
if (!renderer.supportsOutOfOrder()) {
break;
}
}
}
return renderer.supportsOutOfOrder() || prepared.isEmpty();
}
/**
* Renders the given page and notified about unresolved IDs if any.
* @param pageViewport the page to be rendered.
*/
protected void renderPage(PageViewport pageViewport) {
try {
renderer.renderPage(pageViewport);
} catch (Exception e) {
AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
renderer.getUserAgent().getEventBroadcaster());
eventProducer.pageRenderingError(this,
pageViewport.getPageNumberString(), e);
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
}
}
/**
* Prepare a page.
* An unresolved page can be prepared if the renderer supports
* it and the page will be rendered later.
* @param page the page to prepare
*/
protected void preparePage(PageViewport page) {
if (renderer.supportsOutOfOrder()) {
renderer.preparePage(page);
}
prepared.add(page);
}
/** {@inheritDoc} */
@Override
public void handleOffDocumentItem(OffDocumentItem oDI) {
switch(oDI.getWhenToProcess()) {
case OffDocumentItem.IMMEDIATELY:
renderer.processOffDocumentItem(oDI);
break;
case OffDocumentItem.AFTER_PAGE:
pendingODI.add(oDI);
break;
case OffDocumentItem.END_OF_DOC:
endDocODI.add(oDI);
break;
default:
throw new RuntimeException();
}
}
private void processOffDocumentItems(List<OffDocumentItem> list) {
for (OffDocumentItem oDI : list) {
renderer.processOffDocumentItem(oDI);
}
}
/**
* End the document. Render any end document OffDocumentItems
* {@inheritDoc}
*/
@Override
public void endDocument() throws SAXException {
// render any pages that had unresolved ids
checkPreparedPages(null, true);
processOffDocumentItems(pendingODI);
pendingODI.clear();
processOffDocumentItems(endDocODI);
try {
renderer.stopRenderer();
} catch (IOException ex) {
throw new SAXException(ex);
}
}
}