Package org.apache.fop.area

Source Code of org.apache.fop.area.AreaTreeHandler

/*
* Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed 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: AreaTreeHandler.java 389086 2006-03-27 09:51:14Z jeremias $ */

package org.apache.fop.area;

// Java
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;

// XML
import org.xml.sax.SAXException;

// Apache
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FormattingResults;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.bookmarks.BookmarkTree;
import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
import org.apache.fop.layoutmgr.LayoutManagerMaker;
import org.apache.fop.layoutmgr.LayoutManagerMapping;

/**
* Area tree handler for formatting objects.
*
* Concepts:
* The area tree is to be as small as possible. With minimal classes
* and data to fully represent an area tree for formatting objects.
* The area tree needs to be simple to render and follow the spec
* closely.
* This area tree has the concept of page sequences.
* Wherever possible information is discarded or optimized to
* keep memory use low. The data is also organized to make it
* possible for renderers to minimize their output.
* A page can be saved if not fully resolved and once rendered
* a page contains only size and id reference information.
* The area tree pages are organized in a model that depends on the
* type of renderer.
*/
public class AreaTreeHandler extends FOEventHandler {

    // show statistics after document complete?
    private boolean outputStatistics;

    // for statistics gathering
    private Runtime runtime;

    // heap memory allocated (for statistics)
    private long initialMemory;

    // time used in rendering (for statistics)
    private long startTime;

    // the LayoutManager maker
    private LayoutManagerMaker lmMaker;

    /** AreaTreeModel in use */
    protected AreaTreeModel model;

    // The fo:root node of the document
    private Root rootFObj;

    // HashMap of ID's whose area is located on one or more consecutive
    // PageViewports.  Each ID has an arraylist of PageViewports that
    // form the defined area of this ID
    private Map idLocations = new HashMap();

    // idref's whose target PageViewports have yet to be identified
    // Each idref has a HashSet of Resolvable objects containing that idref
    private Map unresolvedIDRefs = new HashMap();

     // The formatting results to be handed back to the caller.
    private FormattingResults results = new FormattingResults();

    private PageSequenceLayoutManager prevPageSeqLM;

    private static Log log = LogFactory.getLog(AreaTreeHandler.class);

    /**
     * Constructor.
     * @param userAgent FOUserAgent object for process
     * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
     * @param stream OutputStream
     * @throws FOPException if the RenderPagesModel cannot be created
     */
    public AreaTreeHandler (FOUserAgent userAgent, String outputFormat,
                OutputStream stream) throws FOPException {
        super(userAgent);

        setupModel(userAgent, outputFormat, stream);
           
        lmMaker = userAgent.getFactory().getLayoutManagerMakerOverride();
        if (lmMaker == null) {
            lmMaker = new LayoutManagerMapping();
        }

        outputStatistics = log.isDebugEnabled();

        if (outputStatistics) {
            runtime = Runtime.getRuntime();
        }
    }

    /**
     * Sets up the AreaTreeModel instance for use by the AreaTreeHandler.
     * @param userAgent FOUserAgent object for process
     * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
     * @param stream OutputStream
     * @throws FOPException if the RenderPagesModel cannot be created
     */
    protected void setupModel(FOUserAgent userAgent, String outputFormat,
            OutputStream stream) throws FOPException {
        model = new RenderPagesModel(userAgent, outputFormat, fontInfo,
                stream);
    }
   
    /**
     * Get the area tree model for this area tree.
     *
     * @return AreaTreeModel the model being used for this area tree
     */
    public AreaTreeModel getAreaTreeModel() {
        return model;
    }

    /**
     * Get the LayoutManager maker for this area tree.
     *
     * @return LayoutManagerMaker the LayoutManager maker being used for this area tree
     */
    public LayoutManagerMaker getLayoutManagerMaker() {
        return lmMaker;
    }

