123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- /*
- * $Id$
- * 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");
- }
- }
- }
-
- }
|