blob: 7d84a91ffcec39906b8726dd5d2bfccf79229d99 [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.layoutmgr;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.area.AreaTreeModel;
import org.apache.fop.area.LineArea;
import org.apache.fop.complexscripts.bidi.BidiResolver;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.PageSequenceMaster;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
import org.apache.fop.traits.MinOptMax;
/**
* LayoutManager for a PageSequence. This class is instantiated by
* area.AreaTreeHandler for each fo:page-sequence found in the
* input document.
*/
public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager {
private static Log log = LogFactory.getLog(PageSequenceLayoutManager.class);
private PageProvider pageProvider;
private PageBreaker pageBreaker;
/** Footnotes coming from repeated table headers, to be added before any other footnote. */
private List<List<KnuthElement>> tableHeaderFootnotes;
/** Footnotes coming from repeated table footers, to be added after any other footnote. */
private List<List<KnuthElement>> tableFooterFootnotes;
private int startIntrusionAdjustment;
private int endIntrusionAdjustment;
/**
* Constructor
*
* @param ath the area tree handler object
* @param pseq fo:page-sequence to process
*/
public PageSequenceLayoutManager(AreaTreeHandler ath, PageSequence pseq) {
super(ath, pseq);
this.pageProvider = new PageProvider(ath, pseq);
}
/** @return the PageProvider applicable to this page-sequence. */
public PageProvider getPageProvider() {
return this.pageProvider;
}
/**
* @return the PageSequence being managed by this layout manager
*/
protected PageSequence getPageSequence() {
return (PageSequence)pageSeq;
}
/**
* Provides access to this object
* @return this PageSequenceLayoutManager instance
*/
public PageSequenceLayoutManager getPSLM() {
return this;
}
public FlowLayoutManager getFlowLayoutManager() {
if (pageBreaker == null) {
throw new IllegalStateException("This method can be called only during layout");
}
return pageBreaker.getCurrentChildLM();
}
/** {@inheritDoc} */
public void activateLayout() {
initialize();
// perform step 5.8 of refinement process (Unicode BIDI Processing)
if (areaTreeHandler.isComplexScriptFeaturesEnabled()) {
BidiResolver.resolveInlineDirectionality(getPageSequence());
}
LineArea title = null;
if (getPageSequence().getTitleFO() != null) {
try {
ContentLayoutManager clm = getLayoutManagerMaker()
.makeContentLayoutManager(this, getPageSequence().getTitleFO());
Area parentArea = clm.getParentArea(null);
assert (parentArea instanceof LineArea);
title = (LineArea) parentArea;
} catch (IllegalStateException e) {
// empty title; do nothing
}
}
AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
org.apache.fop.area.PageSequence pageSequenceAreaObject
= new org.apache.fop.area.PageSequence(title);
transferExtensions(pageSequenceAreaObject);
pageSequenceAreaObject.setLocale(getPageSequence().getLocale());
areaTreeModel.startPageSequence(pageSequenceAreaObject);
if (log.isDebugEnabled()) {
log.debug("Starting layout");
}
curPage = makeNewPage(false);
pageBreaker = new PageBreaker(this);
int flowBPD = getCurrentPV().getBodyRegion().getRemainingBPD();
pageBreaker.doLayout(flowBPD);
finishPage();
}
/** {@inheritDoc} */
public void finishPageSequence() {
if (pageSeq.hasId()) {
idTracker.signalIDProcessed(pageSeq.getId());
}
pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum,
(currentPageNum - startPageNum) + 1);
areaTreeHandler.notifyPageSequenceFinished(pageSeq,
(currentPageNum - startPageNum) + 1);
getPageSequence().releasePageSequence();
// If this sequence has a page sequence master so we must reset
// it in preparation for the next sequence
String masterReference = getPageSequence().getMasterReference();
PageSequenceMaster pageSeqMaster
= pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference);
if (pageSeqMaster != null) {
pageSeqMaster.reset();
}
if (log.isDebugEnabled()) {
log.debug("Ending layout");
}
}
/** {@inheritDoc} */
protected Page createPage(int pageNumber, boolean isBlank) {
return pageProvider.getPage(isBlank,
pageNumber, PageProvider.RELTO_PAGE_SEQUENCE);
}
@Override
protected Page makeNewPage(boolean isBlank) {
Page newPage = super.makeNewPage(isBlank);
// Empty pages (pages that have been generated from a SPM that has an un-mapped flow name)
// cannot layout areas from the main flow. Blank pages can be created from empty pages.
if (!isBlank) {
while (!getPageSequence().getMainFlow().getFlowName()
.equals(newPage.getSimplePageMaster()
.getRegion(FO_REGION_BODY).getRegionName())) {
newPage = super.makeNewPage(isBlank);
}
}
return newPage;
}
private void layoutSideRegion(int regionID) {
SideRegion reg = (SideRegion)curPage.getSimplePageMaster().getRegion(regionID);
if (reg == null) {
return;
}
StaticContent sc = getPageSequence().getStaticContent(reg.getRegionName());
if (sc == null) {
return;
}
StaticContentLayoutManager lm = getLayoutManagerMaker()
.makeStaticContentLayoutManager(
this, sc, reg);
lm.doLayout();
}
/** {@inheritDoc} */
protected void finishPage() {
// Layout side regions
layoutSideRegion(FO_REGION_BEFORE);
layoutSideRegion(FO_REGION_AFTER);
layoutSideRegion(FO_REGION_START);
layoutSideRegion(FO_REGION_END);
super.finishPage();
}
/**
* The last page number of the sequence may be incremented, as determined by the
* force-page-count formatting property semantics
* @param lastPageNum number of sequence
* @return the forced last page number of sequence
*/
protected int getForcedLastPageNum(final int lastPageNum) {
int forcedLastPageNum = lastPageNum;
int relativeLastPage = lastPageNum - startPageNum + 1;
if (relativeLastPage % 2 != 0
&& (getPageSequence().getForcePageCount() == Constants.EN_EVEN
|| getPageSequence().getForcePageCount() == Constants.EN_END_ON_EVEN)) {
forcedLastPageNum++;
} else if (relativeLastPage % 2 == 0 && (
getPageSequence().getForcePageCount() == Constants.EN_ODD
|| getPageSequence().getForcePageCount() == Constants.EN_END_ON_ODD)) {
forcedLastPageNum++;
}
return forcedLastPageNum;
}
/**
* Indicates whether the column/page at the given index is on the first page of this page sequence.
*
* @return {@code true} if the given part is on the first page of the sequence
*/
boolean isOnFirstPage(int partIndex) {
return pageProvider.isOnFirstPage(partIndex);
}
/**
* Registers the given footnotes so that they can be added to the current page, before any other footnote.
*
* @param headerFootnotes footnotes coming from a repeated table header
*/
public void addTableHeaderFootnotes(List<List<KnuthElement>> headerFootnotes) {
if (tableHeaderFootnotes == null) {
tableHeaderFootnotes = new ArrayList<List<KnuthElement>>();
}
tableHeaderFootnotes.addAll(headerFootnotes);
}
public List<List<KnuthElement>> getTableHeaderFootnotes() {
return getTableFootnotes(tableHeaderFootnotes);
}
/**
* Registers the given footnotes so that they can be added to the current page, after any other footnote.
*
* @param footerFootnotes footnotes coming from a repeated table footer
*/
public void addTableFooterFootnotes(List<List<KnuthElement>> footerFootnotes) {
if (tableFooterFootnotes == null) {
tableFooterFootnotes = new ArrayList<List<KnuthElement>>();
}
tableFooterFootnotes.addAll(footerFootnotes);
}
public List<List<KnuthElement>> getTableFooterFootnotes() {
return getTableFootnotes(tableFooterFootnotes);
}
private List<List<KnuthElement>> getTableFootnotes(List<List<KnuthElement>> tableFootnotes) {
if (tableFootnotes == null) {
List<List<KnuthElement>> emptyList = Collections.emptyList();
return emptyList;
} else {
return tableFootnotes;
}
}
/**
* Clears the footnotes coming from repeated table headers/footers, in order to start
* afresh for a new page.
*/
public void clearTableHeadingFootnotes() {
if (tableHeaderFootnotes != null) {
tableHeaderFootnotes.clear();
}
if (tableFooterFootnotes != null) {
tableFooterFootnotes.clear();
}
}
public void setStartIntrusionAdjustment(int sia) {
startIntrusionAdjustment = sia;
}
public void setEndIntrusionAdjustment(int eia) {
endIntrusionAdjustment = eia;
}
public int getStartIntrusionAdjustment() {
return startIntrusionAdjustment;
}
public int getEndIntrusionAdjustment() {
return endIntrusionAdjustment;
}
public void recordEndOfFloat(int fHeight) {
pageBreaker.handleEndOfFloat(fHeight);
}
public boolean handlingEndOfFloat() {
return pageBreaker.handlingEndOfFloat();
}
public int getOffsetDueToFloat() {
return pageBreaker.getOffsetDueToFloat();
}
public void recordStartOfFloat(int fHeight, int fYOffset) {
pageBreaker.handleStartOfFloat(fHeight, fYOffset);
}
public boolean handlingStartOfFloat() {
return pageBreaker.handlingStartOfFloat();
}
public int getFloatHeight() {
return pageBreaker.getFloatHeight();
}
public int getFloatYOffset() {
return pageBreaker.getFloatYOffset();
}
public int getCurrentColumnWidth() {
int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth();
flowIPD -= startIntrusionAdjustment + endIntrusionAdjustment;
return flowIPD;
}
public void holdFootnotes(List fl, List ll, int tfl, int ifl, boolean fp, boolean nf, int fnfi, int fli,
int fei, MinOptMax fsl, int pfli, int pfei) {
if (fl != null && fl.size() > 0) {
pageBreaker.holdFootnotes(fl, ll, tfl, ifl, fp, nf, fnfi, fli, fei, fsl, pfli, pfei);
}
}
public void retrieveFootnotes(PageBreakingAlgorithm alg) {
pageBreaker.retrieveFootones(alg);
}
}