    /**
     * Tie a PageViewport with an ID found on a child area of the PV.
     * Note that an area with a given ID may be on more than one PV, hence
     * an ID may have more than one PV associated with it.
     * @param id the property ID of the area
     * @param pv a page viewport that contains the area with this ID
     */
    public void associateIDWithPageViewport(String id, PageViewport pv) {
        if (log.isDebugEnabled()) {
            log.debug("associateIDWithPageViewport(" + id + ", " + pv + ")");
        }
        List pvList = (List) idLocations.get(id);
        if (pvList == null) { // first time ID located
            pvList = new ArrayList();
            idLocations.put(id, pvList);
            pvList.add(pv);

            /*
             * See if this ID is in the unresolved idref list, if so
             * resolve Resolvable objects tied to it.
             */
            tryIDResolution(id, pv, pvList);
        } else {
            pvList.add(pv);
        }
    }

    /**
     * Tries to resolve all unresolved ID references on the given page.
     * @param id ID to resolve
     * @param pv page viewport whose ID refs to resolve
     * @param List of PageViewports
     */
    private void tryIDResolution(String id, PageViewport pv, List pvList) {
        Set todo = (Set) unresolvedIDRefs.get(id);
        if (todo != null) {
            for (Iterator iter = todo.iterator(); iter.hasNext();) {
                Resolvable res = (Resolvable) iter.next();
                res.resolveIDRef(id, pvList);
            }
            unresolvedIDRefs.remove(id);
        }
    }

    /**
     * Tries to resolve all unresolved ID references on the given page.
     * @param pv page viewport whose ID refs to resolve
     */
    public void tryIDResolution(PageViewport pv) {
        String[] ids = pv.getIDRefs();
        if (ids != null) {
            for (int i = 0; i < ids.length; i++) {
                List pvList = (List) idLocations.get(ids[i]);
                if (pvList != null) {
                    tryIDResolution(ids[i], pv, pvList);
                }
            }
        }
    }
   
    /**
     * Get the list of page viewports that have an area with a given id.
     * @param id the id to lookup
     * @return the list of PageViewports
     */
    public List getPageViewportsContainingID(String id) {
        return (List) idLocations.get(id);
    }

    /**
     * Get information about the rendered output, like
     * number of pages created.
     * @return the results structure
     */
    public FormattingResults getResults() {
        return this.results;
    }

    /**
     * Add an Resolvable object with an unresolved idref
     * @param idref the idref whose target id has not yet been located
     * @param res the Resolvable object needing the idref to be resolved
     */
    public void addUnresolvedIDRef(String idref, Resolvable res) {
        Set todo = (Set) unresolvedIDRefs.get(idref);
        if (todo == null) {
            todo = new HashSet();
            unresolvedIDRefs.put(idref, todo);
        }
        // add Resolvable object to this HashSet
        todo.add(res);
    }

    /**
     * Prepare AreaTreeHandler for document processing
     * This is called from FOTreeBuilder.startDocument()
     *
     * @throws SAXException if there is an error
     */
    public void startDocument() throws SAXException {
        //Initialize statistics
        if (outputStatistics) {
            initialMemory = runtime.totalMemory() - runtime.freeMemory();
            startTime = System.currentTimeMillis();
        }
    }

    /**
     * finish the previous pageSequence
     */
    private void finishPrevPageSequence(Numeric initialPageNumber) {
        if (prevPageSeqLM != null) {
            prevPageSeqLM.doForcePageCount(initialPageNumber);
            prevPageSeqLM.finishPageSequence();
            prevPageSeqLM = null;
        }
    }

    /** @see org.apache.fop.fo.FOEventHandler */
    public void startPageSequence(PageSequence pageSequence) {
        rootFObj = pageSequence.getRoot();
        finishPrevPageSequence(pageSequence.getInitialPageNumber());
        pageSequence.initPageNumber();
        //extension attachments from fo:root
        wrapAndAddExtensionAttachments(rootFObj.getExtensionAttachments());
        //extension attachments from fo:declarations
        if (rootFObj.getDeclarations() != null) {
            wrapAndAddExtensionAttachments(rootFObj.getDeclarations().getExtensionAttachments());
        }
    }
   
    private void wrapAndAddExtensionAttachments(List list) {
        Iterator i = list.iterator();
        while (i.hasNext()) {
            ExtensionAttachment attachment = (ExtensionAttachment)i.next();
            addOffDocumentItem(new OffDocumentExtensionAttachment(attachment));
        }
    }
   
