1 17 18 19 20 package org.apache.fop.layoutmgr; 21 22 import org.apache.commons.logging.Log; 23 import org.apache.commons.logging.LogFactory; 24 import org.apache.fop.apps.FOPException; 25 import org.apache.fop.datatypes.Numeric; 26 27 import org.apache.fop.area.AreaTreeHandler; 28 import org.apache.fop.area.AreaTreeModel; 29 import org.apache.fop.area.Block; 30 import org.apache.fop.area.Footnote; 31 import org.apache.fop.area.PageViewport; 32 import org.apache.fop.area.LineArea; 33 import org.apache.fop.area.Resolvable; 34 35 import org.apache.fop.fo.Constants; 36 import org.apache.fop.fo.FONode; 37 import org.apache.fop.fo.FObj; 38 import org.apache.fop.fo.flow.Marker; 39 import org.apache.fop.fo.flow.RetrieveMarker; 40 41 import org.apache.fop.fo.pagination.Flow; 42 import org.apache.fop.fo.pagination.PageSequence; 43 import org.apache.fop.fo.pagination.Region; 44 import org.apache.fop.fo.pagination.RegionBody; 45 import org.apache.fop.fo.pagination.SideRegion; 46 import org.apache.fop.fo.pagination.SimplePageMaster; 47 import org.apache.fop.fo.pagination.StaticContent; 48 import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; 49 import org.apache.fop.layoutmgr.inline.ContentLayoutManager; 50 51 import org.apache.fop.traits.MinOptMax; 52 53 import java.util.LinkedList ; 54 import java.util.List ; 55 import java.util.ListIterator ; 56 57 62 public class PageSequenceLayoutManager extends AbstractLayoutManager { 63 64 private static Log log = LogFactory.getLog(PageSequenceLayoutManager.class); 65 66 70 private AreaTreeHandler areaTreeHandler; 71 72 76 private PageSequence pageSeq; 77 78 private PageProvider pageProvider; 79 80 84 private Page curPage = null; 85 86 90 private FlowLayoutManager childFLM = null; 91 92 private int startPageNum = 0; 93 private int currentPageNum = 0; 94 95 private Block separatorArea = null; 96 97 103 public PageSequenceLayoutManager(AreaTreeHandler ath, PageSequence pseq) { 104 super(pseq); 105 this.areaTreeHandler = ath; 106 this.pageSeq = pseq; 107 this.pageProvider = new PageProvider(this.pageSeq); 108 } 109 110 114 public LayoutManagerMaker getLayoutManagerMaker() { 115 return areaTreeHandler.getLayoutManagerMaker(); 116 } 117 118 119 public PageProvider getPageProvider() { 120 return this.pageProvider; 121 } 122 123 129 public void activateLayout() { 130 startPageNum = pageSeq.getStartingPageNumber(); 131 currentPageNum = startPageNum - 1; 132 133 LineArea title = null; 134 135 if (pageSeq.getTitleFO() != null) { 136 try { 137 ContentLayoutManager clm = getLayoutManagerMaker(). 138 makeContentLayoutManager(this, pageSeq.getTitleFO()); 139 title = (LineArea) clm.getParentArea(null); 140 } catch (IllegalStateException e) { 141 } 143 } 144 145 areaTreeHandler.getAreaTreeModel().startPageSequence(title); 146 log.debug("Starting layout"); 147 148 curPage = makeNewPage(false, false); 149 150 151 Flow mainFlow = pageSeq.getMainFlow(); 152 childFLM = getLayoutManagerMaker(). 153 makeFlowLayoutManager(this, mainFlow); 154 155 PageBreaker breaker = new PageBreaker(this); 156 int flowBPD = (int)getCurrentPV().getBodyRegion().getRemainingBPD(); 157 breaker.doLayout(flowBPD); 158 159 finishPage(); 160 } 161 162 165 public void finishPageSequence() { 166 if (!pageSeq.getId().equals("")) { 167 areaTreeHandler.signalIDProcessed(pageSeq.getId()); 168 } 169 170 pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum, 171 (currentPageNum - startPageNum) + 1); 172 areaTreeHandler.notifyPageSequenceFinished(pageSeq, 173 (currentPageNum - startPageNum) + 1); 174 pageSeq.releasePageSequence(); 175 log.debug("Ending layout"); 176 } 177 178 179 private class PageBreaker extends AbstractBreaker { 180 181 private PageSequenceLayoutManager pslm; 182 private boolean firstPart = true; 183 private boolean pageBreakHandled; 184 private boolean needColumnBalancing; 185 186 private StaticContentLayoutManager footnoteSeparatorLM = null; 187 188 public PageBreaker(PageSequenceLayoutManager pslm) { 189 this.pslm = pslm; 190 } 191 192 193 protected void updateLayoutContext(LayoutContext context) { 194 int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); 195 context.setRefIPD(flowIPD); 196 } 197 198 199 protected LayoutManager getTopLevelLM() { 200 return pslm; 201 } 202 203 204 protected PageSequenceLayoutManager.PageProvider getPageProvider() { 205 return pageProvider; 206 } 207 208 211 protected PageBreakingLayoutListener getLayoutListener() { 212 return new PageBreakingLayoutListener() { 213 214 public void notifyOverflow(int part, FObj obj) { 215 Page p = pageProvider.getPage( 216 false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST); 217 RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion( 218 Region.FO_REGION_BODY); 219 String err = FONode.decorateWithContextInfo( 220 "Content of the region-body on page " 221 + p.getPageViewport().getPageNumberString() 222 + " overflows the available area in block-progression dimension.", 223 obj); 224 if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) { 225 throw new RuntimeException (err); 226 } else { 227 PageSequenceLayoutManager.log.warn(err); 228 } 229 } 230 231 }; 232 } 233 234 235 protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) { 236 needColumnBalancing = false; 237 if (childLC.getNextSpan() != Constants.NOT_SET) { 238 nextSequenceStartsOn = childLC.getNextSpan(); 240 needColumnBalancing = (childLC.getNextSpan() == Constants.EN_ALL); 241 } 242 if (needColumnBalancing) { 243 AbstractBreaker.log.debug( 244 "Column balancing necessary for the next element list!!!"); 245 } 246 return nextSequenceStartsOn; 247 } 248 249 250 protected int getNextBlockList(LayoutContext childLC, 251 int nextSequenceStartsOn, 252 List blockLists) { 253 if (!firstPart) { 254 handleBreakTrait(nextSequenceStartsOn); 259 } 260 firstPart = false; 261 pageBreakHandled = true; 262 pageProvider.setStartOfNextElementList(currentPageNum, 263 getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); 264 return super.getNextBlockList(childLC, nextSequenceStartsOn, blockLists); 265 } 266 267 268 protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) { 269 LinkedList contentList = null; 270 271 while (!childFLM.isFinished() && contentList == null) { 272 contentList = childFLM.getNextKnuthElements(context, alignment); 273 } 274 275 boolean bFootnotesPresent = false; 277 if (contentList != null) { 278 ListIterator contentListIterator = contentList.listIterator(); 279 while (contentListIterator.hasNext()) { 280 ListElement element = (ListElement) contentListIterator.next(); 281 if (element instanceof KnuthBlockBox 282 && ((KnuthBlockBox) element).hasAnchors()) { 283 bFootnotesPresent = true; 285 LayoutContext footnoteContext = new LayoutContext(context); 286 footnoteContext.setStackLimit(context.getStackLimit()); 287 footnoteContext.setRefIPD(getCurrentPV() 288 .getRegionReference(Constants.FO_REGION_BODY).getIPD()); 289 LinkedList footnoteBodyLMs = ((KnuthBlockBox) element).getFootnoteBodyLMs(); 290 ListIterator footnoteBodyIterator = footnoteBodyLMs.listIterator(); 291 while (footnoteBodyIterator.hasNext()) { 294 FootnoteBodyLayoutManager fblm 295 = (FootnoteBodyLayoutManager) footnoteBodyIterator.next(); 296 fblm.setParent(childFLM); 297 fblm.initialize(); 298 ((KnuthBlockBox) element).addElementList( 299 fblm.getNextKnuthElements(footnoteContext, alignment)); 300 } 301 } 302 } 303 } 304 305 StaticContent footnoteSeparator; 307 if (bFootnotesPresent 308 && (footnoteSeparator = pageSeq.getStaticContent( 309 "xsl-footnote-separator")) != null) { 310 317 separatorArea = new Block(); 319 separatorArea.setIPD(pslm.getCurrentPV() 320 .getRegionReference(Constants.FO_REGION_BODY).getIPD()); 321 footnoteSeparatorLM = (StaticContentLayoutManager) 323 getLayoutManagerMaker().makeStaticContentLayoutManager( 324 pslm, footnoteSeparator, separatorArea); 325 footnoteSeparatorLM.doLayout(); 326 327 footnoteSeparatorLength = new MinOptMax(separatorArea.getBPD()); 328 } 329 return contentList; 330 } 331 332 protected int getCurrentDisplayAlign() { 333 return curPage.getSimplePageMaster().getRegion( 334 Constants.FO_REGION_BODY).getDisplayAlign(); 335 } 336 337 protected boolean hasMoreContent() { 338 return !childFLM.isFinished(); 339 } 340 341 protected void addAreas(PositionIterator posIter, LayoutContext context) { 342 if (footnoteSeparatorLM != null) { 343 StaticContent footnoteSeparator = pageSeq.getStaticContent( 344 "xsl-footnote-separator"); 345 separatorArea = new Block(); 347 separatorArea.setIPD( 348 getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD()); 349 footnoteSeparatorLM = (StaticContentLayoutManager) 351 getLayoutManagerMaker().makeStaticContentLayoutManager( 352 pslm, footnoteSeparator, separatorArea); 353 footnoteSeparatorLM.doLayout(); 354 } 355 356 childFLM.addAreas(posIter, context); 357 } 358 359 protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 360 BlockSequence originalList, BlockSequence effectiveList) { 361 if (needColumnBalancing) { 362 doPhase3WithColumnBalancing(alg, partCount, originalList, effectiveList); 363 } else { 364 if (!hasMoreContent() && pageSeq.hasPagePositionLast()) { 365 doPhase3WithLastPage(alg, partCount, originalList, effectiveList); 367 } else { 368 addAreas(alg, partCount, originalList, effectiveList); 370 } 371 } 372 } 373 374 private void doPhase3WithLastPage(PageBreakingAlgorithm alg, int partCount, 375 BlockSequence originalList, BlockSequence effectiveList) { 376 int newStartPos; 377 int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount); 378 if (restartPoint > 0) { 379 addAreas(alg, restartPoint, originalList, effectiveList); 381 PageBreakPosition pbp = (PageBreakPosition) 383 alg.getPageBreaks().get(restartPoint - 1); 384 newStartPos = pbp.getLeafPos(); 385 if (newStartPos > 0) { 387 handleBreakTrait(EN_PAGE); 388 } 389 } else { 390 newStartPos = 0; 391 } 392 AbstractBreaker.log.debug("Last page handling now!!!"); 393 AbstractBreaker.log.debug("==================================================="); 394 AbstractBreaker.log.debug("Restarting at " + restartPoint 395 + ", new start position: " + newStartPos); 396 397 pageBreakHandled = true; 398 pageProvider.setStartOfNextElementList(currentPageNum, 400 getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); 401 pageProvider.setLastPageIndex(currentPageNum); 402 403 PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm( 405 getTopLevelLM(), 406 getPageProvider(), getLayoutListener(), 407 alg.getAlignment(), alg.getAlignmentLast(), 408 footnoteSeparatorLength, 409 isPartOverflowRecoveryActivated(), false, false); 410 int iOptPageCount = algRestart.findBreakingPoints(effectiveList, 412 newStartPos, 413 1, true, BreakingAlgorithm.ALL_BREAKS); 414 AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount 415 + " pageBreaks.size()= " + algRestart.getPageBreaks().size()); 416 boolean replaceLastPage 417 = iOptPageCount <= getCurrentPV().getBodyRegion().getColumnCount(); 418 if (replaceLastPage) { 419 420 pslm.curPage = pageProvider.getPage(false, currentPageNum); 422 effectiveList.ignoreAtStart = newStartPos; 424 addAreas(algRestart, iOptPageCount, originalList, effectiveList); 425 } else { 426 effectiveList.ignoreAtStart = newStartPos; 427 addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList); 428 pageProvider.setLastPageIndex(currentPageNum + 1); 430 pslm.curPage = makeNewPage(true, true); 431 } 432 AbstractBreaker.log.debug("==================================================="); 433 } 434 435 private void doPhase3WithColumnBalancing(PageBreakingAlgorithm alg, int partCount, 436 BlockSequence originalList, BlockSequence effectiveList) { 437 AbstractBreaker.log.debug("Column balancing now!!!"); 438 AbstractBreaker.log.debug("==================================================="); 439 int newStartPos; 440 int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount); 441 if (restartPoint > 0) { 442 addAreas(alg, restartPoint, originalList, effectiveList); 444 PageBreakPosition pbp = (PageBreakPosition) 446 alg.getPageBreaks().get(restartPoint - 1); 447 newStartPos = pbp.getLeafPos(); 448 if (newStartPos > 0) { 450 handleBreakTrait(EN_PAGE); 451 } 452 } else { 453 newStartPos = 0; 454 } 455 AbstractBreaker.log.debug("Restarting at " + restartPoint 456 + ", new start position: " + newStartPos); 457 458 pageBreakHandled = true; 459 pageProvider.setStartOfNextElementList(currentPageNum, 461 getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); 462 463 PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm( 465 getTopLevelLM(), 466 getPageProvider(), getLayoutListener(), 467 alignment, Constants.EN_START, footnoteSeparatorLength, 468 isPartOverflowRecoveryActivated(), 469 getCurrentPV().getBodyRegion().getColumnCount()); 470 int iOptPageCount = algRestart.findBreakingPoints(effectiveList, 472 newStartPos, 473 1, true, BreakingAlgorithm.ALL_BREAKS); 474 AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount 475 + " pageBreaks.size()= " + algRestart.getPageBreaks().size()); 476 if (iOptPageCount > getCurrentPV().getBodyRegion().getColumnCount()) { 477 AbstractBreaker.log.warn( 478 "Breaking algorithm produced more columns than are available."); 479 483 } 484 effectiveList.ignoreAtStart = newStartPos; 486 addAreas(algRestart, iOptPageCount, originalList, effectiveList); 487 AbstractBreaker.log.debug("==================================================="); 488 } 489 490 protected void startPart(BlockSequence list, int breakClass) { 491 AbstractBreaker.log.debug("startPart() breakClass=" + breakClass); 492 if (curPage == null) { 493 throw new IllegalStateException ("curPage must not be null"); 494 } 495 if (!pageBreakHandled) { 496 497 if (!firstPart) { 501 handleBreakTrait(breakClass); 506 } 507 pageProvider.setStartOfNextElementList(currentPageNum, 508 getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); 509 } 510 pageBreakHandled = false; 511 firstPart = false; 514 } 515 516 517 protected void handleEmptyContent() { 518 getCurrentPV().getPage().fakeNonEmpty(); 519 } 520 521 protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { 522 if (pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex 524 || pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex) { 525 for (int i = pbp.footnoteFirstListIndex; i <= pbp.footnoteLastListIndex; i++) { 527 LinkedList elementList = alg.getFootnoteList(i); 528 int firstIndex = (i == pbp.footnoteFirstListIndex 529 ? pbp.footnoteFirstElementIndex : 0); 530 int lastIndex = (i == pbp.footnoteLastListIndex 531 ? pbp.footnoteLastElementIndex : elementList.size() - 1); 532 533 SpaceResolver.performConditionalsNotification(elementList, 534 firstIndex, lastIndex, -1); 535 LayoutContext childLC = new LayoutContext(0); 536 AreaAdditionUtil.addAreas(null, 537 new KnuthPossPosIter(elementList, firstIndex, lastIndex + 1), 538 childLC); 539 } 540 Footnote parentArea = (Footnote) getCurrentPV().getBodyRegion().getFootnote(); 542 int topOffset = (int) getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD(); 543 if (separatorArea != null) { 544 topOffset -= separatorArea.getBPD(); 545 } 546 parentArea.setTop(topOffset); 547 parentArea.setSeparator(separatorArea); 548 } 549 getCurrentPV().getCurrentSpan().notifyFlowsFinished(); 550 } 551 552 protected LayoutManager getCurrentChildLM() { 553 return childFLM; 554 } 555 556 557 protected void observeElementList(List elementList) { 558 ElementListObserver.observe(elementList, "breaker", 559 ((PageSequence)pslm.getFObj()).getId()); 560 } 561 562 } 563 564 568 public Page getCurrentPage() { 569 return curPage; 570 } 571 572 579 580 584 public PageSequenceLayoutManager getPSLM() { 585 return this; 586 } 587 588 595 public PageViewport getFirstPVWithID(String idref) { 596 List list = areaTreeHandler.getPageViewportsContainingID(idref); 597 if (list != null && list.size() > 0) { 598 return (PageViewport) list.get(0); 599 } 600 return null; 601 } 602 603 610 public PageViewport getLastPVWithID(String idref) { 611 List list = areaTreeHandler.getPageViewportsContainingID(idref); 612 if (list != null && list.size() > 0) { 613 return (PageViewport) list.get(list.size() - 1); 614 } 615 return null; 616 } 617 618 626 public void addIDToPage(String id) { 627 if (id != null && id.length() > 0) { 628 areaTreeHandler.associateIDWithPageViewport(id, curPage.getPageViewport()); 629 } 630 } 631 632 639 public boolean associateLayoutManagerID(String id) { 640 if (log.isDebugEnabled()) { 641 log.debug("associateLayoutManagerID(" + id + ")"); 642 } 643 if (!areaTreeHandler.alreadyResolvedID(id)) { 644 areaTreeHandler.signalPendingID(id); 645 return false; 646 } else { 647 return true; 648 } 649 } 650 651 656 public void notifyEndOfLayout(String id) { 657 areaTreeHandler.signalIDProcessed(id); 658 } 659 660 676 public void addUnresolvedArea(String id, Resolvable res) { 677 curPage.getPageViewport().addUnresolvedIDRef(id, res); 678 areaTreeHandler.addUnresolvedIDRef(id, curPage.getPageViewport()); 679 } 680 681 698 public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) { 699 AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); 700 String name = rm.getRetrieveClassName(); 701 int pos = rm.getRetrievePosition(); 702 int boundary = rm.getRetrieveBoundary(); 703 704 Marker mark = (Marker)getCurrentPV().getMarker(name, pos); 706 if (mark == null && boundary != EN_PAGE) { 707 boolean doc = boundary == EN_DOCUMENT; 710 int seq = areaTreeModel.getPageSequenceCount(); 711 int page = areaTreeModel.getPageCount(seq) - 1; 712 while (page < 0 && doc && seq > 1) { 713 seq--; 714 page = areaTreeModel.getPageCount(seq) - 1; 715 } 716 while (page >= 0) { 717 PageViewport pv = areaTreeModel.getPage(seq, page); 718 mark = (Marker)pv.getMarker(name, Constants.EN_LEWP); 719 if (mark != null) { 720 break; 721 } 722 page--; 723 if (page < 0 && doc && seq > 1) { 724 seq--; 725 page = areaTreeModel.getPageCount(seq) - 1; 726 } 727 } 728 } 729 730 if (mark == null) { 731 log.debug("found no marker with name: " + name); 732 return null; 733 } else { 734 rm.bindMarker(mark); 735 return rm; 736 } 737 } 738 739 private Page makeNewPage(boolean bIsBlank, boolean bIsLast) { 740 if (curPage != null) { 741 finishPage(); 742 } 743 744 currentPageNum++; 745 746 curPage = pageProvider.getPage(bIsBlank, 747 currentPageNum, PageProvider.RELTO_PAGE_SEQUENCE); 748 749 if (log.isDebugEnabled()) { 750 log.debug("[" + curPage.getPageViewport().getPageNumberString() 751 + (bIsBlank ? "*" : "") + "]"); 752 } 753 754 addIDToPage(pageSeq.getId()); 755 return curPage; 756 } 757 758 private void layoutSideRegion(int regionID) { 759 SideRegion reg = (SideRegion)curPage.getSimplePageMaster().getRegion(regionID); 760 if (reg == null) { 761 return; 762 } 763 StaticContent sc = pageSeq.getStaticContent(reg.getRegionName()); 764 if (sc == null) { 765 return; 766 } 767 768 StaticContentLayoutManager lm = (StaticContentLayoutManager) 769 getLayoutManagerMaker().makeStaticContentLayoutManager( 770 this, sc, reg); 771 lm.doLayout(); 772 } 773 774 private void finishPage() { 775 curPage.getPageViewport().dumpMarkers(); 776 layoutSideRegion(FO_REGION_BEFORE); 778 layoutSideRegion(FO_REGION_AFTER); 779 layoutSideRegion(FO_REGION_START); 780 layoutSideRegion(FO_REGION_END); 781 782 areaTreeHandler.tryIDResolution(curPage.getPageViewport()); 785 areaTreeHandler.getAreaTreeModel().addPage(curPage.getPageViewport()); 787 if (log.isDebugEnabled()) { 788 log.debug("page finished: " + curPage.getPageViewport().getPageNumberString() 789 + ", current num: " + currentPageNum); 790 } 791 curPage = null; 792 } 793 794 800 private void handleBreakTrait(int breakVal) { 801 if (breakVal == Constants.EN_ALL) { 802 curPage.getPageViewport().createSpan(true); 804 return; 805 } else if (breakVal == Constants.EN_NONE) { 806 curPage.getPageViewport().createSpan(false); 807 return; 808 } else if (breakVal == Constants.EN_COLUMN || breakVal <= 0) { 809 PageViewport pv = curPage.getPageViewport(); 810 811 boolean forceNewPageWithSpan = false; 813 RegionBody rb = (RegionBody)curPage.getSimplePageMaster().getRegion( 814 Constants.FO_REGION_BODY); 815 if (breakVal < 0 816 && rb.getColumnCount() > 1 817 && pv.getCurrentSpan().getColumnCount() == 1) { 818 forceNewPageWithSpan = true; 819 } 820 821 if (forceNewPageWithSpan) { 822 curPage = makeNewPage(false, false); 823 curPage.getPageViewport().createSpan(true); 824 } else if (pv.getCurrentSpan().hasMoreFlows()) { 825 pv.getCurrentSpan().moveToNextFlow(); 826 } else { 827 curPage = makeNewPage(false, false); 828 } 829 return; 830 } 831 log.debug("handling break-before after page " + currentPageNum 832 + " breakVal=" + breakVal); 833 if (needBlankPageBeforeNew(breakVal)) { 834 curPage = makeNewPage(true, false); 835 } 836 if (needNewPage(breakVal)) { 837 curPage = makeNewPage(false, false); 838 } 839 } 840 841 846 private boolean needBlankPageBeforeNew(int breakVal) { 847 if (breakVal == Constants.EN_PAGE || (curPage.getPageViewport().getPage().isEmpty())) { 848 return false; 850 } else { 851 852 if (currentPageNum % 2 == 0) { return (breakVal == Constants.EN_EVEN_PAGE); 854 } else { return (breakVal == Constants.EN_ODD_PAGE); 856 } 857 } 858 } 859 860 864 private boolean needNewPage(int breakVal) { 865 if (curPage.getPageViewport().getPage().isEmpty()) { 866 if (breakVal == Constants.EN_PAGE) { 867 return false; 868 } else if (currentPageNum % 2 == 0) { return (breakVal == Constants.EN_ODD_PAGE); 870 } else { return (breakVal == Constants.EN_EVEN_PAGE); 872 } 873 } else { 874 return true; 875 } 876 } 877 878 879 888 public class PageProvider { 889 890 private Log log = LogFactory.getLog(PageProvider.class); 891 892 893 public static final int RELTO_PAGE_SEQUENCE = 0; 894 895 public static final int RELTO_CURRENT_ELEMENT_LIST = 1; 896 897 private int startPageOfPageSequence; 898 private int startPageOfCurrentElementList; 899 private int startColumnOfCurrentElementList; 900 private List cachedPages = new java.util.ArrayList (); 901 902 private int lastPageIndex = -1; 903 private int indexOfCachedLastPage = -1; 904 905 private int lastRequestedIndex = -1; 907 private int lastReportedBPD = -1; 908 909 913 public PageProvider(PageSequence ps) { 914 this.startPageOfPageSequence = ps.getStartingPageNumber(); 915 } 916 917 923 public void setStartOfNextElementList(int startPage, int startColumn) { 924 log.debug("start of the next element list is:" 925 + " page=" + startPage + " col=" + startColumn); 926 this.startPageOfCurrentElementList = startPage - startPageOfPageSequence + 1; 927 this.startColumnOfCurrentElementList = startColumn; 928 this.lastRequestedIndex = -1; 930 this.lastReportedBPD = -1; 931 } 932 933 938 public void setLastPageIndex(int index) { 939 this.lastPageIndex = index; 940 } 941 942 949 public int getAvailableBPD(int index) { 950 if (this.lastRequestedIndex == index) { 952 if (log.isTraceEnabled()) { 953 log.trace("getAvailableBPD(" + index + ") -> (cached) " + lastReportedBPD); 954 } 955 return this.lastReportedBPD; 956 } 957 int c = index; 958 int pageIndex = 0; 959 int colIndex = startColumnOfCurrentElementList; 960 Page page = getPage( 961 false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); 962 while (c > 0) { 963 colIndex++; 964 if (colIndex >= page.getPageViewport().getCurrentSpan().getColumnCount()) { 965 colIndex = 0; 966 pageIndex++; 967 page = getPage( 968 false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); 969 } 970 c--; 971 } 972 this.lastRequestedIndex = index; 973 this.lastReportedBPD = page.getPageViewport().getBodyRegion().getRemainingBPD(); 974 if (log.isTraceEnabled()) { 975 log.trace("getAvailableBPD(" + index + ") -> " + lastReportedBPD); 976 } 977 return this.lastReportedBPD; 978 } 979 980 986 public int getStartingPartIndexForLastPage(int partCount) { 987 int result = 0; 988 int idx = 0; 989 int pageIndex = 0; 990 int colIndex = startColumnOfCurrentElementList; 991 Page page = getPage( 992 false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); 993 while (idx < partCount) { 994 if ((colIndex >= page.getPageViewport().getCurrentSpan().getColumnCount())) { 995 colIndex = 0; 996 pageIndex++; 997 page = getPage( 998 false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); 999 result = idx; 1000 } 1001 colIndex++; 1002 idx++; 1003 } 1004 return result; 1005 } 1006 1007 1015 public Page getPage(boolean isBlank, int index, int relativeTo) { 1016 if (relativeTo == RELTO_PAGE_SEQUENCE) { 1017 return getPage(isBlank, index); 1018 } else if (relativeTo == RELTO_CURRENT_ELEMENT_LIST) { 1019 int effIndex = startPageOfCurrentElementList + index; 1020 effIndex += startPageOfPageSequence - 1; 1021 return getPage(isBlank, effIndex); 1022 } else { 1023 throw new IllegalArgumentException ( 1024 "Illegal value for relativeTo: " + relativeTo); 1025 } 1026 } 1027 1028 private Page getPage(boolean isBlank, int index) { 1029 boolean isLastPage = (lastPageIndex >= 0) && (index == lastPageIndex); 1030 if (log.isTraceEnabled()) { 1031 log.trace("getPage(" + index + " " + (isBlank ? "blank" : "non-blank") 1032 + (isLastPage ? " <LAST>" : "") + ")"); 1033 } 1034 int intIndex = index - startPageOfPageSequence; 1035 if (log.isTraceEnabled()) { 1036 if (isBlank) { 1037 log.trace("blank page requested: " + index); 1038 } 1039 if (isLastPage) { 1040 log.trace("last page requested: " + index); 1041 } 1042 } 1043 while (intIndex >= cachedPages.size()) { 1044 if (log.isTraceEnabled()) { 1045 log.trace("Caching " + index); 1046 } 1047 cacheNextPage(index, isBlank, isLastPage); 1048 } 1049 Page page = (Page)cachedPages.get(intIndex); 1050 boolean replace = false; 1051 if (page.getPageViewport().isBlank() != isBlank) { 1052 log.debug("blank condition doesn't match. Replacing PageViewport."); 1053 replace = true; 1054 } 1055 if ((isLastPage && indexOfCachedLastPage != intIndex) 1056 || (!isLastPage && indexOfCachedLastPage >= 0)) { 1057 log.debug("last page condition doesn't match. Replacing PageViewport."); 1058 replace = true; 1059 indexOfCachedLastPage = (isLastPage ? intIndex : -1); 1060 } 1061 if (replace) { 1062 disardCacheStartingWith(intIndex); 1063 page = cacheNextPage(index, isBlank, isLastPage); 1064 } 1065 return page; 1066 } 1067 1068 private void disardCacheStartingWith(int index) { 1069 while (index < cachedPages.size()) { 1070 this.cachedPages.remove(cachedPages.size() - 1); 1071 if (!pageSeq.goToPreviousSimplePageMaster()) { 1072 log.warn("goToPreviousSimplePageMaster() on the first page called!"); 1073 } 1074 } 1075 } 1076 1077 private Page cacheNextPage(int index, boolean isBlank, boolean isLastPage) { 1078 try { 1079 String pageNumberString = pageSeq.makeFormattedPageNumber(index); 1080 SimplePageMaster spm = pageSeq.getNextSimplePageMaster( 1081 index, (startPageOfPageSequence == index), isLastPage, isBlank); 1082 1083 Region body = spm.getRegion(FO_REGION_BODY); 1084 if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) { 1085 throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName() 1088 + "' does not map to the region-body in page-master '" 1089 + spm.getMasterName() + "'. FOP presently " 1090 + "does not support this."); 1091 } 1092 Page page = new Page(spm, index, pageNumberString, isBlank); 1093 page.getPageViewport().setKey(areaTreeHandler.generatePageViewportKey()); 1095 page.getPageViewport().setForeignAttributes(spm.getForeignAttributes()); 1096 cachedPages.add(page); 1097 return page; 1098 } catch (FOPException e) { 1099 throw new IllegalStateException (e.getMessage()); 1102 } 1103 } 1104 1105 } 1106 1107 1112 public void doForcePageCount(Numeric nextPageSeqInitialPageNumber) { 1113 1114 int forcePageCount = pageSeq.getForcePageCount(); 1115 1116 1126 1127 if (nextPageSeqInitialPageNumber != null && forcePageCount == Constants.EN_AUTO) { 1130 if (nextPageSeqInitialPageNumber.getEnum() != 0) { 1131 int nextPageSeqPageNumberType = nextPageSeqInitialPageNumber.getEnum(); 1133 if (nextPageSeqPageNumberType == Constants.EN_AUTO_ODD) { 1134 forcePageCount = Constants.EN_END_ON_EVEN; 1135 } else if (nextPageSeqPageNumberType == Constants.EN_AUTO_EVEN) { 1136 forcePageCount = Constants.EN_END_ON_ODD; 1137 } else { forcePageCount = Constants.EN_NO_FORCE; 1139 } 1140 } else { int nextPageSeqPageStart = nextPageSeqInitialPageNumber.getValue(); 1142 nextPageSeqPageStart = (nextPageSeqPageStart > 0) ? nextPageSeqPageStart : 1; 1144 if (nextPageSeqPageStart % 2 == 0) { forcePageCount = Constants.EN_END_ON_ODD; 1146 } else { forcePageCount = Constants.EN_END_ON_EVEN; 1148 } 1149 } 1150 } 1151 1152 if (forcePageCount == Constants.EN_EVEN) { 1153 if ((currentPageNum - startPageNum + 1) % 2 != 0) { curPage = makeNewPage(true, false); 1155 } 1156 } else if (forcePageCount == Constants.EN_ODD) { 1157 if ((currentPageNum - startPageNum + 1) % 2 == 0) { curPage = makeNewPage(true, false); 1159 } 1160 } else if (forcePageCount == Constants.EN_END_ON_EVEN) { 1161 if (currentPageNum % 2 != 0) { curPage = makeNewPage(true, false); 1163 } 1164 } else if (forcePageCount == Constants.EN_END_ON_ODD) { 1165 if (currentPageNum % 2 == 0) { curPage = makeNewPage(true, false); 1167 } 1168 } else if (forcePageCount == Constants.EN_NO_FORCE) { 1169 } 1171 1172 if (curPage != null) { 1173 finishPage(); 1174 } 1175 } 1176} 1177 | Popular Tags |