--- test/layoutengine/standard-testcases/markers_3.xml (revision 627324) +++ test/layoutengine/standard-testcases/markers_3.xml (revision ) @@ -55,7 +55,7 @@ - + [PAGE] [PS] @@ -96,7 +96,7 @@ - + sometext sometext sometext --- test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml (revision 992029) +++ test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml (revision ) @@ -45,7 +45,7 @@ - + Block before the page break. Block after the page break. --- test/layoutengine/standard-testcases/block_padding_2.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_padding_2.xml (revision ) @@ -49,16 +49,7 @@ - + - - - - - - - - - @@ -90,15 +81,6 @@ - - - - - - - - - --- test/layoutengine/standard-testcases/inline_block_nested_4.xml (revision 627324) +++ test/layoutengine/standard-testcases/inline_block_nested_4.xml (revision ) @@ -52,19 +52,13 @@ - - - - - - - + 3 --- test/layoutengine/standard-testcases/block_border_bug43917.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_border_bug43917.xml (revision ) @@ -55,7 +55,6 @@ - @@ -70,7 +69,6 @@ - --- test/layoutengine/standard-testcases/inline_block_nested_6.xml (revision 807014) +++ test/layoutengine/standard-testcases/inline_block_nested_6.xml (revision ) @@ -49,14 +49,14 @@ - 5 + 3 5 - 3 + 5 --- test/layoutengine/standard-testcases/table_border-collapse_separate_conditionals.xml (revision 627324) +++ test/layoutengine/standard-testcases/table_border-collapse_separate_conditionals.xml (revision ) @@ -48,7 +48,7 @@ padding-after.conditionality="retain" padding-before.length="2pt" padding-before.conditionality="retain"> - + Cell 1.1 Cell 1.1 Cell 1.1 @@ -60,7 +60,7 @@ border-before-width.conditionality="retain" border-after-width.length="2pt" border-after-width.conditionality="retain"> - + Cell 1.2 Cell 1.2 Cell 1.2 @@ -87,7 +87,7 @@ border-before-width.conditionality="retain" padding-after.length="5pt" padding-after.conditionality="retain"> - + Cell 1.1 Cell 1.1 Cell 1.1 @@ -99,7 +99,7 @@ border-after-width.conditionality="retain" padding-before.length="7pt" padding-before.conditionality="retain"> - + Cell 1.2 Cell 1.2 Cell 1.2 @@ -130,7 +130,7 @@ padding-before.conditionality="retain" padding-after.length="1pt" padding-after.conditionality="retain"> - + Cell 1.1 Cell 1.1 Cell 1.1 @@ -138,7 +138,7 @@ - + Cell 1.2 Cell 1.2 Cell 1.2 @@ -165,7 +165,7 @@ border-after-width.conditionality="retain" padding-after.length="9pt" padding-after.conditionality="retain"> - + Cell 1.1 Cell 1.1 Cell 1.1 @@ -177,7 +177,7 @@ border-before-width.conditionality="retain" padding-before.length="11pt" padding-before.conditionality="retain"> - + Cell 1.2 Cell 1.2 Cell 1.2 @@ -201,7 +201,7 @@ - + Cell 1.1 Cell 1.1 Cell 1.1 --- test/layoutengine/standard-testcases/inline_block-level_nested_1.xml (revision 815383) +++ test/layoutengine/standard-testcases/inline_block-level_nested_1.xml (revision ) @@ -77,23 +77,17 @@ - - - - - - 3 --- test/layoutengine/standard-testcases/table_border-collapse_collapse_conditionals.xml (revision 627324) +++ test/layoutengine/standard-testcases/table_border-collapse_collapse_conditionals.xml (revision ) @@ -43,7 +43,7 @@ - + Cell 1.1 Cell 1.1 @@ -54,7 +54,7 @@ border-before-width.conditionality="retain" padding-after.length="7pt" padding-after.conditionality="retain"> - + Cell 1.2 Cell 1.2 Cell 1.2 @@ -66,7 +66,7 @@ border-before-width.length="5pt" border-before-width.conditionality="retain"> - + Cell 2.1 Cell 2.1 @@ -105,7 +105,7 @@ border-before-width.length="5pt" border-before-width.conditionality="retain"> - + Cell 2.1 Cell 2.1 @@ -115,7 +115,7 @@ border-before-width.length="5pt" border-before-width.conditionality="retain"> - + Cell 3.1 Cell 3.1 --- test/layoutengine/standard-testcases/wrapper_inline_block.xml (revision 627324) +++ test/layoutengine/standard-testcases/wrapper_inline_block.xml (revision ) @@ -40,9 +40,7 @@ - - 3 --- test/layoutengine/standard-testcases/flow_changing-ipd_no-restartable.xml (revision 827047) +++ test/layoutengine/standard-testcases/flow_changing-ipd_no-restartable.xml (revision ) @@ -53,7 +53,7 @@ - + Before page break After page break @@ -70,7 +70,7 @@ - + Before page break After page break @@ -122,7 +122,7 @@ - + Before page break After page break --- test/layoutengine/standard-testcases/block_space-before_space-after_4.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_space-before_space-after_4.xml (revision ) @@ -48,16 +48,14 @@ - - - + 2 - 2 + 3 @@ -69,8 +67,8 @@ - - + + --- test/layoutengine/standard-testcases/markers_2.xml (revision 627324) +++ test/layoutengine/standard-testcases/markers_2.xml (revision ) @@ -55,7 +55,7 @@ - + 1 @@ -68,7 +68,7 @@ text1 text1 - + 2 @@ -78,7 +78,7 @@ text2 text2 - + 3 --- test/layoutengine/standard-testcases/block_space-before_space-after_7.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_space-before_space-after_7.xml (revision ) @@ -47,7 +47,8 @@ - + + --- test/layoutengine/standard-testcases/region-body_column-count_bug37828.xml (revision 627324) +++ test/layoutengine/standard-testcases/region-body_column-count_bug37828.xml (revision ) @@ -62,9 +62,9 @@ - - - + + + --- test/layoutengine/standard-testcases/block_space-before_space-after_6.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_space-before_space-after_6.xml (revision ) @@ -33,7 +33,7 @@ first line - + before break after break @@ -42,7 +42,8 @@ first line - + before break after break --- test/layoutengine/standard-testcases/flow_changing-ipd_last-page.xml (revision 915406) +++ test/layoutengine/standard-testcases/flow_changing-ipd_last-page.xml (revision ) @@ -45,7 +45,7 @@ First block - + Block before the page break. Block after the page break. --- test/layoutengine/standard-testcases/inline_block_nested_1.xml (revision 627324) +++ test/layoutengine/standard-testcases/inline_block_nested_1.xml (revision ) @@ -39,9 +39,7 @@ - - 3 --- test/layoutengine/standard-testcases/block_space-before_space-after_bug38102.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_space-before_space-after_bug38102.xml (revision ) @@ -113,7 +113,8 @@ - + + @@ -127,7 +128,6 @@ - 3 --- test/layoutengine/standard-testcases/block_space-before_space-after_9a.xml (revision 627324) +++ test/layoutengine/standard-testcases/block_space-before_space-after_9a.xml (revision ) @@ -59,9 +59,7 @@ - + - - --- 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,76 @@ 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 boxCount = 0; + ListElement current, last = null; + for (ListIterator it = contentList.listIterator(contentList.size()); + it.hasPrevious();) { + current = it.previous(); + if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { + // non-auxiliary box => assume a 'line' and increase boxCount + boxCount++; + if (boxCount >= widows) { + break; - } + } + } else if (current.isGlue()) { + if (it.hasPrevious() && it.previous().isBox()) { + // return to current + it.next(); + it.next(); + KnuthGlue glue = (KnuthGlue)current; + if (glue.getShrink() == 0 && glue.getStretch() == 0) { + // non-stretchable glue => convert to auxiliary box + it.set(new KnuthBox(glue.getWidth(), glue.getPosition(), true)); + } else { + // stretchable glue => convert to auxiliary box + it.previous(); + it.previous(); + it.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, true)); + it.previous(); + } + } + } else if (current.isPenalty() || current instanceof BreakElement + && !current.isForcedBreak()) { + if (last != null && last.isBox() + && it.hasPrevious() && it.previous().isBox()) { + // return to current + it.next(); + it.next(); + // penalty or BreakElement in between two boxes => remove + it.remove(); + } else { + it.next(); + if (current.isPenalty()) { + KnuthPenalty penalty = (KnuthPenalty)current; + //Convert penalty to break inhibitor + if (penalty.getPenalty() < KnuthPenalty.INFINITE) { + it.set(new KnuthPenalty(penalty.getWidth(), KnuthPenalty.INFINITE, + penalty.isPenaltyFlagged(), penalty.getPosition(), + penalty.isAuxiliary())); + } + } else { + // a BreakElement + BreakElement breakEl = (BreakElement)current; + if (breakEl.getPenaltyValue() < KnuthPenalty.INFINITE) { + breakEl.setPenaltyValue(KnuthPenalty.INFINITE); + } + } + it.previous(); + } + } + last = current; + } + } + return contentList; + } + /** * Overridden to take into account that the childLM may be the block's * {@link LineLayoutManager}. @@ -132,25 +203,117 @@ // 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); } else { - return childLM.getNextKnuthElements(childLC, alignment, + childElements = childLM.getNextKnuthElements(childLC, alignment, lmStack, restartPosition, restartAtLM); } } + + if (isFirst) { + // handle orphans + int boxCount = 0; + ListElement current, previous = null; + for (ListIterator it = childElements.listIterator(); it.hasNext();) { + current = it.next(); + if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { + // non-auxiliary box => assume a 'line' and increase boxCount + boxCount++; + if (boxCount >= orphans) { + break; - } + } + } else if (current.isGlue()) { + if (previous != null && previous.isBox()) { + 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(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, true)); + it.next(); + } + } + } else if (current.isPenalty() || current instanceof BreakElement + && !current.isForcedBreak()) { + if (previous != null && previous.isBox() + && it.hasNext() && it.next().isBox()) { + // penalty or BreakElement in between boxes => remove + it.previous(); + it.previous(); + it.remove(); + } else { + it.previous(); + it.previous(); + if (current.isPenalty()) { + KnuthPenalty penalty = (KnuthPenalty)current; + //Convert penalty to break inhibitor + if (penalty.getPenalty() < KnuthPenalty.INFINITE) { + it.set(new KnuthPenalty(penalty.getWidth(), KnuthPenalty.INFINITE, + penalty.isPenaltyFlagged(), penalty.getPosition(), + penalty.isAuxiliary())); + } + } else { + // a BreakElement + BreakElement breakEl = (BreakElement)current; + if (breakEl.getPenaltyValue() < KnuthPenalty.INFINITE) { + breakEl.setPenaltyValue(KnuthPenalty.INFINITE); + } + } + } + } + previous = current; + } + } + 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 first childLM did not produce enough + // lines to satisfy this LM's orphans constraint, avoid adding a break possibility + boolean orphansSatisfied = false; + int boxCount = 0; + for (ListElement el : contentList) { + if (el.isBox() && !((KnuthBox) el).isAuxiliary()) { + boxCount++; + if (boxCount >= orphans) { + orphansSatisfied = true; + break; + } + } + } + + if (!orphansSatisfied) { + return; + } + } + + super.addInBetweenBreak(contentList, parentLC, childLC); + } + private void resetSpaces() { this.discardBorderBefore = false; this.discardBorderAfter = false; --- src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision 1067698) +++ src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision ) @@ -155,7 +155,7 @@ private final int follow; private AlignmentContext alignmentContext; - private List knuthParagraphs; + private List knuthParagraphs; private LineLayoutPossibilities lineLayouts; private LineLayoutPossibilities[] lineLayoutsList; @@ -400,7 +400,7 @@ // true if this line contains only zero-height, auxiliary boxes // and the actual line width is 0; in this case, the line "collapses" // i.e. the line area will have bpd = 0 - boolean bZeroHeightLine = (difference == ipd); + boolean isZeroHeightLine = (difference == ipd); // if line-stacking-strategy is "font-height", the line height // is not affected by its content @@ -436,9 +436,9 @@ } lastAC = ac; } - if (bZeroHeightLine + if (isZeroHeightLine && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) { - bZeroHeightLine = false; + isZeroHeightLine = false; } } } @@ -450,7 +450,7 @@ constantLineHeight = lineLead + lineFollow; - if (bZeroHeightLine) { + if (isZeroHeightLine) { return new LineBreakPosition(thisLLM, knuthParagraphs.indexOf(par), firstElementIndex, lastElementIndex, @@ -580,7 +580,7 @@ //PHASE 1: Create Knuth elements if (knuthParagraphs == null) { // it's the first time this method is called - knuthParagraphs = new ArrayList(); + knuthParagraphs = new ArrayList(); // here starts Knuth's algorithm collectInlineKnuthElements(context); @@ -612,12 +612,10 @@ LeafPosition restartPosition) { log.trace("Restarting line breaking from index " + restartPosition.getIndex()); int parIndex = restartPosition.getLeafPos(); - Paragraph paragraph = (Paragraph) knuthParagraphs.get(parIndex); - for (int i = 0; i <= restartPosition.getIndex(); i++) { - paragraph.remove(0); - } - Iterator iter = paragraph.iterator(); - while (iter.hasNext() && !((KnuthElement) iter.next()).isBox()) { + KnuthSequence paragraph = knuthParagraphs.get(parIndex); + paragraph.subList(0, restartPosition.getIndex()).clear(); + Iterator iter = paragraph.iterator(); + while (iter.hasNext() && !iter.next().isBox()) { iter.remove(); } if (!iter.hasNext()) { @@ -650,8 +648,8 @@ Paragraph lastPar = null; - InlineLevelLayoutManager curLM; - while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { + InlineLevelLayoutManager curLM = (InlineLevelLayoutManager) getChildLM(); + while (curLM != null) { List inlineElements = curLM.getNextKnuthElements(inlineLC, effectiveAlignment); if (inlineElements == null || inlineElements.size() == 0) { /* curLM.getNextKnuthElements() returned null or an empty list; @@ -748,7 +746,9 @@ } } } // end of loop over returnedList + curLM = (InlineLevelLayoutManager) getChildLM(); } + if (lastPar != null) { lastPar.endParagraph(); ElementListObserver.observe(lastPar, "line", fobj.getId()); @@ -765,13 +765,13 @@ * @param context the layout context * @return a list of Knuth elements representing broken lines */ - private List createLineBreaks(int alignment, LayoutContext context) { + private List createLineBreaks(int alignment, LayoutContext context) { // find the optimal line breaking points for each paragraph - Iterator paragraphsIterator = knuthParagraphs.iterator(); + Iterator paragraphsIterator = knuthParagraphs.iterator(); lineLayoutsList = new LineLayoutPossibilities[knuthParagraphs.size()]; LineLayoutPossibilities llPoss; for (int i = 0; paragraphsIterator.hasNext(); i++) { - KnuthSequence seq = (KnuthSequence) paragraphsIterator.next(); + KnuthSequence seq = paragraphsIterator.next(); if (!seq.isInlineSequence()) { // This set of line layout possibilities does not matter; // we only need an entry in lineLayoutsList. @@ -807,7 +807,7 @@ lineHeight.getValue(this), lead, follow, (knuthParagraphs.indexOf(currPar) == 0), hyphenationLadderCount.getEnum() == EN_NO_LIMIT - ? 0 : hyphenationLadderCount.getValue(), + ? 0 : hyphenationLadderCount.getValue(), this); alg.setConstantLineWidth(ipd); boolean canWrap = (wrapOption != EN_NO_WRAP); @@ -873,12 +873,15 @@ * @param context the layout context * @return the newly built element list */ - private List postProcessLineBreaks(int alignment, LayoutContext context) { + private List postProcessLineBreaks(int alignment, LayoutContext context) { List returnList = new LinkedList(); int endIndex = -1; for (int p = 0; p < knuthParagraphs.size(); p++) { + + KnuthSequence seq = knuthParagraphs.get(p); + LineLayoutPossibilities llPoss = lineLayoutsList[p]; // penalty between paragraphs if (p > 0) { Keep keep = getKeepTogether(); @@ -889,9 +892,6 @@ context)); } - LineLayoutPossibilities llPoss = lineLayoutsList[p]; - KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(p); - if (!seq.isInlineSequence()) { List targetList = new LinkedList(); ListIterator listIter = seq.listIterator(); @@ -918,10 +918,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( @@ -1149,7 +1146,7 @@ /** {@inheritDoc} */ public List getChangedKnuthElements(List oldList, int alignment) { - List returnList = new LinkedList(); + List returnList = new LinkedList(); for (int p = 0; p < knuthParagraphs.size(); p++) { LineLayoutPossibilities llPoss = lineLayoutsList[p]; //log.debug("demerits of the chosen layout: " + llPoss.getChosenDemerits()); @@ -1392,7 +1389,7 @@ public void addAreas(PositionIterator parentIter, LayoutContext context) { while (parentIter.hasNext()) { - Position pos = (Position) parentIter.next(); + Position pos = parentIter.next(); boolean isLastPosition = !parentIter.hasNext(); if (pos instanceof LineBreakPosition) { addInlineArea(context, (LineBreakPosition) pos, isLastPosition); @@ -1426,12 +1423,12 @@ (lbp.getLeafPos() < seq.size() - 1 ? textAlignment : textAlignmentLast), lbp.difference, lbp.availableStretch, lbp.availableShrink); if (lbp.startIndent != 0) { - lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent)); + lineArea.addTrait(Trait.START_INDENT, lbp.startIndent); } lineArea.setBPD(lbp.lineHeight); lineArea.setIPD(lbp.lineWidth); - lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore)); - lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter)); + lineArea.addTrait(Trait.SPACE_BEFORE, lbp.spaceBefore); + lineArea.addTrait(Trait.SPACE_AFTER, lbp.spaceAfter); alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline); if (seq instanceof Paragraph) {