ASF Bugzilla – Attachment 26845 Details for
Bug 44328
[PATCH] orphans/widows not respected in some cases
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
updated patch, including two tests for orphans/widows
b44328.patch (text/plain), 24.65 KB, created by
Andreas L. Delmelle
on 2011-04-02 17:14:10 UTC
(
hide
)
Description:
updated patch, including two tests for orphans/widows
Filename:
MIME Type:
Creator:
Andreas L. Delmelle
Created:
2011-04-02 17:14:10 UTC
Size:
24.65 KB
patch
obsolete
>Index: test/layoutengine/standard-testcases/block_widows.xml >=================================================================== >--- test/layoutengine/standard-testcases/block_widows.xml (revision ) >+++ test/layoutengine/standard-testcases/block_widows.xml (revision ) >@@ -0,0 +1,143 @@ >+<?xml version="1.0" encoding="UTF-8"?> >+<!-- >+ Licensed to the Apache Software Foundation (ASF) under one or more >+ contributor license agreements. See the NOTICE file distributed with >+ this work for additional information regarding copyright ownership. >+ The ASF licenses this file to You under the Apache License, Version 2.0 >+ (the "License"); you may not use this file except in compliance with >+ the License. You may obtain a copy of the License at >+ >+ http://www.apache.org/licenses/LICENSE-2.0 >+ >+ Unless required by applicable law or agreed to in writing, software >+ distributed under the License is distributed on an "AS IS" BASIS, >+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ See the License for the specific language governing permissions and >+ limitations under the License. >+--> >+<!-- $Id$ --> >+<testcase> >+ <info> >+ <p> >+ This test checks the default behavior of the "widows" property. >+ </p> >+ </info> >+ <fo> >+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> >+ <fo:layout-master-set> >+ <fo:simple-page-master master-name="normal" page-width="3in" page-height="2in"> >+ <fo:region-body /> >+ </fo:simple-page-master> >+ </fo:layout-master-set> >+ <fo:page-sequence master-reference="normal"> >+ <fo:flow flow-name="xsl-region-body"> >+ <fo:block id="block-1"> >+ <!-- Basic test: simple paragraph generating 11 lines. >+ 2 last lines should be kept together due to initial >+ value for widows >+ --> >+ 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 >+ </fo:block> >+ <fo:block id="block-2" break-before="page"> >+ <!-- Basic test: simple paragraph generating 11 lines, followed by >+ another block. >+ A break between lines 10 and 11 is allowed. >+ --> >+ 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 >+ <fo:block> >+ Lorem ipsum dolor sit amet, consectetur >+ adipiscing elit. Cras placerat, lectus vel >+ iaculis euismod, ipsum enim dapibus >+ urna, eu pellentesque velit dolor ac >+ </fo:block> >+ </fo:block> >+ <fo:block id="block-3" break-before="page"> >+ <!-- Basic test: 1 paragraph consisting of 11 single-line subparagraphs. >+ Last 2 lines of the main paragraph should be kept together due to >+ initial value for widows. >+ --> >+ <fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ </fo:block> >+ </fo:block> >+ <fo:block id="block-4" break-before="page"> >+ <!-- Special case: linefeed-treatment="preserve" (see Bugzilla 44328) --> >+ <fo:block linefeed-treatment="preserve">Lorem ipsum >+ dolor sit amet, >+ consectetur >+ adipiscing elit. >+ Cras placerat, >+ lectus vel >+ iaculis euismod, >+ ipsum enim >+ dapibus urna, >+ eu pellentesque >+ velit dolor</fo:block> >+ </fo:block> >+ <fo:block id="block-5" break-before="page"> >+ <!-- Special case: 1 paragraph with 10 lines, and 1 with 1. >+ Last 2 lines of the first paragraph should be kept >+ together with the only line of the second due to initial >+ value of widows on the outer block. >+ --> >+ <fo:block> >+ 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 >+ </fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ </fo:block> >+ </fo:flow> >+ </fo:page-sequence> >+ </fo:root> >+ </fo> >+ <checks> >+ <eval expected="9" xpath="count((//page)[1]//lineArea)" /> >+ <eval expected="2" xpath="count((//page)[2]//lineArea)" /> >+ <eval expected="10" xpath="count((//page)[3]//lineArea)" /> >+ <eval expected="5" xpath="count((//page)[4]//lineArea)" /> >+ <eval expected="9" xpath="count((//page)[5]//lineArea)" /> >+ <eval expected="2" xpath="count((//page)[6]//lineArea)" /> >+ <eval expected="9" xpath="count((//page)[7]//lineArea)" /> >+ <eval expected="2" xpath="count((//page)[8]//lineArea)" /> >+ <eval expected="8" xpath="count((//page)[9]//lineArea)" /> >+ <eval expected="3" xpath="count((//page)[10]//lineArea)" /> >+ </checks> >+</testcase> >\ No newline at end of file >Index: test/layoutengine/standard-testcases/block_orphans.xml >=================================================================== >--- test/layoutengine/standard-testcases/block_orphans.xml (revision ) >+++ test/layoutengine/standard-testcases/block_orphans.xml (revision ) >@@ -0,0 +1,167 @@ >+<?xml version="1.0" encoding="UTF-8"?> >+<!-- >+ Licensed to the Apache Software Foundation (ASF) under one or more >+ contributor license agreements. See the NOTICE file distributed with >+ this work for additional information regarding copyright ownership. >+ The ASF licenses this file to You under the Apache License, Version 2.0 >+ (the "License"); you may not use this file except in compliance with >+ the License. You may obtain a copy of the License at >+ >+ http://www.apache.org/licenses/LICENSE-2.0 >+ >+ Unless required by applicable law or agreed to in writing, software >+ distributed under the License is distributed on an "AS IS" BASIS, >+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ See the License for the specific language governing permissions and >+ limitations under the License. >+--> >+<!-- $Id$ --> >+<testcase> >+ <info> >+ <p> >+ This test checks the default behavior of the "orphans" property. >+ </p> >+ </info> >+ <fo> >+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> >+ <fo:layout-master-set> >+ <fo:simple-page-master master-name="normal" page-width="3in" page-height="2in"> >+ <fo:region-body /> >+ </fo:simple-page-master> >+ </fo:layout-master-set> >+ <fo:page-sequence master-reference="normal"> >+ <fo:flow flow-name="xsl-region-body"> >+ <fo:block id="block-1" break-before="page"> >+ <!-- Basic test: 1 paragraph with 9 lines and 1 with 4. >+ All 4 lines of the second paragraph should be kept >+ together due to initial value for orphans. >+ --> >+ <fo:block> >+ 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, >+ </fo:block> >+ <fo:block> >+ Lorem ipsum dolor sit amet, consectetur >+ adipiscing elit. Cras placerat, lectus vel >+ iaculis euismod, ipsum enim dapibus >+ urna, eu pellentesque velit dolor ac >+ </fo:block> >+ </fo:block> >+ <fo:block id="block-2" break-before="page"> >+ <!-- Basic test: 1 block with 9 lines and 1 paragraph with 4 lines. >+ A break after the first line of the second paragraph is allowed. >+ --> >+ <fo:block> >+ 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, >+ </fo:block> >+ Lorem ipsum dolor sit amet, consectetur >+ adipiscing elit. Cras placerat, lectus vel >+ iaculis euismod, ipsum enim dapibus >+ urna, eu pellentesque velit dolor ac >+ </fo:block> >+ <fo:block id="block-3" break-before="page"> >+ <!-- Basic test: 1 regular paragraph of 9 lines followed by 1 consisting >+ of 4 single-line subparagraphs. >+ All 4 lines of the second paragraph should be kept together due >+ to initial value for orphans. >+ --> >+ <fo:block id="block-3a"> >+ 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, >+ </fo:block> >+ <fo:block id="block-3b"> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block>Lorem ipsum</fo:block> >+ </fo:block> >+ </fo:block> >+ <fo:block id="block-4" break-before="page"> >+ <!-- Special case: linefeed-treatment="preserve" (see Bugzilla 44328) --> >+ <fo:block> >+ 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, >+ </fo:block> >+ <fo:block linefeed-treatment="preserve">Lorem ipsum >+ dolor sit amet, >+ consectetur >+ velit dolor</fo:block> >+ </fo:block> >+ <fo:block id="block-5" break-before="page" widows="1"> >+ <!-- Last but not least, demonstrate peculiar side-effect in case >+ of convoluted nesting... >+ Even though widows would allow a break before the last line, >+ all 10 last lines are moved to the next page due to stacked >+ orphans constraints >+ --> >+ <fo:block id="level-1">Lorem ipsum >+ <fo:block>Lorem ipsum</fo:block> >+ <fo:block id="level-2">Lorem ipsum >+ <fo:block id="level-3">Lorem ipsum >+ <fo:block id="level-4">Lorem ipsum >+ <fo:block id="level-5">Lorem ipsum >+ <fo:block id="level-6">Lorem ipsum >+ <fo:block id="level-7">Lorem ipsum >+ <fo:block id="level-8">Lorem ipsum >+ <fo:block id="level-9">Lorem ipsum >+ <fo:block id="level-10">Lorem ipsum >+ <fo:block id="level-11"> >+ Lorem ipsum >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:block> >+ </fo:flow> >+ </fo:page-sequence> >+ </fo:root> >+ </fo> >+ <checks> >+ <eval expected="9" xpath="count((//page)[1]//lineArea)" /> >+ <eval expected="4" xpath="count((//page)[2]//lineArea)" /> >+ <eval expected="10" xpath="count((//page)[3]//lineArea)" /> >+ <eval expected="3" xpath="count((//page)[4]//lineArea)" /> >+ <eval expected="9" xpath="count((//page)[5]//lineArea)" /> >+ <eval expected="4" xpath="count((//page)[6]//lineArea)" /> >+ <eval expected="9" xpath="count((//page)[7]//lineArea)" /> >+ <eval expected="4" xpath="count((//page)[8]//lineArea)" /> >+ <eval expected="2" xpath="count((//page)[9]//lineArea)" /> >+ <eval expected="10" xpath="count((//page)[10]//lineArea)" /> >+ </checks> >+</testcase> >Index: src/java/org/apache/fop/layoutmgr/KnuthPenalty.java >=================================================================== >--- 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; >Index: src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java >=================================================================== >--- 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,35 @@ > public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, > Position restartPosition, LayoutManager restartAtLM) { > resetSpaces(); >- return super.getNextKnuthElements( >+ >+ List<ListElement> contentList = super.getNextKnuthElements( > context, alignment, lmStack, restartPosition, restartAtLM); >+ >+ if (!this.hasNextChildLM()) { >+ // handle widows >+ int boxCount = 0, index = contentList.size(); >+ ListElement current; >+ for (ListIterator<ListElement> it = contentList.listIterator(contentList.size()); >+ it.hasPrevious();) { >+ current = it.previous(); >+ index--; >+ if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { >+ // non-auxiliary box => assume a 'line' and increase boxCount >+ boxCount++; >+ if (boxCount >= widows) { >+ break; >- } >+ } >+ } >+ } >+ 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 +162,78 @@ > // 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<ListElement> 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, endIndex = 0; >+ ListElement current; >+ for (ListIterator<ListElement> it = childElements.listIterator(); it.hasNext();) { >+ current = it.next(); >+ endIndex++; >+ if (current.isBox() && !((KnuthBox) current).isAuxiliary()) { >+ // non-auxiliary box => assume a 'line' and increase boxCount >+ boxCount++; >+ if (boxCount >= orphans) { >+ break; >- } >+ } >+ } >+ } >+ 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<ListElement> 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 >+ int boxCount = 0; >+ for (ListElement el : contentList) { >+ if (el.isBox() && !((KnuthBox) el).isAuxiliary()) { >+ boxCount++; >+ if (boxCount >= orphans) { >+ break; >+ } >+ } >+ } >+ >+ if (boxCount < orphans) { >+ return; >+ } >+ } >+ >+ super.addInBetweenBreak(contentList, parentLC, childLC); >+ } >+ > private void resetSpaces() { > this.discardBorderBefore = false; > this.discardBorderAfter = false; >Index: src/java/org/apache/fop/layoutmgr/ElementListUtils.java >=================================================================== >--- src/java/org/apache/fop/layoutmgr/ElementListUtils.java (revision 1056513) >+++ src/java/org/apache/fop/layoutmgr/ElementListUtils.java (revision ) >@@ -133,6 +133,61 @@ > } > > /** >+ * Removes all legal break points from the given {@code List<ListElement>}. >+ * That is, this method:<br/> >+ * <ul> >+ * <li>removes penalties in between two boxes</li> >+ * <li>converts other penalties to inhibit breaks</li> >+ * <li>converts glues following a box into auxiliary boxes >+ * or penalty-glue sequences</li> >+ * </ul> >+ * >+ * @param elements the element list from which the breaks will be removed >+ */ >+ public static void removeLegalBreaks(List<ListElement> elements) { >+ >+ ListElement current; >+ boolean previousIsBox = false; >+ >+ for (ListIterator<ListElement> 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; >+ } >+ } >+ >+ /** > * 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 >@@ -218,7 +273,8 @@ > int prevBreak = startIndex - 1; > while (prevBreak >= 0) { > KnuthElement el = (KnuthElement)elems.get(prevBreak); >- if (el.isPenalty() && el.getPenalty() < KnuthElement.INFINITE) { >+ if (el.isPenalty() >+ && ((KnuthElement)el).getPenalty() < KnuthElement.INFINITE) { > break; > } > prevBreak--; >Index: src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java >=================================================================== >--- src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision 1088079) >+++ src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (revision ) >@@ -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(
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 44328
:
21452
|
26800
|
26801
|
26807
|
26813
|
26820
|
26842
|
26843
|
26845
|
26846
|
26851
|
26852
|
27055
|
27066