123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- * 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.layoutmgr;
-
- import java.util.List;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.area.AreaTreeHandler;
- import org.apache.fop.area.AreaTreeModel;
- import org.apache.fop.area.IDTracker;
- import org.apache.fop.area.PageViewport;
- import org.apache.fop.area.Resolvable;
- import org.apache.fop.datatypes.Numeric;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.flow.Marker;
- import org.apache.fop.fo.flow.RetrieveMarker;
- import org.apache.fop.fo.pagination.AbstractPageSequence;
-
- /**
- * Abstract base class for a page sequence layout manager.
- */
- public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutManager
- implements TopLevelLayoutManager {
-
- private static Log log = LogFactory.getLog(AbstractPageSequenceLayoutManager.class);
-
- /**
- * AreaTreeHandler which activates the PSLM and controls
- * the rendering of its pages.
- */
- protected AreaTreeHandler areaTreeHandler;
-
- /** ID tracker supplied by the AreaTreeHandler */
- protected IDTracker idTracker;
-
- /** page sequence formatting object being processed by this class */
- protected AbstractPageSequence pageSeq;
-
- /** Current page with page-viewport-area being filled by the PSLM. */
- protected Page curPage;
-
- /** the current page number */
- protected int currentPageNum = 0;
- /** The stating page number */
- protected int startPageNum = 0;
-
- /**
- * Constructor
- *
- * @param ath the area tree handler object
- * @param pseq fo:page-sequence to process
- */
- public AbstractPageSequenceLayoutManager(AreaTreeHandler ath, AbstractPageSequence pseq) {
- super(pseq);
- this.areaTreeHandler = ath;
- this.idTracker = ath.getIDTracker();
- this.pageSeq = pseq;
- }
-
- /**
- * @return the LayoutManagerMaker object associated to the areaTreeHandler
- */
- public LayoutManagerMaker getLayoutManagerMaker() {
- return areaTreeHandler.getLayoutManagerMaker();
- }
-
- /**
- * Provides access to the current page.
- * @return the current Page
- */
- public Page getCurrentPage() {
- return curPage;
- }
-
- /**
- * Provides access for setting the current page.
- * @param currentPage the new current Page
- */
- protected void setCurrentPage(Page currentPage) {
- this.curPage = currentPage;
- }
-
- /**
- * Provides access to the current page number
- * @return the current page number
- */
- protected int getCurrentPageNum() {
- return currentPageNum;
- }
-
- /** {@inheritDoc} */
- public void initialize() {
- startPageNum = pageSeq.getStartingPageNumber();
- currentPageNum = startPageNum - 1;
- }
-
- /**
- * This returns the first PageViewport that contains an id trait
- * matching the idref argument, or null if no such PV exists.
- *
- * @param idref the idref trait needing to be resolved
- * @return the first PageViewport that contains the ID trait
- */
- public PageViewport getFirstPVWithID(String idref) {
- List list = idTracker.getPageViewportsContainingID(idref);
- if (list != null && list.size() > 0) {
- return (PageViewport) list.get(0);
- }
- return null;
- }
-
- /**
- * This returns the last PageViewport that contains an id trait
- * matching the idref argument, or null if no such PV exists.
- *
- * @param idref the idref trait needing to be resolved
- * @return the last PageViewport that contains the ID trait
- */
- public PageViewport getLastPVWithID(String idref) {
- List list = idTracker.getPageViewportsContainingID(idref);
- if (list != null && list.size() > 0) {
- return (PageViewport) list.get(list.size() - 1);
- }
- return null;
- }
-
- /**
- * Add an ID reference to the current page.
- * When adding areas the area adds its ID reference.
- * For the page layout manager it adds the id reference
- * with the current page to the area tree.
- *
- * @param id the ID reference to add
- */
- public void addIDToPage(String id) {
- if (id != null && id.length() > 0) {
- idTracker.associateIDWithPageViewport(id, curPage.getPageViewport());
- }
- }
-
- /**
- * Add an id reference of the layout manager in the AreaTreeHandler,
- * if the id hasn't been resolved yet
- * @param id the id to track
- * @return a boolean indicating if the id has already been resolved
- * TODO Maybe give this a better name
- */
- public boolean associateLayoutManagerID(String id) {
- if (log.isDebugEnabled()) {
- log.debug("associateLayoutManagerID(" + id + ")");
- }
- if (!idTracker.alreadyResolvedID(id)) {
- idTracker.signalPendingID(id);
- return false;
- } else {
- return true;
- }
- }
-
- /**
- * Notify the areaTreeHandler that the LayoutManagers containing
- * idrefs have finished creating areas
- * @param id the id for which layout has finished
- */
- public void notifyEndOfLayout(String id) {
- idTracker.signalIDProcessed(id);
- }
-
- /**
- * Identify an unresolved area (one needing an idref to be
- * resolved, e.g. the internal-destination of an fo:basic-link)
- * for both the AreaTreeHandler and PageViewport object.
- *
- * The IDTracker keeps a document-wide list of idref's
- * and the PV's needing them to be resolved. It uses this to
- * send notifications to the PV's when an id has been resolved.
- *
- * The PageViewport keeps lists of id's needing resolving, along
- * with the child areas (page-number-citation, basic-link, etc.)
- * of the PV needing their resolution.
- *
- * @param id the ID reference to add
- * @param res the resolvable object that needs resolving
- */
- public void addUnresolvedArea(String id, Resolvable res) {
- curPage.getPageViewport().addUnresolvedIDRef(id, res);
- idTracker.addUnresolvedIDRef(id, curPage.getPageViewport());
- }
-
- /**
- * Bind the RetrieveMarker to the corresponding Marker subtree.
- * If the boundary is page then it will only check the
- * current page. For page-sequence and document it will
- * lookup preceding pages from the area tree and try to find
- * a marker.
- * If we retrieve a marker from a preceding page,
- * then the containing page does not have a qualifying area,
- * and all qualifying areas have ended.
- * Therefore we use last-ending-within-page (Constants.EN_LEWP)
- * as the position.
- *
- * @param rm the RetrieveMarker instance whose properties are to
- * used to find the matching Marker.
- * @return a bound RetrieveMarker instance, or null if no Marker
- * could be found.
- */
- public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
- AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
- String name = rm.getRetrieveClassName();
- int boundary = rm.getRetrieveBoundary();
-
- // get marker from the current markers on area tree
- Marker mark = getCurrentPV().resolveMarker(rm);
- if (mark == null && boundary != EN_PAGE) {
- // go back over pages until mark found
- // if document boundary then keep going
- boolean doc = (boundary == EN_DOCUMENT);
- int seq = areaTreeModel.getPageSequenceCount();
- int page = areaTreeModel.getPageCount(seq) - 1;
- while (page < 0 && doc && seq > 1) {
- seq--;
- page = areaTreeModel.getPageCount(seq) - 1;
- }
- while (page >= 0) {
- PageViewport pv = areaTreeModel.getPage(seq, page);
- int originalPosition = rm.getPosition();
- rm.changePositionTo(Constants.EN_LEWP);
- mark = (Marker) pv.resolveMarker(rm);
- // this is probably not necessary since the RM will not be used again, but to be safe...
- rm.changePositionTo(originalPosition);
- if (mark != null) {
- break;
- }
- page--;
- if (page < 0 && doc && seq > 1) {
- seq--;
- page = areaTreeModel.getPageCount(seq) - 1;
- }
- }
- }
-
- if (mark == null) {
- log.debug("found no marker with name: " + name);
- return null;
- } else {
- rm.bindMarker(mark);
- return rm;
- }
- }
-
- /**
- * Creates and returns a new page.
- * @param pageNumber the page number
- * @param isBlank true if it's a blank page
- * @return the newly created page
- */
- protected abstract Page createPage(int pageNumber, boolean isBlank);
-
- /**
- * Makes a new page
- *
- * @param isBlank whether this page is blank or not
- * @return a new page
- */
- protected Page makeNewPage(boolean isBlank) {
- if (curPage != null) {
- finishPage();
- }
-
- currentPageNum++;
-
- curPage = createPage(currentPageNum, isBlank);
-
- if (log.isDebugEnabled()) {
- log.debug("[" + curPage.getPageViewport().getPageNumberString()
- + (isBlank ? "*" : "") + "]");
- }
-
- addIDToPage(pageSeq.getRoot().getId());
- addIDToPage(pageSeq.getId());
- return curPage;
- }
-
- /**
- * Finishes a page in preparation for a new page.
- */
- protected void finishPage() {
- if (log.isTraceEnabled()) {
- curPage.getPageViewport().dumpMarkers();
- }
-
- // Try to resolve any unresolved IDs for the current page.
- //
- idTracker.tryIDResolution(curPage.getPageViewport());
- // Queue for ID resolution and rendering
- areaTreeHandler.getAreaTreeModel().addPage(curPage.getPageViewport());
- if (log.isDebugEnabled()) {
- log.debug("page finished: " + curPage.getPageViewport().getPageNumberString()
- + ", current num: " + currentPageNum);
- }
- curPage = null;
- }
-
- /** {@inheritDoc} */
- public void doForcePageCount(Numeric nextPageSeqInitialPageNumber) {
-
- int forcePageCount = pageSeq.getForcePageCount();
-
- // xsl-spec version 1.0 (15.oct 2001)
- // auto | even | odd | end-on-even | end-on-odd | no-force | inherit
- // auto:
- // Force the last page in this page-sequence to be an odd-page
- // if the initial-page-number of the next page-sequence is even.
- // Force it to be an even-page
- // if the initial-page-number of the next page-sequence is odd.
- // If there is no next page-sequence
- // or if the value of its initial-page-number is "auto" do not force any page.
-
- // if force-page-count is auto then set the value of forcePageCount
- // depending on the initial-page-number of the next page-sequence
- if (nextPageSeqInitialPageNumber != null && forcePageCount == Constants.EN_AUTO) {
- if (nextPageSeqInitialPageNumber.getEnum() != 0) {
- // auto | auto-odd | auto-even
- int nextPageSeqPageNumberType = nextPageSeqInitialPageNumber.getEnum();
- if (nextPageSeqPageNumberType == Constants.EN_AUTO_ODD) {
- forcePageCount = Constants.EN_END_ON_EVEN;
- } else if (nextPageSeqPageNumberType == Constants.EN_AUTO_EVEN) {
- forcePageCount = Constants.EN_END_ON_ODD;
- } else { // auto
- forcePageCount = Constants.EN_NO_FORCE;
- }
- } else { // <integer> for explicit page number
- int nextPageSeqPageStart = nextPageSeqInitialPageNumber.getValue();
- // spec rule
- nextPageSeqPageStart = (nextPageSeqPageStart > 0) ? nextPageSeqPageStart : 1;
- if (nextPageSeqPageStart % 2 == 0) { // explicit even startnumber
- forcePageCount = Constants.EN_END_ON_ODD;
- } else { // explicit odd startnumber
- forcePageCount = Constants.EN_END_ON_EVEN;
- }
- }
- }
-
- if (forcePageCount == Constants.EN_EVEN) {
- if ((currentPageNum - startPageNum + 1) % 2 != 0) { // we have an odd number of pages
- curPage = makeNewPage(true);
- }
- } else if (forcePageCount == Constants.EN_ODD) {
- if ((currentPageNum - startPageNum + 1) % 2 == 0) { // we have an even number of pages
- curPage = makeNewPage(true);
- }
- } else if (forcePageCount == Constants.EN_END_ON_EVEN) {
- if (currentPageNum % 2 != 0) { // we are now on an odd page
- curPage = makeNewPage(true);
- }
- } else if (forcePageCount == Constants.EN_END_ON_ODD) {
- if (currentPageNum % 2 == 0) { // we are now on an even page
- curPage = makeNewPage(true);
- }
- } else if (forcePageCount == Constants.EN_NO_FORCE) {
- // i hope: nothing special at all
- }
-
- if (curPage != null) {
- finishPage();
- }
- }
-
- /** {@inheritDoc} */
- public void reset() {
- throw new IllegalStateException();
- }
-
- }
|