    /**
     * End the PageSequence.
     * The PageSequence formats Pages and adds them to the AreaTree.
     * The area tree then handles what happens with the pages.
     *
     * @param pageSequence the page sequence ending
     */
    public void endPageSequence(PageSequence pageSequence) {

        if (outputStatistics) {
            long memoryNow = runtime.totalMemory() - runtime.freeMemory();
            log.debug("Current heap size: " + (memoryNow / 1024L) + "Kb");
        }

        // If no main flow, nothing to layout!
        if (pageSequence.getMainFlow() != null) {
            PageSequenceLayoutManager pageSLM;
            pageSLM = getLayoutManagerMaker().makePageSequenceLayoutManager(
                    this, pageSequence);
            pageSLM.activateLayout();
            // preserve the current PageSequenceLayoutManger for the
            // force-page-count check at the beginning of the next PageSequence
            prevPageSeqLM = pageSLM;
        }
    }

    /**
     * Called by the PageSequenceLayoutManager when it is finished with a page-sequence.
     * @param pageSequence the page-sequence just finished
     * @param pageCount The number of pages generated for the page-sequence
     */
    public void notifyPageSequenceFinished(PageSequence pageSequence,
                                           int pageCount) {
        this.results.haveFormattedPageSequence(pageSequence,
                                               pageCount);
        if (log.isDebugEnabled()) {
            log.debug("Last page-sequence produced "
                    + pageCount + " pages.");
        }
    }

    /**
     * End the document.
     *
     * @throws SAXException if there is some error
     */
    public void endDocument() throws SAXException {

        finishPrevPageSequence(null);
        // process fo:bookmark-tree
        BookmarkTree bookmarkTree = rootFObj.getBookmarkTree();
        if (bookmarkTree != null) {
            BookmarkData data = new BookmarkData(bookmarkTree);
            addOffDocumentItem(data);
        }

        model.endDocument();

        if (outputStatistics) {
            long memoryNow = runtime.totalMemory() - runtime.freeMemory();
            long memoryUsed = (memoryNow - initialMemory) / 1024L;
            long timeUsed = System.currentTimeMillis() - startTime;
            int pageCount = rootFObj.getTotalPagesGenerated();
            log.debug("Initial heap size: " + (initialMemory / 1024L) + "Kb");
            log.debug("Current heap size: " + (memoryNow / 1024L) + "Kb");
            log.debug("Total memory used: " + memoryUsed + "Kb");
            log.debug("Total time used: " + timeUsed + "ms");
            log.debug("Pages rendered: " + pageCount);
            if (pageCount > 0) {
                long perPage = (timeUsed / pageCount);
                long ppm = (timeUsed != 0 ? Math.round(60000 * pageCount / (double)timeUsed) : -1);
                log.debug("Avg render time: " + perPage + "ms/page (" + ppm + "pages/min)");
            }
        }
    }

    /**
     * Add a OffDocumentItem to the area tree model
     * This checks if the OffDocumentItem is resolvable and attempts
     * to resolve or add the resolvable ids for later resolution.
     * @param odi the OffDocumentItem to add.
     */
    private void addOffDocumentItem(OffDocumentItem odi) {
        if (odi instanceof Resolvable) {
            Resolvable res = (Resolvable) odi;
            String[] ids = res.getIDRefs();
            for (int count = 0; count < ids.length; count++) {
                if (idLocations.containsKey(ids[count])) {
                    res.resolveIDRef(ids[count], (List) idLocations.get(ids[count]));
                } else {
                    log.warn(odi.getName() + ": Unresolved id reference \""
                        + ids[count] + "\" found.");
                    addUnresolvedIDRef(ids[count], res);
                }
            }
            // check to see if ODI is now fully resolved, if so process it
            if (res.isResolved()) {
                model.handleOffDocumentItem(odi);
            }
        } else {
            model.handleOffDocumentItem(odi);
        }
    }
}
TOP

Related Classes of org.apache.fop.area.AreaTreeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.