Package org.apache.fop.fo.pagination

Source Code of org.apache.fop.fo.pagination.PageSequence

/*
* $Id: PageSequence.java,v 1.39 2001/09/11 10:04:25 keiron Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* 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;

// FOP
import org.apache.fop.fo.*;
import org.apache.fop.fo.properties.*;
import org.apache.fop.fo.flow.Flow;
import org.apache.fop.fo.flow.StaticContent;
import org.apache.fop.layout.Area;
import org.apache.fop.layout.AreaContainer;
import org.apache.fop.layout.BodyAreaContainer;
import org.apache.fop.layout.AreaTree;
import org.apache.fop.layout.Page;
import org.apache.fop.layout.PageMaster;
import org.apache.fop.apps.FOPException;

// Java
import java.util.*;


/**
* This provides pagination of flows onto pages. Much of the logic for paginating
* flows is contained in this class. The main entry point is the format method.
*/
public class PageSequence extends FObj {
    //
    // Factory methods
    //
    public static class Maker extends FObj.Maker {
        public FObj make(FObj parent,
                         PropertyList propertyList) throws FOPException {
            return new PageSequence(parent, propertyList);
        }

    }

    public static FObj.Maker maker() {
        return new PageSequence.Maker();
    }

    //
    // intial-page-number types
    //
    private static final int EXPLICIT = 0;
    private static final int AUTO = 1;
    private static final int AUTO_EVEN = 2;
    private static final int AUTO_ODD = 3;

    //
    // associations
    //

    /**
     * The parent root object
     */
    private Root root;

    /**
     * the set of layout masters (provided by the root object)
     */
    private LayoutMasterSet layoutMasterSet;

    // There doesn't seem to be anything in the spec requiring flows
    // to be in the order given, only that they map to the regions
    // defined in the page sequence, so all we need is this one hashtable
    // the set of flows includes StaticContent flows also

    /**
     * Map of flows to their flow name (flow-name, Flow)
     */
    private Hashtable _flowMap;

    /**
     * the "master-name" attribute
     */
    private String masterName;

    // according to communication from Paul Grosso (XSL-List,
    // 001228, Number 406), confusion in spec section 6.4.5 about
    // multiplicity of fo:flow in XSL 1.0 is cleared up - one (1)
    // fo:flow per fo:page-sequence only.
    private boolean isFlowSet = false;

    //
    // state attributes used during layout
    //

    private Page currentPage;

    // page number and related formatting variables
    private String ipnValue;
    private int currentPageNumber = 0;
    private PageNumberGenerator pageNumberGenerator;

    private int forcePageCount = 0;
    private int pageCount = 0;
    private boolean isForcing = false;

    /**
     * specifies page numbering type (auto|auto-even|auto-odd|explicit)
     */
    private int pageNumberType;

    /**
     * used to determine whether to calculate auto, auto-even, auto-odd
     */
    private boolean thisIsFirstPage;

    /**
     * the current subsequence while formatting a given page sequence
     */
    private SubSequenceSpecifier currentSubsequence;

    /**
     * the current index in the subsequence list
     */
    private int currentSubsequenceNumber =
        -1;    // starting case is -1 so that first getNext increments to 0

    /**
     * the name of the current page master
     */
    private String currentPageMasterName;


    protected PageSequence(FObj parent,
                           PropertyList propertyList) throws FOPException {
        super(parent, propertyList);
        this.name = "fo:page-sequence";

        if (parent.getName().equals("fo:root")) {
            this.root = (Root)parent;
            // this.root.addPageSequence(this);
        }
        else {
            throw new FOPException("page-sequence must be child of root, not "
                                   + parent.getName());
        }

        layoutMasterSet = root.getLayoutMasterSet();

        // best time to run some checks on LayoutMasterSet
        layoutMasterSet.checkRegionNames();

        _flowMap = new Hashtable();

        thisIsFirstPage =
            true;    // we are now on the first page of the page sequence
        ipnValue = this.properties.get("initial-page-number").getString();

        if (ipnValue.equals("auto")) {
            pageNumberType = AUTO;
        } else if (ipnValue.equals("auto-even")) {
            pageNumberType = AUTO_EVEN;
        } else if (ipnValue.equals("auto-odd")) {
            pageNumberType = AUTO_ODD;
        } else {
            pageNumberType = EXPLICIT;
            try {
                int pageStart = new Integer(ipnValue).intValue();
                this.currentPageNumber = (pageStart > 0) ? pageStart - 1 : 0;
            } catch (NumberFormatException nfe) {
                throw new FOPException("\"" + ipnValue
                                       + "\" is not a valid value for initial-page-number");
            }
        }

        masterName = this.properties.get("master-name").getString();

        // get the 'format' properties
        this.pageNumberGenerator =
            new PageNumberGenerator(this.properties.get("format").getString(),
                                    this.properties.get("grouping-separator").getCharacter(),
                                    this.properties.get("grouping-size").getNumber().intValue(),
                                    this.properties.get("letter-value").getEnum());

        this.forcePageCount =
            this.properties.get("force-page-count").getEnum();

        // this.properties.get("country");
        // this.properties.get("language");
        // this.properties.get("id");
    }


