From e3262c749a4db01da99a5ab1bed99c104633c922 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Wed, 22 Jun 2005 14:59:54 +0000 Subject: [PATCH] breaks supported on tables, table-row and table-cell content now. Cheap approach for now. TableContentLM is not yet restartable, but the Breaker handles that pretty well. Improved page break handling to support the different break classes even if no new block sequence is started. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198765 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/layoutmgr/AbstractBreaker.java | 44 +++++++---- .../layoutmgr/BlockStackingLayoutManager.java | 4 + .../layoutmgr/PageSequenceLayoutManager.java | 4 +- .../apache/fop/layoutmgr/table/EffRow.java | 6 ++ .../table/TableContentLayoutManager.java | 77 +++++++++++++++++-- .../layoutmgr/table/TableLayoutManager.java | 22 ++++-- .../fop/layoutmgr/table/TableStepper.java | 48 +++++++++++- 7 files changed, 178 insertions(+), 27 deletions(-) diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index 99dff32ba..b6f2eda24 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -127,7 +127,7 @@ public abstract class AbstractBreaker { return (blockLists.size() == 0); } - protected void startPart(BlockSequence list, boolean bIsFirstPage) { + protected void startPart(BlockSequence list, int breakClass) { //nop } @@ -183,7 +183,7 @@ public abstract class AbstractBreaker { PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), getPageViewportProvider(), alignment, alignmentLast, footnoteSeparatorLength); - int iOptPageNumber; + int iOptPageCount; BlockSequence effectiveList; if (alignment == Constants.EN_JUSTIFY) { @@ -194,16 +194,16 @@ public abstract class AbstractBreaker { effectiveList = blockList; } - //iOptPageNumber = alg.firstFit(effectiveList, flowBPD, 1, true); + //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true); alg.setConstantLineWidth(flowBPD); - iOptPageNumber = alg.findBreakingPoints(effectiveList, /*flowBPD,*/ - 1, true, true); - log.debug("PLM> iOptPageNumber= " + iOptPageNumber + iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/ + 1, true, true); + log.debug("PLM> iOptPageCount= " + iOptPageCount + " pageBreaks.size()= " + alg.getPageBreaks().size()); //*** Phase 3: Add areas *** - doPhase3(alg, iOptPageNumber, blockList, effectiveList); + doPhase3(alg, iOptPageCount, blockList, effectiveList); } } @@ -233,20 +233,38 @@ public abstract class AbstractBreaker { int endElementIndex = 0; for (int p = 0; p < partCount; p++) { PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p); - endElementIndex = pbp.getLeafPos(); - log.debug("PLM> part: " + (p + 1) - + ", break at position " + endElementIndex); - startPart(effectiveList, (p == 0)); - - int displayAlign = getCurrentDisplayAlign(); + //Check the last break position for forced breaks + int lastBreakClass; + if (p == 0) { + lastBreakClass = effectiveList.getStartOn(); + } else { + KnuthElement lastBreakElement = effectiveList.getElement(endElementIndex); + if (lastBreakElement.isPenalty()) { + KnuthPenalty pen = (KnuthPenalty)lastBreakElement; + lastBreakClass = pen.getBreakClass(); + } else { + lastBreakClass = Constants.EN_AUTO; + } + } + //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: " + (p + 1) + + ", start at pos " + startElementIndex + + ", break at pos " + endElementIndex); + + startPart(effectiveList, lastBreakClass); + + int displayAlign = getCurrentDisplayAlign(); + // ignore the last elements added by the // PageSequenceLayoutManager endElementIndex -= (endElementIndex == (originalList.size() - 1)) diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index e36f22c9b..fa65dfda9 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -822,6 +822,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager breakBefore = ((org.apache.fop.fo.flow.Block) fobj).getBreakBefore(); } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakBefore(); + } else if (fobj instanceof org.apache.fop.fo.flow.Table) { + breakBefore = ((org.apache.fop.fo.flow.Table) fobj).getBreakBefore(); } if (breakBefore == EN_PAGE || breakBefore == EN_COLUMN @@ -849,6 +851,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager breakAfter = ((org.apache.fop.fo.flow.Block) fobj).getBreakAfter(); } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakAfter(); + } else if (fobj instanceof org.apache.fop.fo.flow.Table) { + breakAfter = ((org.apache.fop.fo.flow.Table) fobj).getBreakAfter(); } if (breakAfter == EN_PAGE || breakAfter == EN_COLUMN diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index 049811461..59f07bf93 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -261,7 +261,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { addAreas(alg, partCount, originalList, effectiveList); } - protected void startPart(BlockSequence list, boolean bIsFirstPage) { + protected void startPart(BlockSequence list, int breakClass) { if (curPV == null) { throw new IllegalStateException("curPV must not be null"); } else { @@ -276,7 +276,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { // the current BlockSequence, it could have a break // condition that must be satisfied; // otherwise, we may simply need a new page - handleBreakTrait(bIsFirstPage ? list.getStartOn() : Constants.EN_PAGE); + handleBreakTrait(breakClass); } } pvProvider.setStartPageOfNextElementList(currentPageNum); diff --git a/src/java/org/apache/fop/layoutmgr/table/EffRow.java b/src/java/org/apache/fop/layoutmgr/table/EffRow.java index ddb2a47cb..1e0befa99 100644 --- a/src/java/org/apache/fop/layoutmgr/table/EffRow.java +++ b/src/java/org/apache/fop/layoutmgr/table/EffRow.java @@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table; import java.util.Iterator; import java.util.List; +import org.apache.fop.fo.flow.TableRow; import org.apache.fop.traits.MinOptMax; /** @@ -63,6 +64,11 @@ public class EffRow { return this.bodyType; } + /** @return the table-row FO for this EffRow, or null if there is no table-row. */ + public TableRow getTableRow() { + return getGridUnit(0).getRow(); + } + /** @return the calculated height for this EffRow. */ public MinOptMax getHeight() { return this.height; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index 0381b4edd..4ac134309 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Block; import org.apache.fop.area.Trait; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.Table; import org.apache.fop.fo.flow.TableBody; import org.apache.fop.fo.flow.TableRow; @@ -63,6 +64,7 @@ public class TableContentLayoutManager { private LinkedList footerList; private int headerNetHeight = 0; private int footerNetHeight = 0; + private boolean firstBreakBeforeServed = false; private int startXOffset; private int usedBPD; @@ -130,7 +132,7 @@ public class TableContentLayoutManager { KnuthBox headerAsFirst = null; KnuthBox headerAsSecondToLast = null; KnuthBox footerAsLast = null; - if (headerIter != null) { + if (headerIter != null && headerList == null) { this.headerList = getKnuthElementsForRowIterator( headerIter, context, alignment, TableRowIterator.HEADER); ElementListUtils.removeLegalBreaks(this.headerList); @@ -148,7 +150,7 @@ public class TableContentLayoutManager { headerAsSecondToLast = box; } } - if (footerIter != null) { + if (footerIter != null && footerList == null) { this.footerList = getKnuthElementsForRowIterator( footerIter, context, alignment, TableRowIterator.FOOTER); ElementListUtils.removeLegalBreaks(this.footerList); @@ -190,11 +192,39 @@ public class TableContentLayoutManager { LinkedList returnList = new LinkedList(); EffRow[] rowGroup = null; while ((rowGroup = iter.getNextRowGroup()) != null) { + //Check for break-before on the table-row at the start of the row group + TableRow rowFO = rowGroup[0].getTableRow(); + if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) { + log.info("break-before found"); + if (returnList.size() > 0) { + KnuthElement last = (KnuthElement)returnList.getLast(); + if (last.isPenalty()) { + KnuthPenalty pen = (KnuthPenalty)last; + pen.setP(-KnuthPenalty.INFINITE); + pen.setBreakClass(rowFO.getBreakBefore()); + } + } else { + if (!firstBreakBeforeServed) { + returnList.add(new KnuthPenalty(0, -KnuthPenalty.INFINITE, + false, rowFO.getBreakBefore(), new Position(getTableLM()), true)); + iter.backToPreviousRow(); + firstBreakBeforeServed = true; + break; + } + } + } + firstBreakBeforeServed = true; + + //Border resolution if (!isSeparateBorderModel()) { resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter); } + + //Element list creation createElementsForRowGroup(context, alignment, bodyType, returnList, rowGroup); + + //Handle keeps if (context.isKeepWithNextPending()) { log.debug("child LM (row group) signals pending keep-with-next"); } @@ -205,7 +235,24 @@ public class TableContentLayoutManager { KnuthElement last = (KnuthElement)returnList.getLast(); if (last.isPenalty()) { KnuthPenalty pen = (KnuthPenalty)last; - pen.setP(KnuthPenalty.INFINITE); + //Only honor keep if there's no forced break + if (!pen.isForcedBreak()) { + pen.setP(KnuthPenalty.INFINITE); + } + } + } + } + + //Check for break-after on the table-row at the end of the row group + rowFO = rowGroup[rowGroup.length - 1].getTableRow(); + if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) { + log.info("break-after found"); + if (returnList.size() > 0) { + KnuthElement last = (KnuthElement)returnList.getLast(); + if (last.isPenalty()) { + KnuthPenalty pen = (KnuthPenalty)last; + pen.setP(-KnuthPenalty.INFINITE); + pen.setBreakClass(rowFO.getBreakAfter()); } } } @@ -215,7 +262,11 @@ public class TableContentLayoutManager { //Remove last penalty KnuthElement last = (KnuthElement)returnList.getLast(); if (last.isPenalty()) { - returnList.removeLast(); + KnuthPenalty pen = (KnuthPenalty)last; + if (!pen.isForcedBreak()) { + //Only remove if we don't signal a forced break + returnList.removeLast(); + } } } return returnList; @@ -395,8 +446,24 @@ public class TableContentLayoutManager { //Get the element list for the cell contents LinkedList elems = primary.getCellLM().getNextKnuthElements( childLC, alignment); - primary.setElements(elems); + //Temporary? Multiple calls in case of break conditions. + //TODO Revisit when table layout is restartable + while (!primary.getCellLM().isFinished()) { + LinkedList additionalElems = primary.getCellLM().getNextKnuthElements( + childLC, alignment); + elems.addAll(additionalElems); + } ElementListObserver.observe(elems, "table-cell", primary.getCell().getId()); + + if (((KnuthElement)elems.getLast()).isPenalty() + && ((KnuthPenalty)elems.getLast()).getP() + == -KnuthElement.INFINITE) { + // a descendant of this block has break-after + log.warn("Descendant of table-cell signals break: " + + primary.getCellLM().isFinished()); + } + + primary.setElements(elems); if (childLC.isKeepWithNextPending()) { log.debug("child LM signals pending keep-with-next"); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index babdaa34b..bb9a40a71 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -116,12 +116,20 @@ public class TableLayoutManager extends BlockStackingLayoutManager return iIndents; } - /** - * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int) - */ + /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { - //Body curLM; // currently active LM + LinkedList returnList = new LinkedList(); + + if (!bBreakBeforeServed) { + try { + if (addKnuthElementsForBreakBefore(returnList)) { + return returnList; + } + } finally { + bBreakBeforeServed = true; + } + } referenceIPD = context.getRefIPD(); if (fobj.getInlineProgressionDimension().getOptimum().getEnum() != EN_AUTO) { @@ -169,7 +177,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager LinkedList returnedList = null; LinkedList contentList = new LinkedList(); - LinkedList returnList = new LinkedList(); //Position returnPosition = new NonLeafPosition(this, null); //Body prevLM = null; @@ -179,7 +186,9 @@ public class TableLayoutManager extends BlockStackingLayoutManager stackSize)); childLC.setRefIPD(context.getRefIPD()); - contentLM = new TableContentLayoutManager(this); + if (contentLM == null) { + contentLM = new TableContentLayoutManager(this); + } returnedList = contentLM.getNextKnuthElements(childLC, alignment); if (childLC.isKeepWithNextPending()) { log.debug("TableContentLM signals pending keep-with-next"); @@ -261,6 +270,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } wrapPositionElements(contentList, returnList); + addKnuthElementsForBreakAfter(returnList); setFinished(true); return returnList; } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java index 4b09fce35..2fe38e79a 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java @@ -24,6 +24,8 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthBox; @@ -57,6 +59,7 @@ public class TableStepper { private int[] borderAfter; private boolean rowBacktrackForLastStep; private boolean[] keepWithNextSignals; + private boolean[] forcedBreaks; /** * Main constructor @@ -77,9 +80,23 @@ public class TableStepper { borderBefore = new int[columnCount]; borderAfter = new int[columnCount]; keepWithNextSignals = new boolean[columnCount]; + forcedBreaks = new boolean[columnCount]; Arrays.fill(end, -1); } + private void clearBreakCondition() { + Arrays.fill(forcedBreaks, false); + } + + private boolean isBreakCondition() { + for (int i = 0; i < forcedBreaks.length; i++) { + if (forcedBreaks[i]) { + return true; + } + } + return false; + } + private EffRow getActiveRow() { return rowGroup[activeRow]; } @@ -147,6 +164,7 @@ public class TableStepper { widths[column] = 0; startRow[column] = activeRow; keepWithNextSignals[column] = false; + forcedBreaks[column] = false; } else if (gu.isPrimary()) { PrimaryGridUnit pgu = (PrimaryGridUnit)gu; boolean makeBoxForWholeRow = false; @@ -184,6 +202,7 @@ public class TableStepper { widths[column] = 0; startRow[column] = activeRow; keepWithNextSignals[column] = false; + forcedBreaks[column] = false; } } @@ -303,6 +322,10 @@ public class TableStepper { if (signalKeepWithNext || getTableLM().mustKeepTogether()) { p = KnuthPenalty.INFINITE; } + if (isBreakCondition()) { + p = -KnuthPenalty.INFINITE; //Overrides any keeps (see 4.8 in XSL 1.0) + clearBreakCondition(); + } returnList.add(new KnuthPenalty(effPenaltyLen, p, false, penaltyPos, false)); log.debug("step=" + step + " (+" + increase + ")" @@ -321,11 +344,20 @@ public class TableStepper { //we have to signal the still pending last keep-with-next using the LayoutContext. context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING); } + if (isBreakCondition()) { + ((KnuthPenalty)returnList.getLast()).setP(-KnuthPenalty.INFINITE); + } lastTCPos.setFlag(TableContentPosition.LAST_IN_ROWGROUP, true); return returnList; } private int getNextStep(int lastStep) { + //Check for forced break conditions + /* + if (isBreakCondition()) { + return -1; + }*/ + int[] backupWidths = new int[start.length]; System.arraycopy(widths, 0, backupWidths, 0, backupWidths.length); @@ -349,6 +381,11 @@ public class TableStepper { if (rowPendingIndicator == 0) { if (activeRow < rowGroup.length - 1) { + TableRow rowFO = getActiveRow().getTableRow(); + if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) { + log.warn("break-after ignored on table-row because of row spanning " + + "in progress (See XSL 1.0, 7.19.1)"); + } activeRow++; log.debug("===> new row: " + activeRow); initializeElementLists(); @@ -357,6 +394,11 @@ public class TableStepper { backupWidths[i] = 0; } } + rowFO = getActiveRow().getTableRow(); + if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) { + log.warn("break-before ignored on table-row because of row spanning " + + "in progress (See XSL 1.0, 7.19.2)"); + } } } @@ -370,7 +412,11 @@ public class TableStepper { end[i]++; KnuthElement el = (KnuthElement)elementLists[i].get(end[i]); if (el.isPenalty()) { - if (el.getP() < KnuthElement.INFINITE) { + if (el.getP() <= -KnuthElement.INFINITE) { + log.warn("FORCED break encountered!"); + forcedBreaks[i] = true; + break; + } else if (el.getP() < KnuthElement.INFINITE) { //First legal break point break; } -- 2.39.5