123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953 |
- /*
- * 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.Iterator;
- import java.util.List;
- import java.util.ListIterator;
-
- import org.apache.fop.area.Block;
- import org.apache.fop.area.BodyRegion;
- import org.apache.fop.area.Footnote;
- import org.apache.fop.area.PageViewport;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.FObj;
- import org.apache.fop.fo.pagination.Region;
- import org.apache.fop.fo.pagination.RegionBody;
- import org.apache.fop.fo.pagination.StaticContent;
- import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
- import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
- import org.apache.fop.layoutmgr.list.ListItemLayoutManager;
- import org.apache.fop.traits.MinOptMax;
-
- /**
- * Handles the breaking of pages in an fo:flow
- */
- public class PageBreaker extends AbstractBreaker {
-
- private PageSequenceLayoutManager pslm;
- private boolean firstPart = true;
- private boolean pageBreakHandled;
- private boolean needColumnBalancing;
- private PageProvider pageProvider;
- private Block separatorArea;
- private boolean spanAllActive;
- private boolean layoutRedone;
- private int previousIndex;
- private boolean handlingStartOfFloat;
- private boolean handlingEndOfFloat;
- private int floatHeight;
- private int floatYOffset;
-
- private List relayedFootnotesList;
- private List relayedLengthList;
- private int relayedTotalFootnotesLength;
- private int relayedInsertedFootnotesLength;
- private boolean relayedFootnotesPending;
- private boolean relayedNewFootnotes;
- private int relayedFirstNewFootnoteIndex;
- private int relayedFootnoteListIndex;
- private int relayedFootnoteElementIndex = -1;
- private MinOptMax relayedFootnoteSeparatorLength;
- private int previousFootnoteListIndex = -2;
- private int previousFootnoteElementIndex = -2;
-
- /**
- * The FlowLayoutManager object, which processes
- * the single fo:flow of the fo:page-sequence
- */
- private FlowLayoutManager childFLM;
-
- private StaticContentLayoutManager footnoteSeparatorLM;
-
- /**
- * Construct page breaker.
- * @param pslm the page sequence layout manager
- */
- public PageBreaker(PageSequenceLayoutManager pslm) {
- this.pslm = pslm;
- this.pageProvider = pslm.getPageProvider();
- this.childFLM = pslm.getLayoutManagerMaker().makeFlowLayoutManager(
- pslm, pslm.getPageSequence().getMainFlow());
- }
-
- /** {@inheritDoc} */
- protected void updateLayoutContext(LayoutContext context) {
- int flowIPD = pslm.getCurrentColumnWidth();
- context.setRefIPD(flowIPD);
- }
-
- /** {@inheritDoc} */
- protected LayoutManager getTopLevelLM() {
- return pslm;
- }
-
- /** {@inheritDoc} */
- protected PageProvider getPageProvider() {
- return pslm.getPageProvider();
- }
-
- /**
- * Starts the page breaking process.
- * @param flowBPD the constant available block-progression-dimension (used for every part)
- */
- void doLayout(int flowBPD) {
- doLayout(flowBPD, false);
- }
-
- /** {@inheritDoc} */
- protected PageBreakingLayoutListener createLayoutListener() {
- return new PageBreakingLayoutListener() {
-
- public void notifyOverflow(int part, int amount, FObj obj) {
- Page p = pageProvider.getPageFromColumnIndex(part);
- RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
- Region.FO_REGION_BODY);
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
- body.getUserAgent().getEventBroadcaster());
-
- boolean canRecover = (body.getOverflow() != Constants.EN_ERROR_IF_OVERFLOW);
- boolean needClip = (body.getOverflow() == Constants.EN_HIDDEN
- || body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
- eventProducer.regionOverflow(this, body.getName(),
- p.getPageViewport().getPageNumberString(),
- amount, needClip, canRecover,
- body.getLocator());
- }
-
- };
- }
-
- /** {@inheritDoc} */
- protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
- needColumnBalancing = false;
- if (childLC.getNextSpan() != Constants.NOT_SET) {
- //Next block list will have a different span.
- nextSequenceStartsOn = childLC.getNextSpan();
- needColumnBalancing = childLC.getNextSpan() == Constants.EN_ALL
- && childLC.getDisableColumnBalancing() == Constants.EN_FALSE;
-
- }
- if (needColumnBalancing) {
- AbstractBreaker.log.debug(
- "Column balancing necessary for the next element list!!!");
- }
- return nextSequenceStartsOn;
- }
-
- /** {@inheritDoc} */
- protected int getNextBlockList(LayoutContext childLC,
- int nextSequenceStartsOn) {
- return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
- }
-
- /** {@inheritDoc} */
- protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
- Position positionAtIPDChange, LayoutManager restartLM, List firstElements) {
- if (!layoutRedone && !handlingFloat()) {
- if (!firstPart) {
- // if this is the first page that will be created by
- // the current BlockSequence, it could have a break
- // condition that must be satisfied;
- // otherwise, we may simply need a new page
- handleBreakTrait(nextSequenceStartsOn);
- }
- firstPart = false;
- pageBreakHandled = true;
-
- pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), pslm.getCurrentPV()
- .getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive);
- }
- return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange,
- restartLM, firstElements);
- }
-
- private boolean containsFootnotes(List contentList, LayoutContext context) {
- boolean containsFootnotes = false;
- if (contentList != null) {
- ListIterator contentListIterator = contentList.listIterator();
- while (contentListIterator.hasNext()) {
- ListElement element = (ListElement) contentListIterator.next();
- if (element instanceof KnuthBlockBox
- && ((KnuthBlockBox) element).hasAnchors()) {
- // element represents a line with footnote citations
- containsFootnotes = true;
- KnuthBlockBox box = (KnuthBlockBox) element;
- List<List<KnuthElement>> footnotes = getFootnoteKnuthElements(childFLM, context,
- box.getFootnoteBodyLMs());
- for (List<KnuthElement> footnote : footnotes) {
- box.addElementList(footnote);
- }
- }
- }
- }
- return containsFootnotes;
- }
-
- public static List<List<KnuthElement>> getFootnoteKnuthElements(FlowLayoutManager flowLM, LayoutContext context,
- List<FootnoteBodyLayoutManager> footnoteBodyLMs) {
- List<List<KnuthElement>> footnotes = new ArrayList<List<KnuthElement>>();
- LayoutContext footnoteContext = LayoutContext.copyOf(context);
- footnoteContext.setStackLimitBP(context.getStackLimitBP());
- footnoteContext.setRefIPD(flowLM.getPSLM()
- .getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
- for (FootnoteBodyLayoutManager fblm : footnoteBodyLMs) {
- fblm.setParent(flowLM);
- fblm.initialize();
- List<KnuthElement> footnote = fblm.getNextKnuthElements(footnoteContext, Constants.EN_START);
- // TODO this does not respect possible stacking constraints between footnotes
- SpaceResolver.resolveElementList(footnote);
- footnotes.add(footnote);
- }
- return footnotes;
- }
-
- private void handleFootnoteSeparator() {
- StaticContent footnoteSeparator;
- footnoteSeparator = pslm.getPageSequence().getStaticContent("xsl-footnote-separator");
- if (footnoteSeparator != null) {
- // the footnote separator can contain page-dependent content such as
- // page numbers or retrieve markers, so its areas cannot simply be
- // obtained now and repeated in each page;
- // we need to know in advance the separator bpd: the actual separator
- // could be different from page to page, but its bpd would likely be
- // always the same
-
- // create a Block area that will contain the separator areas
- separatorArea = new Block();
- separatorArea.setIPD(pslm.getCurrentPV()
- .getRegionReference(Constants.FO_REGION_BODY).getIPD());
- // create a StaticContentLM for the footnote separator
- footnoteSeparatorLM
- = pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
- pslm, footnoteSeparator, separatorArea);
- footnoteSeparatorLM.doLayout();
-
- footnoteSeparatorLength = MinOptMax.getInstance(separatorArea.getBPD());
- }
- }
-
- /** {@inheritDoc} */
- protected List getNextKnuthElements(LayoutContext context, int alignment) {
- List contentList = null;
-
- while (!childFLM.isFinished() && contentList == null) {
- contentList = childFLM.getNextKnuthElements(context, alignment);
- }
-
- // scan contentList, searching for footnotes
- if (containsFootnotes(contentList, context)) {
- // handle the footnote separator
- handleFootnoteSeparator();
- }
-
- return contentList;
- }
-
- /** {@inheritDoc} */
- protected List getNextKnuthElements(LayoutContext context, int alignment,
- Position positionAtIPDChange, LayoutManager restartAtLM) {
- List contentList = null;
-
- do {
- contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange,
- restartAtLM);
- } while (!childFLM.isFinished() && contentList == null);
-
- // scan contentList, searching for footnotes
- if (containsFootnotes(contentList, context)) {
- // handle the footnote separator
- handleFootnoteSeparator();
- }
- return contentList;
- }
-
- /**
- * @return current display alignment
- */
- protected int getCurrentDisplayAlign() {
- return pslm.getCurrentPage().getSimplePageMaster().getRegion(
- Constants.FO_REGION_BODY).getDisplayAlign();
- }
-
- /**
- * @return whether or not this flow has more page break opportunities
- */
- protected boolean hasMoreContent() {
- return !childFLM.isFinished();
- }
-
- /**
- * Adds an area to the flow layout manager
- * @param posIter the position iterator
- * @param context the layout context
- */
- protected void addAreas(PositionIterator posIter, LayoutContext context) {
- if (footnoteSeparatorLM != null) {
- StaticContent footnoteSeparator = pslm.getPageSequence().getStaticContent(
- "xsl-footnote-separator");
- // create a Block area that will contain the separator areas
- separatorArea = new Block();
- separatorArea.setIPD(
- pslm.getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
- // create a StaticContentLM for the footnote separator
- footnoteSeparatorLM = pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
- pslm, footnoteSeparator, separatorArea);
- footnoteSeparatorLM.doLayout();
- }
-
- childFLM.addAreas(posIter, context);
- }
-
- /**
- * {@inheritDoc}
- * This implementation checks whether to trigger column-balancing,
- * or whether to take into account a 'last-page' condition.
- */
- protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList, BlockSequence effectiveList) {
-
- if (needColumnBalancing) {
- //column balancing for the last part
- redoLayout(alg, partCount, originalList, effectiveList);
- return;
- }
-
- if (shouldRedoLayout(partCount)) {
- redoLayout(alg, partCount, originalList, effectiveList);
- return;
- }
-
- //nothing special: just add the areas now
- addAreas(alg, partCount, originalList, effectiveList);
- }
-
- protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList,
- BlockSequence effectiveList) {
- int newStartPos = 0;
- int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
- if (restartPoint > 0 && !layoutRedone) {
- // Add definitive areas for the parts before the
- // restarting point
- addAreas(alg, restartPoint, originalList, effectiveList);
- // Get page break from which we restart
- PageBreakPosition pbp = alg.getPageBreaks().get(restartPoint - 1);
- newStartPos = alg.par.getFirstBoxIndex(pbp.getLeafPos() + 1);
- // Handle page break right here to avoid any side-effects
- if (newStartPos > 0) {
- handleBreakTrait(Constants.EN_PAGE);
- }
- }
- pageBreakHandled = true;
- // Update so the available BPD is reported correctly
- int currentPageNum = pslm.getCurrentPageNum();
- int currentColumn = pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex();
- pageProvider.setStartOfNextElementList(currentPageNum, currentColumn, spanAllActive);
-
- // Make sure we only add the areas we haven't added already
- effectiveList.ignoreAtStart = newStartPos;
- if (!layoutRedone) {
- // Handle special page-master for last page
- setLastPageIndex(currentPageNum);
- // BodyRegion lastBody = pageProvider.getPage(false, currentPageNum).getPageViewport().getBodyRegion();
- pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
- previousIndex = pageProvider.getIndexOfCachedLastPage();
- } else {
- setLastPageIndex(currentPageNum + 1);
- // pslm.setCurrentPage(previousPage);
- pageProvider.discardCacheStartingWith(previousIndex);
- pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
- }
- layoutRedone = true;
- }
-
- /**
- * Restart the algorithm at the break corresponding to the given partCount. Used to
- * re-do the part after the last break in case of either column-balancing or a last
- * page-master.
- */
- private void redoLayout(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList, BlockSequence effectiveList) {
-
- int newStartPos = 0;
- int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
- if (restartPoint > 0) {
- //Add definitive areas for the parts before the
- //restarting point
- addAreas(alg, restartPoint, originalList, effectiveList);
- //Get page break from which we restart
- PageBreakPosition pbp = alg.getPageBreaks().get(restartPoint - 1);
- newStartPos = alg.par.getFirstBoxIndex(pbp.getLeafPos() + 1);
- //Handle page break right here to avoid any side-effects
- if (newStartPos > 0) {
- handleBreakTrait(Constants.EN_PAGE);
- }
- }
-
- AbstractBreaker.log.debug("Restarting at " + restartPoint
- + ", new start position: " + newStartPos);
-
- pageBreakHandled = true;
- //Update so the available BPD is reported correctly
- int currentPageNum = pslm.getCurrentPageNum();
-
- pageProvider.setStartOfNextElementList(currentPageNum,
- pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive);
-
- //Make sure we only add the areas we haven't added already
- effectiveList.ignoreAtStart = newStartPos;
-
- PageBreakingAlgorithm algRestart;
- if (needColumnBalancing) {
- AbstractBreaker.log.debug("Column balancing now!!!");
- AbstractBreaker.log.debug("===================================================");
-
- //Restart last page
- algRestart = new BalancingColumnBreakingAlgorithm(
- getTopLevelLM(), getPageProvider(), createLayoutListener(),
- alignment, Constants.EN_START, footnoteSeparatorLength,
- isPartOverflowRecoveryActivated(),
- pslm.getCurrentPV().getBodyRegion().getColumnCount());
- AbstractBreaker.log.debug("===================================================");
- } else {
- // Handle special page-master for last page
- BodyRegion currentBody = pageProvider.getPage(false, currentPageNum)
- .getPageViewport().getBodyRegion();
-
- setLastPageIndex(currentPageNum);
-
- BodyRegion lastBody = pageProvider.getPage(false, currentPageNum)
- .getPageViewport().getBodyRegion();
- lastBody.getMainReference().setSpans(currentBody.getMainReference().getSpans());
- AbstractBreaker.log.debug("Last page handling now!!!");
- AbstractBreaker.log.debug("===================================================");
- //Restart last page
- algRestart = new PageBreakingAlgorithm(
- getTopLevelLM(), getPageProvider(), createLayoutListener(),
- alg.getAlignment(), alg.getAlignmentLast(),
- footnoteSeparatorLength,
- isPartOverflowRecoveryActivated(), false, false);
- AbstractBreaker.log.debug("===================================================");
- }
-
- int optimalPageCount = algRestart.findBreakingPoints(effectiveList,
- newStartPos,
- 1, true, BreakingAlgorithm.ALL_BREAKS);
- AbstractBreaker.log.debug("restart: optimalPageCount= " + optimalPageCount
- + " pageBreaks.size()= " + algRestart.getPageBreaks().size());
-
- boolean fitsOnePage
- = optimalPageCount <= pslm.getCurrentPV()
- .getBodyRegion().getMainReference().getCurrentSpan().getColumnCount();
-
- if (needColumnBalancing) {
- if (!fitsOnePage) {
- AbstractBreaker.log.warn(
- "Breaking algorithm produced more columns than are available.");
- /* reenable when everything works
- throw new IllegalStateException(
- "Breaking algorithm must not produce more columns than available.");
- */
- }
- } else {
- if (fitsOnePage) {
- //Replace last page
- pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
- } else {
- //Last page-master cannot hold the content.
- //Add areas now...
- addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList);
- //...and add a blank last page
- setLastPageIndex(currentPageNum + 1);
- pslm.setCurrentPage(pslm.makeNewPage(true));
- return;
- }
- }
-
- addAreas(algRestart, optimalPageCount, originalList, effectiveList);
- }
-
- private void setLastPageIndex(int currentPageNum) {
- int lastPageIndex = pslm.getForcedLastPageNum(currentPageNum);
- pageProvider.setLastPageIndex(lastPageIndex);
- }
-
- /** {@inheritDoc} */
- protected void startPart(BlockSequence list, int breakClass) {
- AbstractBreaker.log.debug("startPart() breakClass=" + getBreakClassName(breakClass));
- if (pslm.getCurrentPage() == null) {
- throw new IllegalStateException("curPage must not be null");
- }
- if (!pageBreakHandled) {
-
- //firstPart is necessary because we need the first page before we start the
- //algorithm so we have a BPD and IPD. This may subject to change later when we
- //start handling more complex cases.
- if (!firstPart) {
- // if this is the first page that will be created by
- // the current BlockSequence, it could have a break
- // condition that must be satisfied;
- // otherwise, we may simply need a new page
- handleBreakTrait(breakClass);
- }
- pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(),
- pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(),
- this.spanAllActive);
- }
- pageBreakHandled = false;
- // add static areas and resolve any new id areas
- // finish page and add to area tree
- firstPart = false;
- }
-
- /** {@inheritDoc} */
- protected void handleEmptyContent() {
- pslm.getCurrentPV().getPage().fakeNonEmpty();
- }
-
- /** {@inheritDoc} */
- protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
- // add footnote areas
- if (!pslm.getTableHeaderFootnotes().isEmpty()
- || pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex
- || pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex
- || !pslm.getTableFooterFootnotes().isEmpty()) {
- for (List<KnuthElement> footnote : pslm.getTableHeaderFootnotes()) {
- addFootnoteAreas(footnote);
- }
- // call addAreas() for each FootnoteBodyLM
- for (int i = pbp.footnoteFirstListIndex; i <= pbp.footnoteLastListIndex; i++) {
- List elementList = alg.getFootnoteList(i);
- int firstIndex = (i == pbp.footnoteFirstListIndex
- ? pbp.footnoteFirstElementIndex : 0);
- int lastIndex = (i == pbp.footnoteLastListIndex
- ? pbp.footnoteLastElementIndex : elementList.size() - 1);
- addFootnoteAreas(elementList, firstIndex, lastIndex + 1);
- }
- for (List<KnuthElement> footnote : pslm.getTableFooterFootnotes()) {
- addFootnoteAreas(footnote);
- }
- // set the offset from the top margin
- Footnote parentArea = pslm.getCurrentPV().getBodyRegion().getFootnote();
- int topOffset = pslm.getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD();
- if (separatorArea != null) {
- topOffset -= separatorArea.getBPD();
- }
- parentArea.setTop(topOffset);
- parentArea.setSeparator(separatorArea);
- }
- pslm.getCurrentPV().getCurrentSpan().notifyFlowsFinished();
- pslm.clearTableHeadingFootnotes();
- }
-
- private void addFootnoteAreas(List<KnuthElement> footnote) {
- addFootnoteAreas(footnote, 0, footnote.size());
- }
-
- private void addFootnoteAreas(List<KnuthElement> footnote, int startIndex, int endIndex) {
- SpaceResolver.performConditionalsNotification(footnote, startIndex, endIndex - 1, -1);
- LayoutContext childLC = LayoutContext.newInstance();
- AreaAdditionUtil.addAreas(null, new KnuthPossPosIter(footnote, startIndex, endIndex), childLC);
- }
-
- /** {@inheritDoc} */
- protected FlowLayoutManager getCurrentChildLM() {
- return childFLM;
- }
-
- /** {@inheritDoc} */
- protected void observeElementList(List elementList) {
- ElementListObserver.observe(elementList, "breaker",
- pslm.getFObj().getId());
- }
-
- /**
- * Depending on the kind of break condition, move to next column
- * or page. May need to make an empty page if next page would
- * not have the desired "handedness".
- * @param breakVal - value of break-before or break-after trait.
- */
- private void handleBreakTrait(int breakVal) {
- Page curPage = pslm.getCurrentPage();
- switch (breakVal) {
- case Constants.EN_ALL:
- //break due to span change in multi-column layout
- curPage.getPageViewport().createSpan(true);
- this.spanAllActive = true;
- return;
- case Constants.EN_NONE:
- curPage.getPageViewport().createSpan(false);
- this.spanAllActive = false;
- return;
- case Constants.EN_COLUMN:
- case Constants.EN_AUTO:
- case Constants.EN_PAGE:
- case -1:
- PageViewport pv = curPage.getPageViewport();
-
- //Check if previous page was spanned
- boolean forceNewPageWithSpan = false;
- RegionBody rb = (RegionBody)curPage.getSimplePageMaster().getRegion(
- Constants.FO_REGION_BODY);
- forceNewPageWithSpan
- = (rb.getColumnCount() > 1
- && pv.getCurrentSpan().getColumnCount() == 1);
-
- if (forceNewPageWithSpan) {
- log.trace("Forcing new page with span");
- curPage = pslm.makeNewPage(false);
- curPage.getPageViewport().createSpan(true);
- } else {
- if (breakVal == Constants.EN_PAGE) {
- handleBreakBeforeFollowingPage(breakVal);
- } else {
- if (pv.getCurrentSpan().hasMoreFlows()) {
- log.trace("Moving to next flow");
- pv.getCurrentSpan().moveToNextFlow();
- } else {
- log.trace("Making new page");
- pslm.makeNewPage(false);
- }
- }
- }
- return;
- default:
- handleBreakBeforeFollowingPage(breakVal);
- }
- }
-
- private void handleBreakBeforeFollowingPage(int breakVal) {
- log.debug("handling break-before after page " + pslm.getCurrentPageNum() + " breakVal="
- + getBreakClassName(breakVal));
- if (needBlankPageBeforeNew(breakVal)) {
- log.trace("Inserting blank page");
- /* curPage = */pslm.makeNewPage(true);
- }
- if (needNewPage(breakVal)) {
- log.trace("Making new page");
- /* curPage = */pslm.makeNewPage(false);
- }
- }
-
- /**
- * Check if a blank page is needed to accommodate
- * desired even or odd page number.
- * @param breakVal - value of break-before or break-after trait.
- */
- private boolean needBlankPageBeforeNew(int breakVal) {
- if (breakVal == Constants.EN_PAGE
- || (pslm.getCurrentPage().getPageViewport().getPage().isEmpty())) {
- // any page is OK or we already have an empty page
- return false;
- } else {
- /* IF we are on the kind of page we need, we'll need a new page. */
- if (pslm.getCurrentPageNum() % 2 == 0) { // even page
- return (breakVal == Constants.EN_EVEN_PAGE);
- } else { // odd page
- return (breakVal == Constants.EN_ODD_PAGE);
- }
- }
- }
-
- /**
- * See if need to generate a new page
- * @param breakVal - value of break-before or break-after trait.
- */
- private boolean needNewPage(int breakVal) {
- if (pslm.getCurrentPage().getPageViewport().getPage().isEmpty()) {
- if (breakVal == Constants.EN_PAGE) {
- return false;
- } else if (pslm.getCurrentPageNum() % 2 == 0) { // even page
- return (breakVal == Constants.EN_ODD_PAGE);
- } else { // odd page
- return (breakVal == Constants.EN_EVEN_PAGE);
- }
- } else {
- return true;
- }
- }
-
- protected boolean shouldRedoLayout() {
- return shouldRedoLayout(-1);
- }
-
- protected boolean shouldRedoLayout(int partCount) {
- boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast();
- if (!lastPageMasterDefined && partCount != -1) {
- lastPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1);
- }
- return (!hasMoreContent() && lastPageMasterDefined && !layoutRedone);
- }
-
- protected boolean wasLayoutRedone() {
- return layoutRedone;
- }
-
- protected boolean lastPageHasIPDChange() {
- boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast();
- boolean onlyPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly();
- if (lastPageMasterDefined && !onlyPageMasterDefined) {
- // code not very robust and unable to handle situations were only and last are defined
- int currentIPD = this.pageProvider.getCurrentIPD();
- int lastPageIPD = this.pageProvider.getLastPageIPD();
- if (lastPageIPD != -1 && currentIPD != lastPageIPD) {
- return true;
- }
- }
- return false;
- }
-
- protected boolean handlingStartOfFloat() {
- return handlingStartOfFloat;
- }
-
- protected void handleStartOfFloat(int fHeight, int fYOffset) {
- handlingStartOfFloat = true;
- handlingEndOfFloat = false;
- floatHeight = fHeight;
- floatYOffset = fYOffset;
- childFLM.handleFloatOn();
- }
-
- protected int getFloatHeight() {
- return floatHeight;
- }
-
- protected int getFloatYOffset() {
- return floatYOffset;
- }
-
- protected boolean handlingEndOfFloat() {
- return handlingEndOfFloat;
- }
-
- protected void handleEndOfFloat(int fHeight) {
- handlingEndOfFloat = true;
- handlingStartOfFloat = false;
- floatHeight = fHeight;
- childFLM.handleFloatOff();
- }
-
- protected boolean handlingFloat() {
- return (handlingStartOfFloat || handlingEndOfFloat);
- }
-
- public int getOffsetDueToFloat() {
- handlingEndOfFloat = false;
- return floatHeight + floatYOffset;
- }
-
- protected int handleFloatLayout(PageBreakingAlgorithm alg, int optimalPageCount, BlockSequence blockList,
- LayoutContext childLC) {
- pageBreakHandled = true;
- List firstElements = Collections.EMPTY_LIST;
- KnuthNode floatNode = alg.getBestFloatEdgeNode();
- int floatPosition = floatNode.position;
- KnuthElement floatElem = alg.getElement(floatPosition);
- Position positionAtBreak = floatElem.getPosition();
- if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
- throw new UnsupportedOperationException("Don't know how to restart at position" + positionAtBreak);
- }
- /* Retrieve the original position wrapped into this space position */
- positionAtBreak = positionAtBreak.getPosition();
- addAreas(alg, optimalPageCount, blockList, blockList);
- blockLists.clear();
- blockListIndex = -1;
- LayoutManager restartAtLM = null;
- if (positionAtBreak != null && positionAtBreak.getIndex() == -1) {
- if (positionAtBreak instanceof ListItemLayoutManager.ListItemPosition) {
- restartAtLM = positionAtBreak.getLM();
- } else {
- Position position;
- Iterator iter = blockList.listIterator(floatPosition + 1);
- do {
- KnuthElement nextElement = (KnuthElement) iter.next();
- position = nextElement.getPosition();
- } while (position == null || position instanceof SpaceResolver.SpaceHandlingPosition
- || position instanceof SpaceResolver.SpaceHandlingBreakPosition
- && position.getPosition().getIndex() == -1);
- LayoutManager surroundingLM = positionAtBreak.getLM();
- while (position.getLM() != surroundingLM) {
- position = position.getPosition();
- }
- restartAtLM = position.getPosition().getLM();
- }
- }
- int nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, positionAtBreak,
- restartAtLM, firstElements);
- return nextSequenceStartsOn;
- }
-
- protected void addAreasForFloats(PageBreakingAlgorithm alg, int startPart, int partCount,
- BlockSequence originalList, BlockSequence effectiveList, final LayoutContext childLC,
- int lastBreak, int startElementIndex, int endElementIndex) {
- FloatPosition pbp = alg.getFloatPosition();
-
- // Check the last break position for forced breaks
- int lastBreakClass;
- if (startElementIndex == 0) {
- lastBreakClass = effectiveList.getStartOn();
- } else {
- ListElement lastBreakElement = effectiveList.getElement(endElementIndex);
- if (lastBreakElement.isPenalty()) {
- KnuthPenalty pen = (KnuthPenalty) lastBreakElement;
- if (pen.getPenalty() == KnuthPenalty.INFINITE) {
- /**
- * That means that there was a keep.within-page="always", but that
- * it's OK to break at a column. TODO The break class is being
- * abused to implement keep.within-column and keep.within-page.
- * This is very misleading and must be revised.
- */
- lastBreakClass = Constants.EN_COLUMN;
- } else {
- lastBreakClass = pen.getBreakClass();
- }
- } else {
- lastBreakClass = Constants.EN_COLUMN;
- }
- }
-
- // the end of the new part
- endElementIndex = pbp.getLeafPos();
-
- // ignore the first elements added by the
- // PageSequenceLayoutManager
- startElementIndex += (startElementIndex == 0) ? effectiveList.ignoreAtStart : 0;
-
- log.debug("PLM> part: " + (startPart + partCount + 1) + ", start at pos " + startElementIndex
- + ", break at pos " + endElementIndex + ", break class = "
- + getBreakClassName(lastBreakClass));
-
- startPart(effectiveList, lastBreakClass);
-
- int displayAlign = getCurrentDisplayAlign();
-
- // The following is needed by SpaceResolver.performConditionalsNotification()
- // further down as there may be important Position elements in the element list trailer
- int notificationEndElementIndex = endElementIndex;
-
- // ignore the last elements added by the
- // PageSequenceLayoutManager
- endElementIndex -= (endElementIndex == (originalList.size() - 1)) ? effectiveList.ignoreAtEnd : 0;
-
- // ignore the last element in the page if it is a KnuthGlue
- // object
- if (((KnuthElement) effectiveList.get(endElementIndex)).isGlue()) {
- endElementIndex--;
- }
-
- // ignore KnuthGlue and KnuthPenalty objects
- // at the beginning of the line
- startElementIndex = alg.par.getFirstBoxIndex(startElementIndex);
-
- if (startElementIndex <= endElementIndex) {
- if (log.isDebugEnabled()) {
- log.debug(" addAreas from " + startElementIndex + " to " + endElementIndex);
- }
- // set the space adjustment ratio
- childLC.setSpaceAdjust(pbp.bpdAdjust);
- // add space before if display-align is center or bottom
- // add space after if display-align is distribute and
- // this is not the last page
- if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
- childLC.setSpaceBefore(pbp.difference / 2);
- } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
- childLC.setSpaceBefore(pbp.difference);
- }
-
- // Handle SpaceHandling(Break)Positions, see SpaceResolver!
- SpaceResolver.performConditionalsNotification(effectiveList, startElementIndex,
- notificationEndElementIndex, lastBreak);
- // Add areas of lines, in the current page, before the float or during float
- addAreas(new KnuthPossPosIter(effectiveList, startElementIndex, endElementIndex + 1), childLC);
- // add areas for the float, if applicable
- if (alg.handlingStartOfFloat()) {
- for (int k = startElementIndex; k < endElementIndex + 1; k++) {
- ListElement le = effectiveList.getElement(k);
- if (le instanceof KnuthBlockBox) {
- KnuthBlockBox kbb = (KnuthBlockBox) le;
- for (FloatContentLayoutManager fclm : kbb.getFloatContentLMs()) {
- fclm.processAreas(childLC);
- int floatHeight = fclm.getFloatHeight();
- int floatYOffset = fclm.getFloatYOffset();
- PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
- pslm.recordStartOfFloat(floatHeight, floatYOffset);
- }
- }
- }
- }
- if (alg.handlingEndOfFloat()) {
- PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
- pslm.setEndIntrusionAdjustment(0);
- pslm.setStartIntrusionAdjustment(0);
- int effectiveFloatHeight = alg.getFloatHeight();
- pslm.recordEndOfFloat(effectiveFloatHeight);
- }
- if (alg.handlingFloat()) {
- PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
- alg.relayFootnotes(pslm);
- }
- } else {
- // no content for this part
- handleEmptyContent();
- }
-
- pageBreakHandled = true;
- }
-
- 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) {
- relayedFootnotesList = fl;
- relayedLengthList = ll;
- relayedTotalFootnotesLength = tfl;
- relayedInsertedFootnotesLength = ifl;
- relayedFootnotesPending = fp;
- relayedNewFootnotes = nf;
- relayedFirstNewFootnoteIndex = fnfi;
- relayedFootnoteListIndex = fli;
- relayedFootnoteElementIndex = fei;
- relayedFootnoteSeparatorLength = fsl;
- previousFootnoteListIndex = pfli;
- previousFootnoteElementIndex = pfei;
- }
-
- public void retrieveFootones(PageBreakingAlgorithm alg) {
- if (relayedFootnotesList != null && relayedFootnotesList.size() > 0) {
- alg.loadFootnotes(relayedFootnotesList, relayedLengthList, relayedTotalFootnotesLength,
- relayedInsertedFootnotesLength, relayedFootnotesPending, relayedNewFootnotes,
- relayedFirstNewFootnoteIndex, relayedFootnoteListIndex, relayedFootnoteElementIndex,
- relayedFootnoteSeparatorLength, previousFootnoteListIndex,
- previousFootnoteElementIndex);
- relayedFootnotesList = null;
- relayedLengthList = null;
- relayedTotalFootnotesLength = 0;
- relayedInsertedFootnotesLength = 0;
- relayedFootnotesPending = false;
- relayedNewFootnotes = false;
- relayedFirstNewFootnoteIndex = 0;
- relayedFootnoteListIndex = 0;
- relayedFootnoteElementIndex = -1;
- relayedFootnoteSeparatorLength = null;
- }
- }
- }
|