--- test/layoutengine/standard-testcases/block_table_orphans_widows.xml (revision ) +++ test/layoutengine/standard-testcases/block_table_orphans_widows.xml (revision ) @@ -0,0 +1,119 @@ + + + + + +

+ This test checks the behavior of the "orphans" and "widows" properties + in case tables are nested in blocks. +

+
+ + + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + + + + + + + + + + + + +
--- test/layoutengine/standard-testcases/widows-orphans_keep-with-next.xml (revision ) +++ test/layoutengine/standard-testcases/widows-orphans_keep-with-next.xml (revision ) @@ -0,0 +1,66 @@ + + + + + +

+ This test checks a combination of widows/orphans with a keep-with-next + specified on a first child that generates less lines than the value for + orphans. +

+
+ + + + + + + + + + + + Title + + + line 1 + line 2 + line 3 + line 4 + + + + + + Title + + + line 1 + line 2 + + + + + + ` + + + + + +
--- test/layoutengine/standard-testcases/block_widows.xml (revision ) +++ test/layoutengine/standard-testcases/block_widows.xml (revision ) @@ -0,0 +1,143 @@ + + + + + +

+ This test checks the default behavior of the "widows" property. +

+
+ + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + fermentum tempor nunc. Class aptent + taciti sociosqu ad litora torquent per + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + fermentum tempor nunc. Class aptent + taciti sociosqu ad litora torquent per + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + + + + + Lorem ipsum + dolor sit amet, + consectetur + adipiscing elit. + Cras placerat, + lectus vel + iaculis euismod, + ipsum enim + dapibus urna, + eu pellentesque + velit dolor + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + fermentum tempor nunc. Class aptent + + Lorem ipsum + + + + + + + + + + + + + + + + + +
--- test/layoutengine/standard-testcases/block_orphans.xml (revision ) +++ test/layoutengine/standard-testcases/block_orphans.xml (revision ) @@ -0,0 +1,167 @@ + + + + + +

+ This test checks the default behavior of the "orphans" property. +

+
+ + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + + + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + dui tortor, tempor ut convallis in, + + Lorem ipsum + dolor sit amet, + consectetur + velit dolor + + + + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + Lorem ipsum + + Lorem ipsum + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
--- test/layoutengine/standard-testcases/block_list-block_orphans_widows.xml (revision ) +++ test/layoutengine/standard-testcases/block_list-block_orphans_widows.xml (revision ) @@ -0,0 +1,128 @@ + + + + + +

+ This test checks the behavior of the "orphans" and "widows" properties + in case list-blocks are nested in blocks. +

+
+ + + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + + + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Cras placerat, lectus vel + iaculis euismod, ipsum enim dapibus + urna, eu pellentesque velit dolor ac + + + + + + + + purus. Maecenas vitae pulvinar turpis. + Duis venenatis tincidunt velit, fringilla + dignissim sapien faucibus vel. Quisque + placerat ornare consectetur. Aenean + + + + + + + + + + + + + + + + +
--- src/java/org/apache/fop/layoutmgr/table/TableStepper.java (revision 1083863) +++ src/java/org/apache/fop/layoutmgr/table/TableStepper.java (revision ) @@ -181,6 +181,7 @@ TableContentPosition lastTCPos = null; LinkedList returnList = new LinkedList(); int laststep = 0; + int stepLines; int step = getFirstStep(); do { int maxRemainingHeight = getMaxRemainingHeight(); @@ -201,10 +202,15 @@ LinkedList footnoteList = new LinkedList(); //Put all involved grid units into a list List cellParts = new java.util.ArrayList(columnCount); + stepLines = 0; for (Iterator iter = activeCells.iterator(); iter.hasNext();) { ActiveCell activeCell = (ActiveCell) iter.next(); CellPart part = activeCell.createCellPart(); cellParts.add(part); + int partLines = part.getLineCount(); + if (partLines > stepLines) { + stepLines = partLines; + } activeCell.addFootnotes(footnoteList); } @@ -220,11 +226,7 @@ lastTCPos = tcpos; // TODO TableStepper should remain as footnote-agnostic as possible - if (footnoteList.isEmpty()) { - returnList.add(new KnuthBox(boxLen, tcpos, false)); - } else { - returnList.add(new KnuthBlockBox(boxLen, footnoteList, tcpos, false)); - } + returnList.add(new KnuthBlockBox(boxLen, tcpos, false, stepLines, footnoteList)); int effPenaltyLen = Math.max(0, penaltyOrGlueLen); TableHFPenaltyPosition penaltyPos = new TableHFPenaltyPosition(getTableLM()); --- src/java/org/apache/fop/layoutmgr/table/CellPart.java (revision 990144) +++ src/java/org/apache/fop/layoutmgr/table/CellPart.java (revision ) @@ -20,6 +20,7 @@ package org.apache.fop.layoutmgr.table; import org.apache.fop.fo.flow.table.PrimaryGridUnit; +import org.apache.fop.layoutmgr.ElementListUtils; /** * Represents a non-divisible part of a grid unit. Used by the table stepper. @@ -117,6 +118,10 @@ return condAfterContentLength; } + int getLineCount() { + return ElementListUtils.getLineCount(pgu.getElements().subList(start, end + 1)); + } + /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer("Part: "); --- src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java (revision 1069941) +++ src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java (revision ) @@ -316,6 +316,7 @@ // footnote-agnostic as possible LinkedList footnoteList = null; ListElement el; + int stepLines = 0; for (int i = 0; i < elementLists.length; i++) { for (int j = start[i]; j <= end[i]; j++) { el = (ListElement) elementLists[i].get(j); @@ -326,17 +327,19 @@ footnoteList.addAll(((KnuthBlockBox) el).getFootnoteBodyLMs()); } } + int lineCount = ElementListUtils.getLineCount( + elementLists[i].subList(start[i], end[i] + 1)); + if (lineCount > stepLines) { + stepLines = lineCount; - } + } + } // add the new elements addedBoxHeight += boxHeight; ListItemPosition stepPosition = new ListItemPosition(this, start[0], end[0], start[1], end[1]); - if (footnoteList == null) { - returnList.add(new KnuthBox(boxHeight, stepPosition, false)); - } else { - returnList.add(new KnuthBlockBox(boxHeight, footnoteList, stepPosition, false)); - } + returnList.add( + new KnuthBlockBox(boxHeight, stepPosition, false, stepLines, footnoteList)); if (addedBoxHeight < totalHeight) { Keep keep = keepWithNextActive.compare(getKeepTogether()); @@ -694,6 +697,5 @@ body.reset(); } - } --- src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java (revision 893238) +++ src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java (revision ) @@ -19,6 +19,7 @@ package org.apache.fop.layoutmgr; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -29,81 +30,152 @@ */ public class KnuthBlockBox extends KnuthBox { + /** + * Helper class to encapsulate information related to + * footnotes that are associated with this box + */ + static class FootnoteInfo { + + private final List footnoteList; + /** List of Knuth elements. This is a list of LinkedList elements. */ + private List> elementLists; + + + /** + * Creates a new instance initialized with the given list of + * footnote layout managers. + * @param footnoteList the list of LMs + */ + FootnoteInfo(List footnoteList) { + this.footnoteList = footnoteList; + } + + /** + * Adds the given list to the list of elements (generated by the LMs in + * {@code footnoteList} as the footnote content). + * @param elementList + */ + void addElementList(List elementList) { + if (elementLists == null) { + elementLists = new LinkedList>(); + } + elementLists.add(elementList); + } + } + private MinOptMax ipdRange; /** * Natural width of the line represented by this box. In addition to ipdRange because * it isn't possible to get the opt value stored in a MinOptMax object. */ private int bpd; - private List footnoteList; - /** List of Knuth elements. This is a list of LinkedList elements. */ - private List elementLists = null; + private FootnoteInfo footnoteInfo; + + private int lineCount; + /** * Creates a new box. * * @param width block progression dimension of this box - * @param range min, opt, max inline progression dimension of this box - * @param bpdim natural width of the line represented by this box. - * @param pos the Position stored in this box + * @param pos the {@link Position} stored in this box * @param auxiliary is this box auxiliary? */ - public KnuthBlockBox(int width, MinOptMax range, int bpdim, Position pos, boolean auxiliary) { + public KnuthBlockBox(int width, Position pos, boolean auxiliary) { super(width, pos, auxiliary); - ipdRange = range; - bpd = bpdim; - footnoteList = new LinkedList(); + // default auxiliary box does not count as a line + this.lineCount = (auxiliary ? 0 : 1); } /** - * Creates a new box. + * Creates a new box spanning the number of lines specified. * * @param width block progression dimension of this box - * @param list footnotes cited by elements in this box. The list contains the corresponding - * FootnoteBodyLayoutManagers - * @param pos the Position stored in this box + * @param pos the {@link Position} stored in this box * @param auxiliary is this box auxiliary? + * @param lineCount the number of lines stored in this box */ - public KnuthBlockBox(int width, List list, Position pos, boolean auxiliary) { + public KnuthBlockBox(int width, Position pos, boolean auxiliary, int lineCount) { super(width, pos, auxiliary); - ipdRange = MinOptMax.ZERO; - bpd = 0; - footnoteList = new LinkedList(list); + this.lineCount = lineCount; } /** + * Creates a new box, with the specified range of IPD and fixed BPD. + * (used for vertical justification of lines) + * + * @param width block progression dimension of this box + * @param pos the {@link Position} stored in this box + * @param auxiliary is this box auxiliary? + * @param range min, opt, max inline progression dimension of this box + * @param bpdim natural width of the line represented by this box. + */ + public KnuthBlockBox(int width, Position pos, boolean auxiliary, MinOptMax range, int bpdim) { + this(width, pos, auxiliary); + this.ipdRange = range; + this.bpd = bpdim; + } + + /** + * Creates a new box spanning the specified number of lines, and associates + * it with the given list of footnotes. + * + * @param width block progression dimension of this box + * @param pos the {@link Position} stored in this box + * @param auxiliary is this box auxiliary? + * @param footnoteList footnotes cited by elements in this box. + * The list contains the corresponding FootnoteBodyLayoutManagers + * @param lineCount the number of lines stored in this box + */ + public KnuthBlockBox(int width, Position pos, boolean auxiliary, + int lineCount, List footnoteList) { + this(width, pos, auxiliary, lineCount); + this.ipdRange = MinOptMax.ZERO; + this.bpd = 0; + if (footnoteList != null && !footnoteList.isEmpty()) { + this.footnoteInfo = new FootnoteInfo(footnoteList); + } + } + + /** * @return the LMs for the footnotes cited in this box. */ - public List getFootnoteBodyLMs() { - return footnoteList; + public List getFootnoteBodyLMs() { + return (footnoteInfo == null + ? Collections.emptyList() + : footnoteInfo.footnoteList); } /** - * @return true if this box contains footnote citations. + * Returns {@code true} if this box has anchors for footnotes. + * @return {@code true} if this box has anchors for footnotes, {@code false} otherwise */ public boolean hasAnchors() { - return (footnoteList.size() > 0); + return (footnoteInfo != null + && footnoteInfo.footnoteList != null + && !footnoteInfo.footnoteList.isEmpty()); } /** * Adds the given list of Knuth elements to this box' list of elements. + *

Note: if the box is not associated with footnotes, the parameter is ignored. * * @param list elements corresponding to a footnote body */ - public void addElementList(List list) { - if (elementLists == null) { - elementLists = new LinkedList(); + void addElementList(List list) { + assert (hasAnchors()); + footnoteInfo.addElementList(list); - } + } - elementLists.add(list); - } /** * Returns the list of Knuth sequences registered by this box. * * @return a list of KnuthElement sequences corresponding to footnotes cited in this box */ - public List getElementLists() { - return elementLists; + List getElementLists() { + return footnoteInfo == null + ? Collections.>emptyList() + : footnoteInfo.elementLists; } /** @@ -122,4 +194,12 @@ public int getBPD() { return bpd; } + + /** + * Returns the (maximum) number of lines of content that this box represents + * @return the number of lines in this box + */ + public int getLineCount() { + return lineCount; -} + } +} --- src/java/org/apache/fop/layoutmgr/KnuthBox.java (revision 893238) +++ src/java/org/apache/fop/layoutmgr/KnuthBox.java (revision ) @@ -45,12 +45,12 @@ super(width, pos, auxiliary); } - /** {@inheritDoc} */ + @Override public boolean isBox() { return true; } - /** {@inheritDoc} */ + @Override public String toString() { StringBuffer buffer = new StringBuffer(64); if (isAuxiliary()) { @@ -61,5 +61,4 @@ buffer.append(getWidth()); return buffer.toString(); } - } --- src/java/org/apache/fop/layoutmgr/KnuthPenalty.java (revision 1079013) +++ src/java/org/apache/fop/layoutmgr/KnuthPenalty.java (revision ) @@ -46,6 +46,9 @@ /** Dummy, zero-width penalty */ public static final KnuthPenalty DUMMY_ZERO_PENALTY = new KnuthPenalty(0, 0, false, null, true); + /** Dummy break inhibitor */ + public static final KnuthPenalty DUMMY_INFINITE_PENALTY + = new KnuthPenalty(0, KnuthElement.INFINITE, false, null, true); private int penalty; private boolean penaltyFlagged; --- src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (revision 1073116) +++ src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (revision ) @@ -66,6 +66,9 @@ private MinOptMax effSpaceBefore; private MinOptMax effSpaceAfter; + private int orphans = 2; + private int widows = 2; + /** * Creates a new BlockLayoutManager. * @param inBlock the block FO object to create the layout manager for. @@ -97,6 +100,8 @@ .getOptimum(this).getLength().getValue(this); adjustedSpaceAfter = fo.getCommonMarginBlock().spaceAfter.getSpace() .getOptimum(this).getLength().getValue(this); + orphans = fo.getOrphans(); + widows = fo.getWidows(); } /** {@inheritDoc} */ @@ -110,10 +115,42 @@ public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, Position restartPosition, LayoutManager restartAtLM) { resetSpaces(); - return super.getNextKnuthElements( + + List contentList = super.getNextKnuthElements( context, alignment, lmStack, restartPosition, restartAtLM); + + if (!this.hasNextChildLM()) { + // handle widows + int lineCount = 0, boxCount = 0, index = contentList.size(); + ListElement current; + for (ListIterator it = contentList.listIterator(contentList.size()); + it.hasPrevious();) { + current = it.previous(); + index--; + if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { + boxCount++; + // non-auxiliary box => increase lineCount + if (current instanceof KnuthBlockBox) { + lineCount += ((KnuthBlockBox) current).getLineCount(); + } else { + // assume one line + lineCount++; - } + } + if (lineCount >= widows) { + break; + } + } + } + // if the sublist only consists of boxes, nothing to remove + if (index <= (contentList.size() - boxCount)) { + ElementListUtils.removeLegalBreaks( + contentList.subList(index, contentList.size())); + } + } + return contentList; + } + /** * Overridden to take into account that the childLM may be the block's * {@link LineLayoutManager}. @@ -132,25 +169,82 @@ // nop; will have been properly set by makeChildLayoutContext() } - if (childLM == this.childLMs.get(0)) { + boolean isFirst = childLM == this.childLMs.get(0); + if (isFirst) { childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); //Handled already by the parent (break collapsing, see above) } + List childElements; if (lmStack == null) { - return childLM.getNextKnuthElements(childLC, alignment); + childElements = childLM.getNextKnuthElements(childLC, alignment); } else { if (childLM instanceof LineLayoutManager) { assert (restartPosition instanceof LeafPosition); - return ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment, + childElements + = ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment, - (LeafPosition) restartPosition); + (LeafPosition) restartPosition); } else { - return childLM.getNextKnuthElements(childLC, alignment, + childElements = childLM.getNextKnuthElements(childLC, alignment, lmStack, restartPosition, restartAtLM); } } + + if (childElements != null && isFirst) { + // handle orphans + int lineCount = 0, boxCount = 0, endIndex = 0; + boolean orphansSatisfied = false; + ListElement current; + for (ListIterator it = childElements.listIterator(); it.hasNext();) { + current = it.next(); + endIndex++; + if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { + boxCount++; + // non-auxiliary box => increase lineCount + if (current instanceof KnuthBlockBox) { + lineCount += ((KnuthBlockBox) current).getLineCount(); + } else { + // assume one line + lineCount++; - } + } + if (lineCount >= orphans) { + break; + } + } + } + // if the sublist only consists of boxes, nothing to remove + if (endIndex > boxCount) { + ElementListUtils.removeLegalBreaks(childElements.subList(0, endIndex)); + } + } + return childElements; + } + + /** + * Overridden to deal with a special case for the "orphans" property. + * {@inheritDoc} + */ + @Override + protected void addInBetweenBreak(List contentList, + LayoutContext parentLC, LayoutContext childLC) { + + if (this.childLMs.size() > 1 + && this.curChildLM == this.childLMs.get(1)) { + // Special case - second childLM: + // If the content so far did not produce enough lines to satisfy + // this LM's orphans constraint, avoid adding a break possibility. + if (ElementListUtils.getLineCount(contentList) < orphans) { + // Make sure pending keeps are cleared (= satisfied by the orphans constraint) + parentLC.clearKeepWithNextPending(); + childLC.clearKeepWithPreviousPending(); + return; + } + } + + super.addInBetweenBreak(contentList, parentLC, childLC); + } + private void resetSpaces() { this.discardBorderBefore = false; this.discardBorderAfter = false; --- src/java/org/apache/fop/layoutmgr/ElementListUtils.java (revision 1056513) +++ src/java/org/apache/fop/layoutmgr/ElementListUtils.java (revision ) @@ -133,6 +133,87 @@ } /** + * Removes all legal break points from the given {@code List}. + * That is, this method:
+ *

    + *
  • removes penalties in between two boxes
  • + *
  • converts other penalties to inhibit breaks
  • + *
  • converts glues following a box into auxiliary boxes + * or penalty-glue sequences
  • + *
+ *
+ * Note: leading glues will not be treated, as it is assumed nothing precedes + * the first element of the given list. Similarly, trailing penalties would just be set + * to {@link KnuthElement#INFINITE}, as there is never a following box.
+ * Care should be taken to provide as much context elements as required. Ideally, the + * list should start with a box and end with a box or glue.
+ * + * @param elements the element list from which the breaks will be removed + */ + public static void removeLegalBreaks(List elements) { + + ListElement current; + boolean previousIsBox = false; + + for (ListIterator it = elements.listIterator(); it.hasNext();) { + + current = it.next(); + + if (current.isBox()) { + previousIsBox = true; + continue; + } else if (current.isGlue() && previousIsBox) { + KnuthGlue glue = (KnuthGlue)current; + if (glue.getStretch() == 0 && glue.getShrink() == 0) { + // non-stretchable glue => replace with a box of the same width + it.set(new KnuthBox(glue.getWidth(), glue.getPosition(), true)); + } else { + // stretchable glue => add break-inhibitor + it.previous(); + it.add(KnuthPenalty.DUMMY_INFINITE_PENALTY); + it.next(); + } + } else if (current.isPenalty() || current instanceof BreakElement + && !current.isForcedBreak()) { + boolean nextIsBox = (it.hasNext() && it.next().isBox()); + it.previous(); + if (previousIsBox && nextIsBox) { + // penalty or BreakElement in between boxes => remove + it.previous(); + it.remove(); + } else { + if (current.isPenalty()) { + ((KnuthPenalty)current).setPenalty(KnuthElement.INFINITE); + } else { + ((BreakElement)current).setPenaltyValue(KnuthElement.INFINITE); + } + } + } + previousIsBox = false; + } + } + + /** + * Obtains the (maximum) number of lines in the given list. + * @param elements the list to count the lines for + * @return the number of lines + */ + public static int getLineCount(List elements) { + int lineCount = 0; + for (ListElement current : elements) { + if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { + if (current instanceof KnuthBlockBox) { + lineCount += ((KnuthBlockBox) current).getLineCount(); + } else { + // assume one line + lineCount++; + } + } + } + return lineCount; + } + + /** * Calculates the content length of the given element list. Warning: It doesn't take any * stretch and shrink possibilities into account. * @param elems the element list --- src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision 1088950) +++ src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision ) @@ -656,6 +656,7 @@ /* curLM.getNextKnuthElements() returned null or an empty list; * this can happen if there is nothing more to layout, * so just iterate once more to see if there are other children */ + curLM = (InlineLevelLayoutManager) getChildLM(); continue; } @@ -919,10 +920,7 @@ for (int i = 0; i < llPoss.getChosenLineCount(); i++) { - if (returnList.size() > 0 - && i > 0 //if i==0 break generated above already - && i >= fobj.getOrphans() - && i <= llPoss.getChosenLineCount() - fobj.getWidows()) { + if (returnList.size() > 0 && i > 0) { // penalty allowing a page break between lines Keep keep = getKeepTogether(); returnList.add(new BreakElement( @@ -932,24 +930,28 @@ context)); } endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); + // create a list of the FootnoteBodyLM handling footnotes // whose citations are in this line List footnoteList = new LinkedList(); - ListIterator elementIterator = seq.listIterator(startIndex); - while (elementIterator.nextIndex() <= endIndex) { - KnuthElement element = elementIterator.next(); - if (element instanceof KnuthInlineBox - && ((KnuthInlineBox) element).isAnchor()) { - footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); - } else if (element instanceof KnuthBlockBox) { - footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); + for (Object o : seq.subList(startIndex, endIndex)) { + //TODO: remove cast once KnuthSequence can be made type-safe + KnuthElement el = (KnuthElement) o; + if (el instanceof KnuthInlineBox + && ((KnuthInlineBox) el).isAnchor()) { + footnoteList.add(((KnuthInlineBox) el).getFootnoteBodyLM()); + } else if (el instanceof KnuthBlockBox + && ((KnuthBlockBox) el).hasAnchors()) { + footnoteList.addAll(((KnuthBlockBox) el).getFootnoteBodyLMs()); } } - startIndex = endIndex + 1; + LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i); - returnList.add(new KnuthBlockBox - (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, - footnoteList, lbp, false)); + KnuthBox newBox = new KnuthBlockBox( + lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, lbp, false, 1, footnoteList); + returnList.add(newBox); + + startIndex = endIndex + 1; } } } @@ -1155,9 +1157,7 @@ LineLayoutPossibilities llPoss = lineLayoutsList[p]; //log.debug("demerits of the chosen layout: " + llPoss.getChosenDemerits()); for (int i = 0; i < llPoss.getChosenLineCount(); i++) { - if (!((BlockLevelLayoutManager) parentLayoutManager).mustKeepTogether() - && i >= fobj.getOrphans() - && i <= llPoss.getChosenLineCount() - fobj.getWidows()) { + if (!((BlockLevelLayoutManager) parentLayoutManager).mustKeepTogether()) { // null penalty allowing a page break between lines returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); } @@ -1182,9 +1182,9 @@ contentIPD = MinOptMax.getInstance(lbp.lineWidth - lbp.difference + lbp.startIndent); } - returnList.add(new KnuthBlockBox(lbp.lineHeight, contentIPD, (lbp.ipdAdjust != 0 - ? lbp.lineWidth - lbp.difference : 0), - lbp, false)); + returnList.add(new KnuthBlockBox(lbp.lineHeight, lbp, false, + contentIPD, + (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0))); } } return returnList;