    public void addFlow(Flow flow) throws FOPException {
        if (_flowMap.containsKey(flow.getFlowName())) {
            throw new FOPException("flow-names must be unique within an fo:page-sequence");
        }
        if (!this.layoutMasterSet.regionNameExists(flow.getFlowName())) {
            log.error("region-name '"
                                   + flow.getFlowName()
                                   + "' doesn't exist in the layout-master-set.");
        }
        _flowMap.put(flow.getFlowName(), flow);
        setIsFlowSet(true);
    }


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

        int firstAvailPageNumber = 0;
        do {
            // makePage() moved to after the page-number computations,
            // but store the page-number at this point for that method,
            // since we want the 'current' current page-number...
            firstAvailPageNumber = this.root.getRunningPageNumberCounter();
            boolean tempIsFirstPage = false;

            if (thisIsFirstPage) {
                tempIsFirstPage = thisIsFirstPage;
                if (pageNumberType == AUTO) {
                    this.currentPageNumber =
                        this.root.getRunningPageNumberCounter();
                } else if (pageNumberType == AUTO_ODD) {
                    this.currentPageNumber =
                        this.root.getRunningPageNumberCounter();
                    if (this.currentPageNumber % 2 == 1) {
                        this.currentPageNumber++;
                    }
                } else if (pageNumberType == AUTO_EVEN) {
                    this.currentPageNumber =
                        this.root.getRunningPageNumberCounter();
                    if (this.currentPageNumber % 2 == 0) {
                        this.currentPageNumber++;
                    }
                }
                thisIsFirstPage = false;
            }

            this.currentPageNumber++;

            // deliberately moved down here so page-number calculations
            // are complete;
            // compute flag for 'blank-or-not-blank'
            boolean isEmptyPage = false;

            if ((status.getCode() == Status.FORCE_PAGE_BREAK_EVEN)
                    && ((currentPageNumber % 2) == 1)) {
                isEmptyPage = true;
            } else if ((status.getCode() == Status.FORCE_PAGE_BREAK_ODD)
                       && ((currentPageNumber % 2) == 0)) {
                isEmptyPage = true;
            } else {
                isEmptyPage = false;
            }

            currentPage = makePage(areaTree, firstAvailPageNumber,
                                   tempIsFirstPage, isEmptyPage);

            currentPage.setNumber(this.currentPageNumber);
            String formattedPageNumber =
                pageNumberGenerator.makeFormattedPageNumber(this.currentPageNumber);
            currentPage.setFormattedNumber(formattedPageNumber);
            this.root.setRunningPageNumberCounter(this.currentPageNumber);

            log.info("[" + currentPageNumber + "]");

            if ((status.getCode() == Status.FORCE_PAGE_BREAK_EVEN)
                && ((currentPageNumber % 2) == 1)) {}
            else if ((status.getCode() == Status.FORCE_PAGE_BREAK_ODD)
                 && ((currentPageNumber % 2) == 0)) {}
            else {
                BodyAreaContainer bodyArea = currentPage.getBody();
                bodyArea.setIDReferences(areaTree.getIDReferences());

                Flow flow = getCurrentFlow(RegionBody.REGION_CLASS);

                if (null == flow) {
                    log.error("No flow found for region-body "
                                           + "in page-master '"
                                           + currentPageMasterName + "'");
                    break;

                } else {
                    status = flow.layout(bodyArea);
                }

            }

            // because of markers, do after fo:flow (likely also
            // justifiable because of spec)
            currentPage.setPageSequence(this);
            formatStaticContent(areaTree);

            //log.info("]");
            areaTree.addPage(currentPage);
            this.pageCount++;    // used for 'force-page-count' calculations
        }
        while (flowsAreIncomplete());
        // handle the 'force-page-count'
        forcePage(areaTree, firstAvailPageNumber);

