git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1875645 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_5
@@ -78,6 +78,8 @@ public abstract class FObj extends FONode implements Constants { | |||
private String layer; | |||
// End of property values | |||
private boolean forceKeepTogether; | |||
/** | |||
* Create a new formatting object. | |||
* | |||
@@ -775,6 +777,14 @@ public abstract class FObj extends FONode implements Constants { | |||
return (super.toString() + "[@id=" + this.id + "]"); | |||
} | |||
public boolean isForceKeepTogether() { | |||
return forceKeepTogether; | |||
} | |||
public void setForceKeepTogether(boolean b) { | |||
forceKeepTogether = b; | |||
} | |||
/** Basic {@link FONode.FONodeIterator} implementation */ | |||
public static class FObjIterator implements FONodeIterator { | |||
@@ -19,16 +19,12 @@ | |||
package org.apache.fop.layoutmgr; | |||
import java.util.Collections; | |||
import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.events.EventBroadcaster; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode; | |||
import org.apache.fop.traits.MinOptMax; | |||
@@ -42,9 +38,9 @@ public abstract class AbstractBreaker { | |||
/** logging instance */ | |||
protected static final Log log = LogFactory.getLog(AbstractBreaker.class); | |||
private LayoutManager originalRestartAtLM; | |||
private Position positionAtBreak; | |||
private List firstElementsForRestart; | |||
protected LayoutManager originalRestartAtLM; | |||
protected Position positionAtBreak; | |||
protected List firstElementsForRestart; | |||
protected PageSequenceLayoutManager pslm; | |||
/** | |||
@@ -433,12 +429,16 @@ public abstract class AbstractBreaker { | |||
} | |||
firstElementsForRestart = null; | |||
LayoutManager restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, | |||
visitedBefore, blockList, 1); | |||
RestartAtLM restartAtLMClass = new RestartAtLM(); | |||
LayoutManager restartAtLM = restartAtLMClass.getRestartAtLM(this, alg, ipdChangesOnNextPage, | |||
onLastPageAndIPDChanges, visitedBefore, blockList, 1); | |||
if (restartAtLMClass.invalidPosition) { | |||
return false; | |||
} | |||
if (restartAtLM == null || restartAtLM.getChildLMs().isEmpty()) { | |||
firstElementsForRestart = null; | |||
LayoutManager restartAtLM2 = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, | |||
visitedBefore, blockList, 0); | |||
LayoutManager restartAtLM2 = new RestartAtLM().getRestartAtLM(this, alg, ipdChangesOnNextPage, | |||
onLastPageAndIPDChanges, visitedBefore, blockList, 0); | |||
if (restartAtLM2 != null) { | |||
restartAtLM = restartAtLM2; | |||
} | |||
@@ -465,101 +465,6 @@ public abstract class AbstractBreaker { | |||
return true; | |||
} | |||
private LayoutManager getRestartAtLM(PageBreakingAlgorithm alg, boolean ipdChangesOnNextPage, | |||
boolean onLastPageAndIPDChanges, boolean visitedBefore, | |||
BlockSequence blockList, int start) { | |||
KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg | |||
.getBestNodeForLastPage(); | |||
if (onLastPageAndIPDChanges && visitedBefore && this.originalRestartAtLM == null) { | |||
optimalBreak = null; | |||
} | |||
int positionIndex = findPositionIndex(optimalBreak, alg, start); | |||
if (ipdChangesOnNextPage || (positionAtBreak != null && positionAtBreak.getIndex() > -1)) { | |||
firstElementsForRestart = Collections.EMPTY_LIST; | |||
if (ipdChangesOnNextPage) { | |||
if (containsNonRestartableLM(positionAtBreak)) { | |||
if (alg.getIPDdifference() > 0) { | |||
EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj() | |||
.getUserAgent().getEventBroadcaster(); | |||
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider | |||
.get(eventBroadcaster); | |||
eventProducer.nonRestartableContentFlowingToNarrowerPage(this); | |||
} | |||
firstElementsForRestart = new LinkedList(); | |||
boolean boxFound = false; | |||
Iterator iter = blockList.listIterator(positionIndex + 1); | |||
Position position = null; | |||
while (iter.hasNext() | |||
&& (position == null || containsNonRestartableLM(position))) { | |||
positionIndex++; | |||
KnuthElement element = (KnuthElement) iter.next(); | |||
position = element.getPosition(); | |||
if (element.isBox()) { | |||
boxFound = true; | |||
firstElementsForRestart.add(element); | |||
} else if (boxFound) { | |||
firstElementsForRestart.add(element); | |||
} | |||
} | |||
if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { | |||
/* Retrieve the original position wrapped into this space position */ | |||
positionAtBreak = position.getPosition(); | |||
} else { | |||
positionAtBreak = null; | |||
} | |||
} | |||
} | |||
} | |||
LayoutManager restartAtLM = null; | |||
if (ipdChangesOnNextPage || !(positionAtBreak != null && positionAtBreak.getIndex() > -1)) { | |||
if (positionAtBreak != null && positionAtBreak.getIndex() == -1) { | |||
Position position; | |||
Iterator iter = blockList.listIterator(positionIndex + 1); | |||
do { | |||
KnuthElement nextElement = (KnuthElement) iter.next(); | |||
position = nextElement.getPosition(); | |||
} while (position == null | |||
|| position instanceof SpaceResolver.SpaceHandlingPosition | |||
|| position instanceof SpaceResolver.SpaceHandlingBreakPosition | |||
&& position.getPosition().getIndex() == -1); | |||
LayoutManager surroundingLM = positionAtBreak.getLM(); | |||
while (position.getLM() != surroundingLM) { | |||
position = position.getPosition(); | |||
} | |||
restartAtLM = position.getPosition().getLM(); | |||
} | |||
if (onLastPageAndIPDChanges && restartAtLM != null) { | |||
if (originalRestartAtLM == null) { | |||
originalRestartAtLM = restartAtLM; | |||
} else { | |||
restartAtLM = originalRestartAtLM; | |||
} | |||
firstElementsForRestart = Collections.EMPTY_LIST; | |||
} | |||
} | |||
if (onLastPageAndIPDChanges && !visitedBefore && positionAtBreak.getPosition() != null) { | |||
restartAtLM = positionAtBreak.getPosition().getLM(); | |||
} | |||
return restartAtLM; | |||
} | |||
private int findPositionIndex(KnuthNode optimalBreak, PageBreakingAlgorithm alg, int start) { | |||
int positionIndex = (optimalBreak != null) ? optimalBreak.position : start; | |||
for (int i = positionIndex; i < alg.par.size(); i++) { | |||
KnuthElement elementAtBreak = alg.getElement(i); | |||
if (elementAtBreak.getPosition() == null) { | |||
elementAtBreak = alg.getElement(0); | |||
} | |||
positionAtBreak = elementAtBreak.getPosition(); | |||
/* Retrieve the original position wrapped into this space position */ | |||
positionAtBreak = positionAtBreak.getPosition(); | |||
if (positionAtBreak != null) { | |||
return i; | |||
} | |||
} | |||
return positionIndex; | |||
} | |||
/** | |||
* Returns {@code true} if the given position or one of its descendants | |||
* corresponds to a non-restartable LM. | |||
@@ -567,7 +472,7 @@ public abstract class AbstractBreaker { | |||
* @param position a position | |||
* @return {@code true} if there is a non-restartable LM in the hierarchy | |||
*/ | |||
private boolean containsNonRestartableLM(Position position) { | |||
protected boolean containsNonRestartableLM(Position position) { | |||
LayoutManager lm = position.getLM(); | |||
if (lm != null && !lm.isRestartable()) { | |||
return true; |
@@ -833,6 +833,9 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager | |||
public Keep getKeepTogether() { | |||
Keep keep = Keep.getKeep(getKeepTogetherProperty()); | |||
keep = keep.compare(getParentKeepTogether()); | |||
if (getFObj().isForceKeepTogether()) { | |||
keep = Keep.KEEP_ALWAYS; | |||
} | |||
return keep; | |||
} | |||
@@ -0,0 +1,133 @@ | |||
/* | |||
* 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$ */ | |||
package org.apache.fop.layoutmgr; | |||
import java.util.Collections; | |||
import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import org.apache.fop.events.EventBroadcaster; | |||
/** | |||
* Class to find the restart layoutmanager for changing IPD | |||
*/ | |||
class RestartAtLM { | |||
protected boolean invalidPosition; | |||
protected LayoutManager getRestartAtLM(AbstractBreaker breaker, PageBreakingAlgorithm alg, | |||
boolean ipdChangesOnNextPage, boolean onLastPageAndIPDChanges, | |||
boolean visitedBefore, AbstractBreaker.BlockSequence blockList, int start) { | |||
BreakingAlgorithm.KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg | |||
.getBestNodeForLastPage(); | |||
if (onLastPageAndIPDChanges && visitedBefore && breaker.originalRestartAtLM == null) { | |||
optimalBreak = null; | |||
} | |||
int positionIndex = findPositionIndex(breaker, optimalBreak, alg, start); | |||
if (ipdChangesOnNextPage || (breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() > -1)) { | |||
breaker.firstElementsForRestart = Collections.EMPTY_LIST; | |||
if (ipdChangesOnNextPage) { | |||
if (breaker.containsNonRestartableLM(breaker.positionAtBreak)) { | |||
if (alg.getIPDdifference() > 0) { | |||
EventBroadcaster eventBroadcaster = breaker.getCurrentChildLM().getFObj() | |||
.getUserAgent().getEventBroadcaster(); | |||
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider | |||
.get(eventBroadcaster); | |||
eventProducer.nonRestartableContentFlowingToNarrowerPage(this); | |||
} | |||
breaker.firstElementsForRestart = new LinkedList(); | |||
boolean boxFound = false; | |||
Iterator iter = blockList.listIterator(positionIndex + 1); | |||
Position position = null; | |||
while (iter.hasNext() | |||
&& (position == null || breaker.containsNonRestartableLM(position))) { | |||
positionIndex++; | |||
KnuthElement element = (KnuthElement) iter.next(); | |||
position = element.getPosition(); | |||
if (element.isBox()) { | |||
boxFound = true; | |||
breaker.firstElementsForRestart.add(element); | |||
} else if (boxFound) { | |||
breaker.firstElementsForRestart.add(element); | |||
} | |||
} | |||
if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { | |||
/* Retrieve the original position wrapped into this space position */ | |||
breaker.positionAtBreak = position.getPosition(); | |||
} else { | |||
breaker.positionAtBreak = null; | |||
} | |||
} | |||
} | |||
} | |||
LayoutManager restartAtLM = null; | |||
if (ipdChangesOnNextPage || !(breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() > -1)) { | |||
if (breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() == -1) { | |||
Position position; | |||
Iterator iter = blockList.listIterator(positionIndex + 1); | |||
do { | |||
KnuthElement nextElement = (KnuthElement) iter.next(); | |||
position = nextElement.getPosition(); | |||
} while (position == null | |||
|| position instanceof SpaceResolver.SpaceHandlingPosition | |||
|| position instanceof SpaceResolver.SpaceHandlingBreakPosition | |||
&& position.getPosition().getIndex() == -1); | |||
LayoutManager surroundingLM = breaker.positionAtBreak.getLM(); | |||
while (position.getLM() != surroundingLM) { | |||
position = position.getPosition(); | |||
} | |||
if (position.getPosition() == null) { | |||
position.getLM().getFObj().setForceKeepTogether(true); | |||
invalidPosition = true; | |||
return null; | |||
} | |||
restartAtLM = position.getPosition().getLM(); | |||
} | |||
if (onLastPageAndIPDChanges && restartAtLM != null) { | |||
if (breaker.originalRestartAtLM == null) { | |||
breaker.originalRestartAtLM = restartAtLM; | |||
} else { | |||
restartAtLM = breaker.originalRestartAtLM; | |||
} | |||
breaker.firstElementsForRestart = Collections.EMPTY_LIST; | |||
} | |||
} | |||
if (onLastPageAndIPDChanges && !visitedBefore && breaker.positionAtBreak.getPosition() != null) { | |||
restartAtLM = breaker.positionAtBreak.getPosition().getLM(); | |||
} | |||
return restartAtLM; | |||
} | |||
private int findPositionIndex(AbstractBreaker breaker, BreakingAlgorithm.KnuthNode optimalBreak, | |||
PageBreakingAlgorithm alg, int start) { | |||
int positionIndex = (optimalBreak != null) ? optimalBreak.position : start; | |||
for (int i = positionIndex; i < alg.par.size(); i++) { | |||
KnuthElement elementAtBreak = alg.getElement(i); | |||
if (elementAtBreak.getPosition() == null) { | |||
elementAtBreak = alg.getElement(0); | |||
} | |||
breaker.positionAtBreak = elementAtBreak.getPosition(); | |||
/* Retrieve the original position wrapped into this space position */ | |||
breaker.positionAtBreak = breaker.positionAtBreak.getPosition(); | |||
if (breaker.positionAtBreak != null) { | |||
return i; | |||
} | |||
} | |||
return positionIndex; | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
<?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 that the definition of a special page-master for the last page with a | |||
different width that the previous "rest" page causes FOP to redo the line breaking layout. | |||
</p> | |||
</info> | |||
<fo> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master margin-bottom="8mm" master-name="PageFront" page-width="210mm" page-height="297mm" margin-right="20mm" margin-top="15mm" margin-left="20mm"> | |||
<fo:region-body margin-right="58mm" margin-top="35mm" margin-bottom="20mm" margin-left="0mm" region-name="PageBody"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master margin-bottom="8mm" master-name="PageRest" page-width="210mm" page-height="297mm" margin-right="20mm" margin-top="15mm" margin-left="20mm"> | |||
<fo:region-body margin-bottom="0mm" margin-top="0mm" margin-right="0mm" margin-left="0mm" region-name="PageBody"/> | |||
</fo:simple-page-master> | |||
<fo:page-sequence-master master-name="LetterPages"> | |||
<fo:repeatable-page-master-alternatives> | |||
<fo:conditional-page-master-reference page-position="first" master-reference="PageFront"/> | |||
<fo:conditional-page-master-reference page-position="rest" master-reference="PageRest"/> | |||
<fo:conditional-page-master-reference page-position="last" master-reference="PageRest"/> | |||
</fo:repeatable-page-master-alternatives> | |||
</fo:page-sequence-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence format="1" id="th_default_sequence1" initial-page-number="auto" force-page-count="auto" master-reference="LetterPages"> | |||
<fo:flow flow-name="PageBody"> | |||
<fo:block-container height="210mm"> | |||
<fo:block>test</fo:block> | |||
</fo:block-container> | |||
<fo:block font-size="9pt" line-height="11pt"> | |||
<fo:inline>Step 2 – Finance<fo:block/> | |||
</fo:inline> | |||
If there is any Hire Purchase (HP) / Finance agreement associated with this vehicle, please contact the finance company to provide your authorisation for Bank Box to liaise with them in order to obtain the early settlement figure.</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<eval expected="test" xpath="//pageViewport[1]//lineArea[1]//text[1]/word[1]"/> | |||
<eval expected="Step" xpath="//pageViewport[2]//lineArea[1]//text[1]/word[1]"/> | |||
<eval expected="2" xpath="count(//pageViewport)"/> | |||
</checks> | |||
</testcase> |