        currentPage = null;
    }

    /**
     * Creates a new page area for the given parameters
     * @param areaTree the area tree the page should be contained in
     * @param firstAvailPageNumber the page number for this page
     * @param isFirstPage true when this is the first page in the sequence
     * @param isEmptyPage true if this page will be empty (e.g. forced even or odd break)
     * @return a Page layout object based on the page master selected from the params
     */
    private Page makePage(AreaTree areaTree, int firstAvailPageNumber,
                          boolean isFirstPage,
                          boolean isEmptyPage) throws FOPException {
        // layout this page sequence

        // while there is still stuff in the flow, ask the
        // layoutMasterSet for a new page

        // page number is 0-indexed
        PageMaster pageMaster = getNextPageMaster(masterName,
                                firstAvailPageNumber,
                                isFirstPage, isEmptyPage);

        // a legal alternative is to use the last sub-sequence
        // specification which should be handled in getNextSubsequence. That's not done here.
        if (pageMaster == null) {
            throw new FOPException("page masters exhausted. Cannot recover.");
        }
        Page p = pageMaster.makePage(areaTree);
        if (currentPage != null) {
            Vector foots = currentPage.getPendingFootnotes();
            p.setPendingFootnotes(foots);
        }
        return p;
    }

    /**
     * Formats the static content of the current page
     */
    private void formatStaticContent(AreaTree areaTree) throws FOPException {
        SimplePageMaster simpleMaster = getCurrentSimplePageMaster();

        if (simpleMaster.getRegion(RegionBefore.REGION_CLASS) != null
                && (currentPage.getBefore() != null)) {
            Flow staticFlow =
                (Flow)_flowMap.get(simpleMaster.getRegion(RegionBefore.REGION_CLASS).getRegionName());
            if (staticFlow != null) {
                AreaContainer beforeArea = currentPage.getBefore();
                beforeArea.setIDReferences(areaTree.getIDReferences());
                layoutStaticContent(staticFlow,
                                    simpleMaster.getRegion(RegionBefore.REGION_CLASS),
                                    beforeArea);
            }
        }

        if (simpleMaster.getRegion(RegionAfter.REGION_CLASS) != null
                && (currentPage.getAfter() != null)) {
            Flow staticFlow =
                (Flow)_flowMap.get(simpleMaster.getRegion(RegionAfter.REGION_CLASS).getRegionName());
            if (staticFlow != null) {
                AreaContainer afterArea = currentPage.getAfter();
                afterArea.setIDReferences(areaTree.getIDReferences());
                layoutStaticContent(staticFlow,
                                    simpleMaster.getRegion(RegionAfter.REGION_CLASS),
                                    afterArea);
            }
        }

        if (simpleMaster.getRegion(RegionStart.REGION_CLASS) != null
                && (currentPage.getStart() != null)) {
            Flow staticFlow =
                (Flow)_flowMap.get(simpleMaster.getRegion(RegionStart.REGION_CLASS).getRegionName());
            if (staticFlow != null) {
                AreaContainer startArea = currentPage.getStart();
                startArea.setIDReferences(areaTree.getIDReferences());
                layoutStaticContent(staticFlow,
                                    simpleMaster.getRegion(RegionStart.REGION_CLASS),
                                    startArea);
            }
        }

        if (simpleMaster.getRegion(RegionEnd.REGION_CLASS) != null
                && (currentPage.getEnd() != null)) {
            Flow staticFlow =
                (Flow)_flowMap.get(simpleMaster.getRegion(RegionEnd.REGION_CLASS).getRegionName());
            if (staticFlow != null) {
                AreaContainer endArea = currentPage.getEnd();
                endArea.setIDReferences(areaTree.getIDReferences());
                layoutStaticContent(staticFlow,
                                    simpleMaster.getRegion(RegionEnd.REGION_CLASS),
                                    endArea);
            }
        }

    }

    private void layoutStaticContent(Flow flow, Region region,
                                     AreaContainer area) throws FOPException {
        if (flow instanceof StaticContent) {
            AreaContainer beforeArea = currentPage.getBefore();
            ((StaticContent)flow).layout(area, region);
        } else {
            log.error("" + region.getName()
                                   + " only supports static-content flows currently. Cannot use flow named '"
                                   + flow.getFlowName() + "'");
        }
    }

    /**
     * Returns the next SubSequenceSpecifier for the given page sequence master. The result
     * is bassed on the current state of this page sequence.
     */
    // refactored from PageSequenceMaster
    private SubSequenceSpecifier getNextSubsequence(PageSequenceMaster master) {
        if (master.getSubSequenceSpecifierCount()
                > currentSubsequenceNumber + 1) {

            currentSubsequence =
                master.getSubSequenceSpecifier(currentSubsequenceNumber + 1);
            currentSubsequenceNumber++;
            return currentSubsequence;
        } else {
            return null;
        }

    }

    /**
     * Returns the next simple page master for the given sequence master, page number and
     * other state information
     */
    private SimplePageMaster getNextSimplePageMaster(PageSequenceMaster sequenceMaster,
            int currentPageNumber, boolean thisIsFirstPage,
            boolean isEmptyPage) {
        // handle forcing
        if (isForcing) {
            String nextPageMaster = getNextPageMasterName(sequenceMaster,
                                    currentPageNumber, false, true);
            return this.layoutMasterSet.getSimplePageMaster(nextPageMaster);
        }
        String nextPageMaster = getNextPageMasterName(sequenceMaster,
                                currentPageNumber, thisIsFirstPage, isEmptyPage);
        return this.layoutMasterSet.getSimplePageMaster(nextPageMaster);

    }

    private String getNextPageMasterName(PageSequenceMaster sequenceMaster,
                                         int currentPageNumber,
                                         boolean thisIsFirstPage,
                                         boolean isEmptyPage) {

        if (null == currentSubsequence) {
            currentSubsequence = getNextSubsequence(sequenceMaster);
        }

        String nextPageMaster =
            currentSubsequence.getNextPageMaster(currentPageNumber,
                                                 thisIsFirstPage,
                                                 isEmptyPage);


        if (null == nextPageMaster
                || isFlowForMasterNameDone(currentPageMasterName)) {
            SubSequenceSpecifier nextSubsequence =
                getNextSubsequence(sequenceMaster);
            if (nextSubsequence == null) {
                log.error("Page subsequences exhausted. Using previous subsequence.");
                thisIsFirstPage =
                    true;    // this becomes the first page in the new (old really) page master
                currentSubsequence.reset();

                // we leave currentSubsequence alone
            }
            else {
                currentSubsequence = nextSubsequence;
            }

            nextPageMaster =
                currentSubsequence.getNextPageMaster(currentPageNumber,
                                                     thisIsFirstPage,
                                                     isEmptyPage);
        }
        currentPageMasterName = nextPageMaster;

        return nextPageMaster;

    }

    private SimplePageMaster getCurrentSimplePageMaster() {
        return this.layoutMasterSet.getSimplePageMaster(currentPageMasterName);
    }

    private String getCurrentPageMasterName() {
        return currentPageMasterName;
    }

    // refactored from LayoutMasterSet
    private PageMaster getNextPageMaster(String pageSequenceName,
                                         int currentPageNumber,
                                         boolean thisIsFirstPage,
                                         boolean isEmptyPage) throws FOPException {
        PageMaster pageMaster = null;

        // see if there is a page master sequence for this master name
        PageSequenceMaster sequenceMaster =
            this.layoutMasterSet.getPageSequenceMaster(pageSequenceName);

        if (sequenceMaster != null) {
            pageMaster = getNextSimplePageMaster(sequenceMaster,
                                                 currentPageNumber,
                                                 thisIsFirstPage,
                                                 isEmptyPage).getPageMaster();

        } else {    // otherwise see if there's a simple master by the given name
            SimplePageMaster simpleMaster =
                this.layoutMasterSet.getSimplePageMaster(pageSequenceName);
            if (simpleMaster == null) {
                throw new FOPException("'master-name' for 'fo:page-sequence'"
                                       + "matches no 'simple-page-master' or 'page-sequence-master'");
            }
            currentPageMasterName = pageSequenceName;

            pageMaster = simpleMaster.getNextPageMaster();
        }
        return pageMaster;
    }


    /**
     * Returns true when there is more flow elements left to lay out.
     */
    private boolean flowsAreIncomplete() {
        boolean isIncomplete = false;

        for (Enumeration e = _flowMap.elements(); e.hasMoreElements(); ) {
            Flow flow = (Flow)e.nextElement();
            if (flow instanceof StaticContent) {
                continue;
            }

            Status status = flow.getStatus();
            isIncomplete |= status.isIncomplete();
        }
        return isIncomplete;
    }

    /**
     * Returns the flow that maps to the given region class for the current
     * page master.
     */
    private Flow getCurrentFlow(String regionClass) {
        Region region = getCurrentSimplePageMaster().getRegion(regionClass);
        if (region != null) {
            Flow flow = (Flow)_flowMap.get(region.getRegionName());
            return flow;

        } else {

            System.out.println("flow is null. regionClass = '" + regionClass
                               + "' currentSPM = "
                               + getCurrentSimplePageMaster());

            return null;
        }

    }

    private boolean isFlowForMasterNameDone(String masterName) {
        // parameter is master-name of PMR; we need to locate PM
        // referenced by this, and determine whether flow(s) are OK
        if (isForcing)
            return false;
        if (masterName != null) {

            SimplePageMaster spm =
                this.layoutMasterSet.getSimplePageMaster(masterName);
            Region region = spm.getRegion(RegionBody.REGION_CLASS);


            Flow flow = (Flow)_flowMap.get(region.getRegionName());
            if ((null == flow) || flow.getStatus().isIncomplete())
                return false;
            else
                return true;
        }
        return false;
    }

    public boolean isFlowSet() {
        return isFlowSet;
    }

    public void setIsFlowSet(boolean isFlowSet) {
        this.isFlowSet = isFlowSet;
    }

    public String getIpnValue() {
        return ipnValue;
    }

    public int getCurrentPageNumber() {
        return currentPageNumber;
    }

    private void forcePage(AreaTree areaTree, int firstAvailPageNumber) {
        boolean makePage = false;
        if (this.forcePageCount == ForcePageCount.AUTO) {
            PageSequence nextSequence =
                this.root.getSucceedingPageSequence(this);
            if (nextSequence != null) {
                if (nextSequence.getIpnValue().equals("auto")) {
                    // do nothing special
                }
                else if (nextSequence.getIpnValue().equals("auto-odd")) {
                    if (firstAvailPageNumber % 2 == 0) {
                        makePage = true;
                    }
                } else if (nextSequence.getIpnValue().equals("auto-even")) {
                    if (firstAvailPageNumber % 2 != 0) {
                        makePage = true;
                    }
                } else {
                    int nextSequenceStartPageNumber =
                        nextSequence.getCurrentPageNumber();
                    if ((nextSequenceStartPageNumber % 2 == 0)
                            && (firstAvailPageNumber % 2 == 0)) {
                        makePage = true;
                    } else if ((nextSequenceStartPageNumber % 2 != 0)
                               && (firstAvailPageNumber % 2 != 0)) {
                        makePage = true;
                    }
                }
            }
        } else if ((this.forcePageCount == ForcePageCount.EVEN)
                   && (this.pageCount % 2 != 0)) {
            makePage = true;
        } else if ((this.forcePageCount == ForcePageCount.ODD)
                   && (this.pageCount % 2 == 0)) {
            makePage = true;
        } else if ((this.forcePageCount == ForcePageCount.END_ON_EVEN)
                   && (firstAvailPageNumber % 2 == 0)) {
            makePage = true;
        } else if ((this.forcePageCount == ForcePageCount.END_ON_ODD)
                   && (firstAvailPageNumber % 2 != 0)) {
            makePage = true;
        } else if (this.forcePageCount == ForcePageCount.NO_FORCE) {
            // do nothing
        }

        if (makePage) {
            try {
                this.isForcing = true;
                this.currentPageNumber++;
                firstAvailPageNumber = this.currentPageNumber;
                currentPage = makePage(areaTree, firstAvailPageNumber, false,
                                       true);
                String formattedPageNumber =
                    pageNumberGenerator.makeFormattedPageNumber(this.currentPageNumber);
                currentPage.setFormattedNumber(formattedPageNumber);
                currentPage.setPageSequence(this);
                formatStaticContent(areaTree);
                log.debug("[forced-" + firstAvailPageNumber + "]");
                areaTree.addPage(currentPage);
                this.root.setRunningPageNumberCounter(this.currentPageNumber);
                this.isForcing = false;
            } catch (FOPException fopex) {
                log.debug("'force-page-count' failure");
            }
        }
    }

}
TOP

Related Classes of org.apache.fop.fo.pagination.PageSequence

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.