git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@808157 13f79535-47bb-0310-9956-ffa450edef68pull/37/head
package org.apache.fop.layoutmgr; | package org.apache.fop.layoutmgr; | ||||
import java.util.List; | |||||
import java.util.Stack; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.datatypes.LengthBase; | import org.apache.fop.datatypes.LengthBase; | ||||
import org.apache.fop.datatypes.PercentBaseContext; | import org.apache.fop.datatypes.PercentBaseContext; | ||||
import org.apache.fop.fo.FObj; | import org.apache.fop.fo.FObj; | ||||
return fobj; | return fobj; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void reset() { | |||||
throw new UnsupportedOperationException("Not implemented"); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public boolean isRestartable() { | |||||
return false; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM) { | |||||
throw new UnsupportedOperationException("Not implemented"); | |||||
} | |||||
} | } |
package org.apache.fop.layoutmgr; | package org.apache.fop.layoutmgr; | ||||
import java.util.Collections; | |||||
import java.util.Iterator; | |||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.fo.Constants; | import org.apache.fop.fo.Constants; | ||||
import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode; | |||||
import org.apache.fop.traits.MinOptMax; | import org.apache.fop.traits.MinOptMax; | ||||
import org.apache.fop.util.ListUtil; | import org.apache.fop.util.ListUtil; | ||||
*/ | */ | ||||
protected abstract List getNextKnuthElements(LayoutContext context, int alignment); | protected abstract List getNextKnuthElements(LayoutContext context, int alignment); | ||||
protected List getNextKnuthElements(LayoutContext context, int alignment, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM) { | |||||
throw new UnsupportedOperationException("TODO: implement acceptable fallback"); | |||||
} | |||||
/** @return true if there's no content that could be handled. */ | /** @return true if there's no content that could be handled. */ | ||||
public boolean isEmpty() { | public boolean isEmpty() { | ||||
return (this.blockLists.isEmpty()); | return (this.blockLists.isEmpty()); | ||||
ElementListObserver.observe(elementList, "breaker", null); | ElementListObserver.observe(elementList, "breaker", null); | ||||
} | } | ||||
/** | |||||
* Starts the page breaking process. | |||||
* @param flowBPD the constant available block-progression-dimension (used for every part) | |||||
*/ | |||||
public void doLayout(int flowBPD) { | |||||
doLayout(flowBPD, false); | |||||
} | |||||
/** | /** | ||||
* Starts the page breaking process. | * Starts the page breaking process. | ||||
* @param flowBPD the constant available block-progression-dimension (used for every part) | * @param flowBPD the constant available block-progression-dimension (used for every part) | ||||
getPageProvider(), createLayoutListener(), | getPageProvider(), createLayoutListener(), | ||||
alignment, alignmentLast, footnoteSeparatorLength, | alignment, alignmentLast, footnoteSeparatorLength, | ||||
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored()); | isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored()); | ||||
int iOptPageCount; | |||||
BlockSequence effectiveList; | BlockSequence effectiveList; | ||||
if (getCurrentDisplayAlign() == Constants.EN_X_FILL) { | if (getCurrentDisplayAlign() == Constants.EN_X_FILL) { | ||||
effectiveList = blockList; | effectiveList = blockList; | ||||
} | } | ||||
//iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true); | |||||
alg.setConstantLineWidth(flowBPD); | alg.setConstantLineWidth(flowBPD); | ||||
iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/ | |||||
1, true, BreakingAlgorithm.ALL_BREAKS); | |||||
log.debug("PLM> iOptPageCount= " + iOptPageCount | |||||
+ " pageBreaks.size()= " + alg.getPageBreaks().size()); | |||||
int optimalPageCount = alg.findBreakingPoints(effectiveList, 1, true, | |||||
BreakingAlgorithm.ALL_BREAKS); | |||||
if (alg.ipdChanged()) { | |||||
KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange(); | |||||
int positionIndex = optimalBreak.position; | |||||
KnuthElement elementAtBreak = alg.getElement(positionIndex); | |||||
Position positionAtBreak = elementAtBreak.getPosition(); | |||||
if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) { | |||||
throw new UnsupportedOperationException( | |||||
"Don't know how to restart at position" + positionAtBreak); | |||||
} | |||||
/* Retrieve the original position wrapped into this space position */ | |||||
positionAtBreak = positionAtBreak.getPosition(); | |||||
LayoutManager restartAtLM = null; | |||||
List firstElements = Collections.EMPTY_LIST; | |||||
if (containsNonRestartableLM(positionAtBreak)) { | |||||
firstElements = new LinkedList(); | |||||
boolean boxFound = false; | |||||
Iterator iter = effectiveList.listIterator(++positionIndex); | |||||
Position position = null; | |||||
while (iter.hasNext() | |||||
&& (position == null || containsNonRestartableLM(position))) { | |||||
KnuthElement element = (KnuthElement) iter.next(); | |||||
positionIndex++; | |||||
position = element.getPosition(); | |||||
if (element.isBox()) { | |||||
boxFound = true; | |||||
firstElements.add(element); | |||||
} else if (boxFound) { | |||||
firstElements.add(element); | |||||
} | |||||
} | |||||
if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { | |||||
/* Retrieve the original position wrapped into this space position */ | |||||
positionAtBreak = position.getPosition(); | |||||
} | |||||
} | |||||
if (positionAtBreak.getIndex() == -1) { | |||||
/* | |||||
* This is an indication that we are between two blocks | |||||
* (possibly surrounded by another block), not inside a | |||||
* paragraph. | |||||
*/ | |||||
Position position; | |||||
Iterator iter = effectiveList.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(); | |||||
} | |||||
log.trace("IPD changes after page " + optimalPageCount + " at index " | |||||
+ optimalBreak.position); | |||||
doPhase3(alg, optimalPageCount, blockList, effectiveList); | |||||
blockLists.clear(); | |||||
blockListIndex = -1; | |||||
nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, | |||||
positionAtBreak, restartAtLM, firstElements); | |||||
} else { | |||||
log.debug("PLM> iOptPageCount= " + optimalPageCount | |||||
+ " pageBreaks.size()= " + alg.getPageBreaks().size()); | |||||
//*** Phase 3: Add areas *** | |||||
doPhase3(alg, iOptPageCount, blockList, effectiveList); | |||||
//*** Phase 3: Add areas *** | |||||
doPhase3(alg, optimalPageCount, blockList, effectiveList); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Returns {@code true} if the given position or one of its descendants | |||||
* corresponds to a non-restartable LM. | |||||
* | |||||
* @param position a position | |||||
* @return {@code true} if there is a non-restartable LM in the hierarchy | |||||
*/ | |||||
private boolean containsNonRestartableLM(Position position) { | |||||
LayoutManager lm = position.getLM(); | |||||
if (lm != null && !lm.isRestartable()) { | |||||
return true; | |||||
} else { | |||||
Position subPosition = position.getPosition(); | |||||
if (subPosition == null) { | |||||
return false; | |||||
} else { | |||||
return containsNonRestartableLM(subPosition); | |||||
} | |||||
} | |||||
} | |||||
/** | /** | ||||
* Phase 3 of Knuth algorithm: Adds the areas | * Phase 3 of Knuth algorithm: Adds the areas | ||||
* @param alg PageBreakingAlgorithm instance which determined the breaks | * @param alg PageBreakingAlgorithm instance which determined the breaks | ||||
protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) { | protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) { | ||||
return nextSequenceStartsOn; | return nextSequenceStartsOn; | ||||
} | } | ||||
/** | /** | ||||
* Gets the next block list (sequence) and adds it to a list of block lists if it's not empty. | * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty. | ||||
* @param childLC LayoutContext to use | * @param childLC LayoutContext to use | ||||
*/ | */ | ||||
protected int getNextBlockList(LayoutContext childLC, | protected int getNextBlockList(LayoutContext childLC, | ||||
int nextSequenceStartsOn) { | int nextSequenceStartsOn) { | ||||
return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null); | |||||
} | |||||
/** | |||||
* Gets the next block list (sequence) and adds it to a list of block lists | |||||
* if it's not empty. | |||||
* | |||||
* @param childLC LayoutContext to use | |||||
* @param nextSequenceStartsOn indicates on what page the next sequence | |||||
* should start | |||||
* @param positionAtIPDChange last element on the part before an IPD change | |||||
* @param restartAtLM the layout manager from which to restart, if IPD | |||||
* change occurs between two LMs | |||||
* @param firstElements elements from non-restartable LMs on the new page | |||||
* @return the page on which the next content should appear after a hard | |||||
* break | |||||
*/ | |||||
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM, List firstElements) { | |||||
updateLayoutContext(childLC); | updateLayoutContext(childLC); | ||||
//Make sure the span change signal is reset | //Make sure the span change signal is reset | ||||
childLC.signalSpanChange(Constants.NOT_SET); | childLC.signalSpanChange(Constants.NOT_SET); | ||||
BlockSequence blockList; | BlockSequence blockList; | ||||
List returnedList = getNextKnuthElements(childLC, alignment); | |||||
List returnedList; | |||||
if (positionAtIPDChange == null) { | |||||
returnedList = getNextKnuthElements(childLC, alignment); | |||||
} else { | |||||
returnedList = getNextKnuthElements(childLC, alignment, positionAtIPDChange, | |||||
restartAtLM); | |||||
returnedList.addAll(0, firstElements); | |||||
} | |||||
if (returnedList != null) { | if (returnedList != null) { | ||||
if (returnedList.isEmpty()) { | if (returnedList.isEmpty()) { | ||||
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn); | nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn); |
private static Log log = LogFactory.getLog(AbstractLayoutManager.class); | private static Log log = LogFactory.getLog(AbstractLayoutManager.class); | ||||
/** Parent LayoutManager for this LayoutManager */ | /** Parent LayoutManager for this LayoutManager */ | ||||
protected LayoutManager parentLM = null; | |||||
protected LayoutManager parentLM; | |||||
/** List of child LayoutManagers */ | /** List of child LayoutManagers */ | ||||
protected List childLMs = null; | |||||
protected List childLMs; | |||||
/** Iterator for child LayoutManagers */ | /** Iterator for child LayoutManagers */ | ||||
protected ListIterator fobjIter = null; | |||||
protected ListIterator fobjIter; | |||||
/** Marker map for markers related to this LayoutManager */ | /** Marker map for markers related to this LayoutManager */ | ||||
private Map markers = null; | |||||
private Map markers; | |||||
/** True if this LayoutManager has handled all of its content. */ | /** True if this LayoutManager has handled all of its content. */ | ||||
private boolean isFinished = false; | |||||
private boolean isFinished; | |||||
/** child LM during getNextKnuthElement phase */ | /** child LM during getNextKnuthElement phase */ | ||||
protected LayoutManager curChildLM = null; | |||||
protected LayoutManager curChildLM; | |||||
/** child LM iterator during getNextKnuthElement phase */ | /** child LM iterator during getNextKnuthElement phase */ | ||||
protected ListIterator childLMiter = null; | |||||
protected ListIterator childLMiter; | |||||
private int lastGeneratedPosition = -1; | private int lastGeneratedPosition = -1; | ||||
private int smallestPosNumberChecked = Integer.MAX_VALUE; | private int smallestPosNumberChecked = Integer.MAX_VALUE; | ||||
return null; | return null; | ||||
} | } | ||||
protected void setCurrentChildLM(LayoutManager childLM) { | |||||
curChildLM = childLM; | |||||
childLMiter = new LMiter(this); | |||||
do { | |||||
curChildLM = (LayoutManager) childLMiter.next(); | |||||
} while (curChildLM != childLM); | |||||
} | |||||
/** | /** | ||||
* Return indication if getChildLM will return another LM. | * Return indication if getChildLM will return another LM. | ||||
* @return true if another child LM is still available | * @return true if another child LM is still available | ||||
return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : "")); | return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : "")); | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void reset() { | |||||
isFinished = false; | |||||
curChildLM = null; | |||||
childLMiter = new LMiter(this); | |||||
/* | |||||
* Reset the children LM. Can't rely on childLMiter since it may have | |||||
* been set to null in checkEndOfLayout. | |||||
*/ | |||||
for (LMiter iter = new LMiter(this); iter.hasNext();) { | |||||
((LayoutManager) iter.next()).reset(); | |||||
} | |||||
if (fobj != null) { | |||||
markers = fobj.getMarkers(); | |||||
} | |||||
lastGeneratedPosition = -1; | |||||
} | |||||
} | } |
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void reset() { | |||||
throw new IllegalStateException(); | |||||
} | |||||
} | } |
// set space after for each LM, in order to implement | // set space after for each LM, in order to implement | ||||
// display-align = distribute | // display-align = distribute | ||||
lc.setSpaceAfter(layoutContext.getSpaceAfter()); | lc.setSpaceAfter(layoutContext.getSpaceAfter()); | ||||
lc.setStackLimitsFrom(layoutContext); | |||||
lc.setStackLimitBP(layoutContext.getStackLimitBP()); | |||||
childLM.addAreas(childPosIter, lc); | childLM.addAreas(childPosIter, lc); | ||||
} | } | ||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import java.util.Stack; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
return returnList; | return returnList; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||||
Position restartPosition, LayoutManager restartAtLM) { | |||||
resetSpaces(); | |||||
if (isAbsoluteOrFixed()) { | |||||
return getNextKnuthElementsAbsolute(context, alignment); | |||||
} | |||||
autoHeight = false; | |||||
//boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); | |||||
int maxbpd = context.getStackLimitBP().opt; | |||||
int allocBPD; | |||||
if (height.getEnum() == EN_AUTO | |||||
|| (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) { | |||||
//auto height when height="auto" or "if that dimension is not specified explicitly | |||||
//(i.e., it depends on content's block-progression-dimension)" (XSL 1.0, 7.14.1) | |||||
allocBPD = maxbpd; | |||||
autoHeight = true; | |||||
if (getBlockContainerFO().getReferenceOrientation() == 0) { | |||||
//Cannot easily inline element list when ref-or="180" | |||||
inlineElementList = true; | |||||
} | |||||
} else { | |||||
allocBPD = height.getValue(this); //this is the content-height | |||||
allocBPD += getBPIndents(); | |||||
} | |||||
vpContentBPD = allocBPD - getBPIndents(); | |||||
referenceIPD = context.getRefIPD(); | |||||
if (width.getEnum() == EN_AUTO) { | |||||
updateContentAreaIPDwithOverconstrainedAdjust(); | |||||
} else { | |||||
int contentWidth = width.getValue(this); | |||||
updateContentAreaIPDwithOverconstrainedAdjust(contentWidth); | |||||
} | |||||
double contentRectOffsetX = 0; | |||||
contentRectOffsetX += getBlockContainerFO() | |||||
.getCommonMarginBlock().startIndent.getValue(this); | |||||
double contentRectOffsetY = 0; | |||||
contentRectOffsetY += getBlockContainerFO() | |||||
.getCommonBorderPaddingBackground().getBorderBeforeWidth(false); | |||||
contentRectOffsetY += getBlockContainerFO() | |||||
.getCommonBorderPaddingBackground().getPaddingBefore(false, this); | |||||
updateRelDims(contentRectOffsetX, contentRectOffsetY, autoHeight); | |||||
int availableIPD = referenceIPD - getIPIndents(); | |||||
if (getContentAreaIPD() > availableIPD) { | |||||
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( | |||||
getBlockContainerFO().getUserAgent().getEventBroadcaster()); | |||||
eventProducer.objectTooWide(this, getBlockContainerFO().getName(), | |||||
getContentAreaIPD(), context.getRefIPD(), | |||||
getBlockContainerFO().getLocator()); | |||||
} | |||||
MinOptMax stackLimit = new MinOptMax(relDims.bpd); | |||||
List returnedList; | |||||
List contentList = new LinkedList(); | |||||
List returnList = new LinkedList(); | |||||
if (!breakBeforeServed) { | |||||
breakBeforeServed = true; | |||||
if (!context.suppressBreakBefore()) { | |||||
if (addKnuthElementsForBreakBefore(returnList, context)) { | |||||
return returnList; | |||||
} | |||||
} | |||||
} | |||||
if (!firstVisibleMarkServed) { | |||||
addKnuthElementsForSpaceBefore(returnList, alignment); | |||||
context.updateKeepWithPreviousPending(getKeepWithPrevious()); | |||||
} | |||||
addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); | |||||
firstVisibleMarkServed = true; | |||||
if (autoHeight && inlineElementList) { | |||||
//Spaces, border and padding to be repeated at each break | |||||
addPendingMarks(context); | |||||
BlockLevelLayoutManager curLM; // currently active LM | |||||
BlockLevelLayoutManager prevLM = null; // previously active LM | |||||
LayoutContext childLC = new LayoutContext(0); | |||||
if (lmStack.isEmpty()) { | |||||
assert restartAtLM != null && restartAtLM.getParent() == this; | |||||
curLM = (BlockLevelLayoutManager) restartAtLM; | |||||
curLM.reset(); | |||||
setCurrentChildLM(curLM); | |||||
childLC.copyPendingMarksFrom(context); | |||||
childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); | |||||
childLC.setRefIPD(relDims.ipd); | |||||
childLC.setWritingMode(getBlockContainerFO().getWritingMode()); | |||||
if (curLM == this.childLMs.get(0)) { | |||||
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); | |||||
//Handled already by the parent (break collapsing, see above) | |||||
} | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||||
} else { | |||||
curLM = (BlockLevelLayoutManager) lmStack.pop(); | |||||
setCurrentChildLM(curLM); | |||||
childLC.copyPendingMarksFrom(context); | |||||
childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); | |||||
childLC.setRefIPD(relDims.ipd); | |||||
childLC.setWritingMode(getBlockContainerFO().getWritingMode()); | |||||
if (curLM == this.childLMs.get(0)) { | |||||
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); | |||||
//Handled already by the parent (break collapsing, see above) | |||||
} | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment, lmStack, | |||||
restartPosition, restartAtLM); | |||||
} | |||||
if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { | |||||
//Propagate keep-with-previous up from the first child | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
childLC.clearKeepWithPreviousPending(); | |||||
} | |||||
if (returnedList.size() == 1 | |||||
&& ((ListElement)returnedList.get(0)).isForcedBreak()) { | |||||
// a descendant of this block has break-before | |||||
/* | |||||
if (returnList.size() == 0) { | |||||
// the first child (or its first child ...) has | |||||
// break-before; | |||||
// all this block, including space before, will be put in | |||||
// the | |||||
// following page | |||||
bSpaceBeforeServed = false; | |||||
}*/ | |||||
contentList.addAll(returnedList); | |||||
// "wrap" the Position inside each element | |||||
// moving the elements from contentList to returnList | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
return returnList; | |||||
} else { | |||||
if (prevLM != null) { | |||||
// there is a block handled by prevLM | |||||
// before the one handled by curLM | |||||
addInBetweenBreak(contentList, context, childLC); | |||||
} | |||||
contentList.addAll(returnedList); | |||||
if (!returnedList.isEmpty()) { | |||||
if (((ListElement) ListUtil.getLast(returnedList)) | |||||
.isForcedBreak()) { | |||||
// a descendant of this block has break-after | |||||
if (curLM.isFinished()) { | |||||
// there is no other content in this block; | |||||
// it's useless to add space after before a page break | |||||
setFinished(true); | |||||
} | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
return returnList; | |||||
} | |||||
} | |||||
} | |||||
// propagate and clear | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
childLC.clearKeepsPending(); | |||||
prevLM = curLM; | |||||
while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { | |||||
curLM.reset(); | |||||
childLC = new LayoutContext(0); | |||||
childLC.copyPendingMarksFrom(context); | |||||
// curLM is a ? | |||||
childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); | |||||
childLC.setRefIPD(relDims.ipd); | |||||
childLC.setWritingMode(getBlockContainerFO().getWritingMode()); | |||||
if (curLM == this.childLMs.get(0)) { | |||||
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); | |||||
//Handled already by the parent (break collapsing, see above) | |||||
} | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||||
if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { | |||||
//Propagate keep-with-previous up from the first child | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
childLC.clearKeepWithPreviousPending(); | |||||
} | |||||
if (returnedList.size() == 1 | |||||
&& ((ListElement)returnedList.get(0)).isForcedBreak()) { | |||||
// a descendant of this block has break-before | |||||
/* | |||||
if (returnList.size() == 0) { | |||||
// the first child (or its first child ...) has | |||||
// break-before; | |||||
// all this block, including space before, will be put in | |||||
// the | |||||
// following page | |||||
bSpaceBeforeServed = false; | |||||
}*/ | |||||
contentList.addAll(returnedList); | |||||
// "wrap" the Position inside each element | |||||
// moving the elements from contentList to returnList | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
return returnList; | |||||
} else { | |||||
if (prevLM != null) { | |||||
// there is a block handled by prevLM | |||||
// before the one handled by curLM | |||||
addInBetweenBreak(contentList, context, childLC); | |||||
} | |||||
contentList.addAll(returnedList); | |||||
if (returnedList.isEmpty()) { | |||||
//Avoid NoSuchElementException below (happens with empty blocks) | |||||
continue; | |||||
} | |||||
if (((ListElement) ListUtil.getLast(returnedList)) | |||||
.isForcedBreak()) { | |||||
// a descendant of this block has break-after | |||||
if (curLM.isFinished()) { | |||||
// there is no other content in this block; | |||||
// it's useless to add space after before a page break | |||||
setFinished(true); | |||||
} | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
return returnList; | |||||
} | |||||
} | |||||
// propagate and clear | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
childLC.clearKeepsPending(); | |||||
prevLM = curLM; | |||||
} | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
} else { | |||||
MinOptMax range = new MinOptMax(relDims.ipd); | |||||
BlockContainerBreaker breaker = new BlockContainerBreaker(this, range); | |||||
breaker.doLayout(relDims.bpd, autoHeight); | |||||
boolean contentOverflows = breaker.isOverflow(); | |||||
if (autoHeight) { | |||||
//Update content BPD now that it is known | |||||
int newHeight = breaker.deferredAlg.totalWidth; | |||||
boolean switchedProgressionDirection | |||||
= (getBlockContainerFO().getReferenceOrientation() % 180 != 0); | |||||
if (switchedProgressionDirection) { | |||||
setContentAreaIPD(newHeight); | |||||
} else { | |||||
vpContentBPD = newHeight; | |||||
} | |||||
updateRelDims(contentRectOffsetX, contentRectOffsetY, false); | |||||
} | |||||
Position bcPosition = new BlockContainerPosition(this, breaker); | |||||
returnList.add(new KnuthBox(vpContentBPD, notifyPos(bcPosition), false)); | |||||
//TODO Handle min/opt/max for block-progression-dimension | |||||
/* These two elements will be used to add stretchability to the above box | |||||
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, | |||||
false, returnPosition, false)); | |||||
returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0, | |||||
LINE_NUMBER_ADJUSTMENT, returnPosition, false)); | |||||
*/ | |||||
if (contentOverflows) { | |||||
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( | |||||
getBlockContainerFO().getUserAgent().getEventBroadcaster()); | |||||
boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW); | |||||
eventProducer.viewportOverflow(this, getBlockContainerFO().getName(), | |||||
breaker.getOverflowAmount(), needClip(), canRecover, | |||||
getBlockContainerFO().getLocator()); | |||||
} | |||||
} | |||||
addKnuthElementsForBorderPaddingAfter(returnList, true); | |||||
addKnuthElementsForSpaceAfter(returnList, alignment); | |||||
//All child content is processed. Only break-after can occur now, so... | |||||
context.clearPendingMarks(); | |||||
addKnuthElementsForBreakAfter(returnList, context); | |||||
context.updateKeepWithNextPending(getKeepWithNext()); | |||||
setFinished(true); | |||||
return returnList; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public boolean isRestartable() { | |||||
return true; | |||||
} | |||||
private List getNextKnuthElementsAbsolute(LayoutContext context, int alignment) { | private List getNextKnuthElementsAbsolute(LayoutContext context, int alignment) { | ||||
autoHeight = false; | autoHeight = false; | ||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import java.util.Stack; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
private MinOptMax effSpaceBefore; | private MinOptMax effSpaceBefore; | ||||
private MinOptMax effSpaceAfter; | private MinOptMax effSpaceAfter; | ||||
/** The list of child BreakPoss instances. */ | |||||
protected List childBreaks = new java.util.ArrayList(); | |||||
/** | /** | ||||
* Creates a new BlockLayoutManager. | * Creates a new BlockLayoutManager. | ||||
* @param inBlock the block FO object to create the layout manager for. | * @param inBlock the block FO object to create the layout manager for. | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public List getNextKnuthElements(LayoutContext context, int alignment) { | public List getNextKnuthElements(LayoutContext context, int alignment) { | ||||
return getNextKnuthElements(context, alignment, null, null, null); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||||
Position restartPosition, LayoutManager restartAtLM) { | |||||
resetSpaces(); | resetSpaces(); | ||||
return super.getNextKnuthElements(context, alignment); | |||||
if (lmStack == null) { | |||||
return super.getNextKnuthElements(context, alignment); | |||||
} else { | |||||
return super.getNextKnuthElements(context, alignment, lmStack, restartPosition, | |||||
restartAtLM); | |||||
} | |||||
} | } | ||||
private void resetSpaces() { | private void resetSpaces() { | ||||
// and put them in a new list; | // and put them in a new list; | ||||
LinkedList positionList = new LinkedList(); | LinkedList positionList = new LinkedList(); | ||||
Position pos; | Position pos; | ||||
boolean bSpaceBefore = false; | |||||
boolean bSpaceAfter = false; | |||||
boolean spaceBefore = false; | |||||
boolean spaceAfter = false; | |||||
Position firstPos = null; | Position firstPos = null; | ||||
Position lastPos = null; | Position lastPos = null; | ||||
while (parentIter.hasNext()) { | while (parentIter.hasNext()) { | ||||
// this means the space was not discarded | // this means the space was not discarded | ||||
if (positionList.size() == 0) { | if (positionList.size() == 0) { | ||||
// pos was in the element representing space-before | // pos was in the element representing space-before | ||||
bSpaceBefore = true; | |||||
spaceBefore = true; | |||||
//log.trace(" space before"); | //log.trace(" space before"); | ||||
} else { | } else { | ||||
// pos was in the element representing space-after | // pos was in the element representing space-after | ||||
bSpaceAfter = true; | |||||
spaceAfter = true; | |||||
//log.trace(" space-after"); | //log.trace(" space-after"); | ||||
} | } | ||||
} else if (innerPosition.getLM() == this | } else if (innerPosition.getLM() == this | ||||
// the Positions in positionList were inside the elements | // the Positions in positionList were inside the elements | ||||
// created by the LineLM | // created by the LineLM | ||||
childPosIter = new StackingIter(positionList.listIterator()); | childPosIter = new StackingIter(positionList.listIterator()); | ||||
} else { | |||||
} else { | |||||
// the Positions in positionList were inside the elements | // the Positions in positionList were inside the elements | ||||
// created by the BlockLM in the createUnitElements() method | // created by the BlockLM in the createUnitElements() method | ||||
//if (((Position) positionList.getLast()) instanceof | //if (((Position) positionList.getLast()) instanceof | ||||
// + " spacing"); | // + " spacing"); | ||||
// add space before and / or after the paragraph | // add space before and / or after the paragraph | ||||
// to reach a multiple of bpUnit | // to reach a multiple of bpUnit | ||||
if (bSpaceBefore && bSpaceAfter) { | |||||
if (spaceBefore && spaceAfter) { | |||||
foSpaceBefore = new SpaceVal(getBlockFO() | foSpaceBefore = new SpaceVal(getBlockFO() | ||||
.getCommonMarginBlock().spaceBefore, this).getSpace(); | .getCommonMarginBlock().spaceBefore, this).getSpace(); | ||||
foSpaceAfter = new SpaceVal(getBlockFO() | foSpaceAfter = new SpaceVal(getBlockFO() | ||||
+ foSpaceBefore.min | + foSpaceBefore.min | ||||
+ foSpaceAfter.min) | + foSpaceAfter.min) | ||||
* bpUnit - splitLength - adjustedSpaceBefore; | * bpUnit - splitLength - adjustedSpaceBefore; | ||||
} else if (bSpaceBefore) { | |||||
} else if (spaceBefore) { | |||||
adjustedSpaceBefore = neededUnits(splitLength | adjustedSpaceBefore = neededUnits(splitLength | ||||
+ foSpaceBefore.min) | + foSpaceBefore.min) | ||||
* bpUnit - splitLength; | * bpUnit - splitLength; | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public boolean isRestartable() { | |||||
return true; | |||||
} | |||||
} | } | ||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import java.util.Stack; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.Block; | import org.apache.fop.area.Block; | ||||
import org.apache.fop.area.BlockParent; | import org.apache.fop.area.BlockParent; | ||||
import org.apache.fop.fo.FObj; | |||||
import org.apache.fop.fo.Constants; | import org.apache.fop.fo.Constants; | ||||
import org.apache.fop.fo.FObj; | |||||
import org.apache.fop.fo.properties.BreakPropertySet; | import org.apache.fop.fo.properties.BreakPropertySet; | ||||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | |||||
import org.apache.fop.fo.properties.KeepProperty; | import org.apache.fop.fo.properties.KeepProperty; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | |||||
import org.apache.fop.layoutmgr.inline.InlineLayoutManager; | import org.apache.fop.layoutmgr.inline.InlineLayoutManager; | ||||
import org.apache.fop.layoutmgr.inline.LineLayoutManager; | import org.apache.fop.layoutmgr.inline.LineLayoutManager; | ||||
import org.apache.fop.traits.MinOptMax; | import org.apache.fop.traits.MinOptMax; | ||||
*/ | */ | ||||
private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class); | private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class); | ||||
/** | |||||
* Reference to FO whose areas it's managing or to the traits | |||||
* of the FO. | |||||
*/ | |||||
//protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this! | |||||
protected BlockParent parentArea = null; | |||||
protected BlockParent parentArea; | |||||
/** Value of the block-progression-unit (non-standard property) */ | /** Value of the block-progression-unit (non-standard property) */ | ||||
protected int bpUnit = 0; | |||||
protected int bpUnit; | |||||
/** space-before value adjusted for block-progression-unit handling */ | /** space-before value adjusted for block-progression-unit handling */ | ||||
protected int adjustedSpaceBefore = 0; | |||||
protected int adjustedSpaceBefore; | |||||
/** space-after value adjusted for block-progression-unit handling */ | /** space-after value adjusted for block-progression-unit handling */ | ||||
protected int adjustedSpaceAfter = 0; | |||||
protected int adjustedSpaceAfter; | |||||
/** Only used to store the original list when createUnitElements is called */ | /** Only used to store the original list when createUnitElements is called */ | ||||
protected List storedList = null; | |||||
protected List storedList; | |||||
/** Indicates whether break before has been served or not */ | /** Indicates whether break before has been served or not */ | ||||
protected boolean breakBeforeServed = false; | |||||
protected boolean breakBeforeServed; | |||||
/** Indicates whether the first visible mark has been returned by this LM, yet */ | /** Indicates whether the first visible mark has been returned by this LM, yet */ | ||||
protected boolean firstVisibleMarkServed = false; | |||||
protected boolean firstVisibleMarkServed; | |||||
/** Reference IPD available */ | /** Reference IPD available */ | ||||
protected int referenceIPD = 0; | |||||
protected int referenceIPD; | |||||
/** the effective start-indent value */ | /** the effective start-indent value */ | ||||
protected int startIndent = 0; | |||||
protected int startIndent; | |||||
/** the effective end-indent value */ | /** the effective end-indent value */ | ||||
protected int endIndent = 0; | |||||
protected int endIndent; | |||||
/** | /** | ||||
* Holds the (one-time use) fo:block space-before | * Holds the (one-time use) fo:block space-before | ||||
* and -after properties. Large fo:blocks are split | * and -after properties. Large fo:blocks are split | ||||
* Block and space-after at the end of the last Block | * Block and space-after at the end of the last Block | ||||
* used in rendering the fo:block. | * used in rendering the fo:block. | ||||
*/ | */ | ||||
protected MinOptMax foSpaceBefore = null; | |||||
protected MinOptMax foSpaceBefore; | |||||
/** see foSpaceBefore */ | /** see foSpaceBefore */ | ||||
protected MinOptMax foSpaceAfter = null; | |||||
protected MinOptMax foSpaceAfter; | |||||
private Position auxiliaryPosition; | private Position auxiliaryPosition; | ||||
private int contentAreaIPD = 0; | |||||
private int contentAreaIPD; | |||||
/** | /** | ||||
* @param node the fo this LM deals with | * @param node the fo this LM deals with | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public List getNextKnuthElements(LayoutContext context, int alignment) { | public List getNextKnuthElements(LayoutContext context, int alignment) { | ||||
//log.debug("BLM.getNextKnuthElements> keep-together = " | |||||
// + layoutProps.keepTogether.getType()); | |||||
//log.debug(" keep-with-previous = " + | |||||
// layoutProps.keepWithPrevious.getType()); | |||||
//log.debug(" keep-with-next = " + | |||||
// layoutProps.keepWithNext.getType()); | |||||
BlockLevelLayoutManager curLM; // currently active LM | |||||
BlockLevelLayoutManager prevLM = null; // previously active LM | |||||
referenceIPD = context.getRefIPD(); | referenceIPD = context.getRefIPD(); | ||||
updateContentAreaIPDwithOverconstrainedAdjust(); | updateContentAreaIPDwithOverconstrainedAdjust(); | ||||
List returnedList = null; | |||||
List contentList = new LinkedList(); | List contentList = new LinkedList(); | ||||
List returnList = new LinkedList(); | |||||
List elements = new LinkedList(); | |||||
if (!breakBeforeServed) { | if (!breakBeforeServed) { | ||||
breakBeforeServed = true; | breakBeforeServed = true; | ||||
if (!context.suppressBreakBefore()) { | if (!context.suppressBreakBefore()) { | ||||
if (addKnuthElementsForBreakBefore(returnList, context)) { | |||||
return returnList; | |||||
if (addKnuthElementsForBreakBefore(elements, context)) { | |||||
return elements; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!firstVisibleMarkServed) { | if (!firstVisibleMarkServed) { | ||||
addKnuthElementsForSpaceBefore(returnList, alignment); | |||||
addKnuthElementsForSpaceBefore(elements, alignment); | |||||
context.updateKeepWithPreviousPending(getKeepWithPrevious()); | context.updateKeepWithPreviousPending(getKeepWithPrevious()); | ||||
} | } | ||||
addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); | |||||
addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed); | |||||
firstVisibleMarkServed = true; | firstVisibleMarkServed = true; | ||||
//Spaces, border and padding to be repeated at each break | //Spaces, border and padding to be repeated at each break | ||||
//Used to indicate a special break-after case when all content has already been generated. | //Used to indicate a special break-after case when all content has already been generated. | ||||
BreakElement forcedBreakAfterLast = null; | BreakElement forcedBreakAfterLast = null; | ||||
while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { | |||||
LayoutManager currentChildLM; | |||||
while ((currentChildLM = (LayoutManager) getChildLM()) != null) { | |||||
LayoutContext childLC = new LayoutContext(0); | LayoutContext childLC = new LayoutContext(0); | ||||
childLC.copyPendingMarksFrom(context); | |||||
if (curLM instanceof LineLayoutManager) { | |||||
// curLM is a LineLayoutManager | |||||
// set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!) | |||||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||||
childLC.setStackLimitIP(new MinOptMax(getContentAreaIPD())); | |||||
childLC.setRefIPD(getContentAreaIPD()); | |||||
} else { | |||||
// curLM is a ? | |||||
//childLC.setStackLimit(MinOptMax.subtract(context | |||||
// .getStackLimit(), stackSize)); | |||||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||||
childLC.setRefIPD(referenceIPD); | |||||
} | |||||
if (curLM == this.childLMs.get(0)) { | |||||
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); | |||||
//Handled already by the parent (break collapsing, see above) | |||||
} | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||||
if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { | |||||
List childrenElements = getNextChildElements(currentChildLM, context, childLC, | |||||
alignment); | |||||
if (contentList.isEmpty()) { | |||||
//Propagate keep-with-previous up from the first child | //Propagate keep-with-previous up from the first child | ||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | ||||
childLC.clearKeepWithPreviousPending(); | |||||
} | } | ||||
if (returnedList != null | |||||
&& returnedList.size() == 1 | |||||
&& ((ListElement) returnedList.get(0)).isForcedBreak()) { | |||||
if (childrenElements != null && !childrenElements.isEmpty()) { | |||||
if (!contentList.isEmpty() | |||||
&& !ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
// there is a block handled by prevLM before the one | |||||
// handled by curLM, and the one handled | |||||
// by the current LM does not begin with a break | |||||
addInBetweenBreak(contentList, context, childLC); | |||||
} | |||||
if (childrenElements.size() == 1 | |||||
&& ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
// a descendant of this block has break-before | |||||
forcedBreakAfterLast = (BreakElement) childrenElements.get(0); | |||||
context.clearPendingMarks(); | |||||
break; | |||||
} | |||||
if (curLM.isFinished() && !hasNextChildLM()) { | |||||
if (contentList.isEmpty()) { | |||||
// Empty fo:block, zero-length box makes sure the IDs and/or markers | |||||
// are registered and borders/padding are painted. | |||||
elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); | |||||
} | |||||
// a descendant of this block has break-before | // a descendant of this block has break-before | ||||
forcedBreakAfterLast = (BreakElement) returnedList.get(0); | |||||
contentList.addAll(childrenElements); | |||||
wrapPositionElements(contentList, elements); | |||||
return elements; | |||||
} else { | |||||
contentList.addAll(childrenElements); | |||||
if (ElementListUtils.endsWithForcedBreak(childrenElements)) { | |||||
// a descendant of this block has break-after | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); | |||||
context.clearPendingMarks(); | |||||
break; | |||||
} | |||||
wrapPositionElements(contentList, elements); | |||||
return elements; | |||||
} | |||||
} | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
} | |||||
} | |||||
if (!contentList.isEmpty()) { | |||||
wrapPositionElements(contentList, elements); | |||||
} else if (forcedBreakAfterLast == null) { | |||||
// Empty fo:block, zero-length box makes sure the IDs and/or markers | |||||
// are registered. | |||||
elements.add(new KnuthBox(0, notifyPos(new Position(this)), true)); | |||||
} | |||||
addKnuthElementsForBorderPaddingAfter(elements, true); | |||||
addKnuthElementsForSpaceAfter(elements, alignment); | |||||
//All child content is processed. Only break-after can occur now, so... | |||||
context.clearPendingMarks(); | |||||
if (forcedBreakAfterLast == null) { | |||||
addKnuthElementsForBreakAfter(elements, context); | |||||
} else { | |||||
forcedBreakAfterLast.clearPendingMarks(); | |||||
elements.add(forcedBreakAfterLast); | |||||
} | |||||
context.updateKeepWithNextPending(getKeepWithNext()); | |||||
setFinished(true); | |||||
return elements; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||||
Position restartPosition, LayoutManager restartAtLM) { | |||||
referenceIPD = context.getRefIPD(); | |||||
updateContentAreaIPDwithOverconstrainedAdjust(); | |||||
List contentList = new LinkedList(); | |||||
List elements = new LinkedList(); | |||||
if (!breakBeforeServed) { | |||||
breakBeforeServed = true; | |||||
if (!context.suppressBreakBefore()) { | |||||
if (addKnuthElementsForBreakBefore(elements, context)) { | |||||
return elements; | |||||
} | |||||
} | |||||
} | |||||
if (!firstVisibleMarkServed) { | |||||
addKnuthElementsForSpaceBefore(elements, alignment); | |||||
context.updateKeepWithPreviousPending(getKeepWithPrevious()); | |||||
} | |||||
addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed); | |||||
firstVisibleMarkServed = true; | |||||
//Spaces, border and padding to be repeated at each break | |||||
addPendingMarks(context); | |||||
//Used to indicate a special break-after case when all content has already been generated. | |||||
BreakElement forcedBreakAfterLast = null; | |||||
LayoutContext childLC = new LayoutContext(0); | |||||
List childrenElements; | |||||
LayoutManager currentChildLM; | |||||
if (lmStack.isEmpty()) { | |||||
assert restartAtLM != null && restartAtLM.getParent() == this; | |||||
currentChildLM = restartAtLM; | |||||
currentChildLM.reset(); | |||||
setCurrentChildLM(currentChildLM); | |||||
childrenElements = getNextChildElements(currentChildLM, context, childLC, | |||||
alignment); | |||||
} else { | |||||
currentChildLM = (BlockLevelLayoutManager) lmStack.pop(); | |||||
setCurrentChildLM(currentChildLM); | |||||
childrenElements = getNextChildElements(currentChildLM, context, childLC, alignment, | |||||
lmStack, restartPosition, restartAtLM); | |||||
} | |||||
if (contentList.isEmpty()) { | |||||
//Propagate keep-with-previous up from the first child | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
} | |||||
if (childrenElements != null && !childrenElements.isEmpty()) { | |||||
if (!contentList.isEmpty() | |||||
&& !ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
// there is a block handled by prevLM before the one | |||||
// handled by curLM, and the one handled | |||||
// by the current LM does not begin with a break | |||||
addInBetweenBreak(contentList, context, childLC); | |||||
} | |||||
if (childrenElements.size() == 1 | |||||
&& ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
// a descendant of this block has break-before | |||||
forcedBreakAfterLast = (BreakElement) childrenElements.get(0); | |||||
context.clearPendingMarks(); | context.clearPendingMarks(); | ||||
break; | |||||
// break; TODO | |||||
} | } | ||||
if (contentList.isEmpty()) { | if (contentList.isEmpty()) { | ||||
// Empty fo:block, zero-length box makes sure the IDs and/or markers | // Empty fo:block, zero-length box makes sure the IDs and/or markers | ||||
// are registered and borders/padding are painted. | // are registered and borders/padding are painted. | ||||
returnList.add(new KnuthBox(0, notifyPos(new Position(this)), false)); | |||||
elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); | |||||
} | } | ||||
// a descendant of this block has break-before | // a descendant of this block has break-before | ||||
contentList.addAll(returnedList); | |||||
/* extension: conversione di tutta la sequenza fin'ora ottenuta */ | |||||
if (bpUnit > 0) { | |||||
storedList = contentList; | |||||
contentList = createUnitElements(contentList); | |||||
} | |||||
/* end of extension */ | |||||
contentList.addAll(childrenElements); | |||||
// "wrap" the Position inside each element | |||||
// moving the elements from contentList to returnList | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
wrapPositionElements(contentList, elements); | |||||
return returnList; | |||||
return elements; | |||||
} else { | } else { | ||||
if (returnedList == null || returnedList.isEmpty()) { | |||||
//Avoid NoSuchElementException below (happens with empty blocks) | |||||
continue; | |||||
contentList.addAll(childrenElements); | |||||
if (ElementListUtils.endsWithForcedBreak(childrenElements)) { | |||||
// a descendant of this block has break-after | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); | |||||
context.clearPendingMarks(); | |||||
// break; TODO | |||||
} | |||||
wrapPositionElements(contentList, elements); | |||||
return elements; | |||||
} | } | ||||
if (prevLM != null | |||||
&& !ElementListUtils.startsWithForcedBreak(returnedList)) { | |||||
} | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
} | |||||
while ((currentChildLM = (LayoutManager) getChildLM()) != null) { | |||||
currentChildLM.reset(); // TODO won't work with forced breaks | |||||
childLC = new LayoutContext(0); | |||||
childrenElements = getNextChildElements(currentChildLM, context, childLC, | |||||
alignment); | |||||
if (contentList.isEmpty()) { | |||||
//Propagate keep-with-previous up from the first child | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
} | |||||
if (childrenElements != null && !childrenElements.isEmpty()) { | |||||
if (!contentList.isEmpty() | |||||
&& !ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
// there is a block handled by prevLM before the one | // there is a block handled by prevLM before the one | ||||
// handled by curLM, and the one handled | // handled by curLM, and the one handled | ||||
// by the current LM does not begin with a break | // by the current LM does not begin with a break | ||||
addInBetweenBreak(contentList, context, childLC); | addInBetweenBreak(contentList, context, childLC); | ||||
} | } | ||||
contentList.addAll(returnedList); | |||||
if (ElementListUtils.endsWithForcedBreak(returnedList)) { | |||||
// a descendant of this block has break-after | |||||
if (curLM.isFinished() && !hasNextChildLM()) { | |||||
forcedBreakAfterLast = (BreakElement) ListUtil | |||||
.removeLast(contentList); | |||||
if (childrenElements.size() == 1 | |||||
&& ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
// a descendant of this block has break-before | |||||
forcedBreakAfterLast = (BreakElement) childrenElements.get(0); | |||||
context.clearPendingMarks(); | context.clearPendingMarks(); | ||||
break; | break; | ||||
} | } | ||||
/* extension: conversione di tutta la sequenza fin'ora ottenuta */ | |||||
if (bpUnit > 0) { | |||||
storedList = contentList; | |||||
contentList = createUnitElements(contentList); | |||||
if (contentList.isEmpty()) { | |||||
// Empty fo:block, zero-length box makes sure the IDs and/or markers | |||||
// are registered and borders/padding are painted. | |||||
elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); | |||||
} | } | ||||
/* end of extension */ | |||||
// a descendant of this block has break-before | |||||
contentList.addAll(childrenElements); | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(contentList, returnList); | |||||
wrapPositionElements(contentList, elements); | |||||
return returnList; | |||||
return elements; | |||||
} else { | |||||
contentList.addAll(childrenElements); | |||||
if (ElementListUtils.endsWithForcedBreak(childrenElements)) { | |||||
// a descendant of this block has break-after | |||||
if (currentChildLM.isFinished() && !hasNextChildLM()) { | |||||
forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); | |||||
context.clearPendingMarks(); | |||||
break; | |||||
} | |||||
wrapPositionElements(contentList, elements); | |||||
return elements; | |||||
} | |||||
} | } | ||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
} | } | ||||
// propagate and clear | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
childLC.clearKeepsPending(); | |||||
prevLM = curLM; | |||||
} | |||||
/* Extension: conversione di tutta la sequenza fin'ora ottenuta */ | |||||
if (bpUnit > 0) { | |||||
storedList = contentList; | |||||
contentList = createUnitElements(contentList); | |||||
} | } | ||||
/* end of extension */ | |||||
returnedList = new LinkedList(); | |||||
if (!contentList.isEmpty()) { | if (!contentList.isEmpty()) { | ||||
wrapPositionElements(contentList, returnList); | |||||
wrapPositionElements(contentList, elements); | |||||
} else if (forcedBreakAfterLast == null) { | } else if (forcedBreakAfterLast == null) { | ||||
// Empty fo:block, zero-length box makes sure the IDs and/or markers | // Empty fo:block, zero-length box makes sure the IDs and/or markers | ||||
// are registered. | // are registered. | ||||
returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true)); | |||||
elements.add(new KnuthBox(0, notifyPos(new Position(this)), true)); | |||||
} | } | ||||
addKnuthElementsForBorderPaddingAfter(returnList, true); | |||||
addKnuthElementsForSpaceAfter(returnList, alignment); | |||||
addKnuthElementsForBorderPaddingAfter(elements, true); | |||||
addKnuthElementsForSpaceAfter(elements, alignment); | |||||
//All child content is processed. Only break-after can occur now, so... | //All child content is processed. Only break-after can occur now, so... | ||||
context.clearPendingMarks(); | context.clearPendingMarks(); | ||||
if (forcedBreakAfterLast == null) { | if (forcedBreakAfterLast == null) { | ||||
addKnuthElementsForBreakAfter(returnList, context); | |||||
} | |||||
if (forcedBreakAfterLast != null) { | |||||
addKnuthElementsForBreakAfter(elements, context); | |||||
} else { | |||||
forcedBreakAfterLast.clearPendingMarks(); | forcedBreakAfterLast.clearPendingMarks(); | ||||
returnList.add(forcedBreakAfterLast); | |||||
elements.add(forcedBreakAfterLast); | |||||
} | } | ||||
context.updateKeepWithNextPending(getKeepWithNext()); | context.updateKeepWithNextPending(getKeepWithNext()); | ||||
setFinished(true); | setFinished(true); | ||||
return returnList; | |||||
return elements; | |||||
} | |||||
private List getNextChildElements(LayoutManager childLM, LayoutContext context, | |||||
LayoutContext childLC, int alignment) { | |||||
return getNextChildElements(childLM, context, childLC, alignment, null, null, null); | |||||
} | |||||
private List getNextChildElements(LayoutManager childLM, LayoutContext context, | |||||
LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition, | |||||
LayoutManager restartAtLM) { | |||||
childLC.copyPendingMarksFrom(context); | |||||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||||
if (childLM instanceof LineLayoutManager) { | |||||
childLC.setRefIPD(getContentAreaIPD()); | |||||
} else { | |||||
childLC.setRefIPD(referenceIPD); | |||||
} | |||||
if (childLM == this.childLMs.get(0)) { | |||||
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); | |||||
//Handled already by the parent (break collapsing, see above) | |||||
} | |||||
if (lmStack == null) { | |||||
return childLM.getNextKnuthElements(childLC, alignment); | |||||
} else { | |||||
if (childLM instanceof LineLayoutManager) { | |||||
return ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment, | |||||
(LeafPosition) restartPosition); | |||||
} else { | |||||
return childLM.getNextKnuthElements(childLC, alignment, | |||||
lmStack, restartPosition, restartAtLM); | |||||
} | |||||
} | |||||
} | } | ||||
/** | /** | ||||
return -1; | return -1; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void reset() { | |||||
super.reset(); | |||||
breakBeforeServed = false; | |||||
firstVisibleMarkServed = false; | |||||
// TODO startIndent, endIndent | |||||
} | |||||
} | } | ||||
elementIndex, previousIsBox, allowedBreaks).isBox(); | elementIndex, previousIsBox, allowedBreaks).isBox(); | ||||
if (activeNodeCount == 0) { | if (activeNodeCount == 0) { | ||||
if (ipdChanged()) { | |||||
return handleIpdChange(); | |||||
} | |||||
if (!force) { | if (!force) { | ||||
log.debug("Could not find a set of breaking points " + threshold); | log.debug("Could not find a set of breaking points " + threshold); | ||||
return 0; | return 0; | ||||
return line; | return line; | ||||
} | } | ||||
protected boolean ipdChanged() { | |||||
return false; | |||||
} | |||||
protected int handleIpdChange() { | |||||
throw new IllegalStateException(); | |||||
} | |||||
/** | /** | ||||
* Recover from a {@link KnuthNode} leading to a line that is too long. | * Recover from a {@link KnuthNode} leading to a line that is too long. | ||||
* The default implementation creates a new node corresponding to a break | * The default implementation creates a new node corresponding to a break | ||||
* @return the width/length in millipoints | * @return the width/length in millipoints | ||||
*/ | */ | ||||
protected int getLineWidth(int line) { | protected int getLineWidth(int line) { | ||||
if (this.lineWidth < 0) { | |||||
throw new IllegalStateException("lineWidth must be set" | |||||
+ (this.lineWidth != 0 ? " and positive, but it is: " + this.lineWidth : "")); | |||||
} else { | |||||
return this.lineWidth; | |||||
} | |||||
assert lineWidth >= 0; | |||||
return this.lineWidth; | |||||
} | } | ||||
/** @return the constant line/part width or -1 if there is no such value */ | /** @return the constant line/part width or -1 if there is no such value */ | ||||
* @param par the corresponding paragraph | * @param par the corresponding paragraph | ||||
* @param total the number of lines into which the paragraph will be broken | * @param total the number of lines into which the paragraph will be broken | ||||
*/ | */ | ||||
private void calculateBreakPoints(KnuthNode node, KnuthSequence par, | |||||
protected void calculateBreakPoints(KnuthNode node, KnuthSequence par, | |||||
int total) { | int total) { | ||||
KnuthNode bestActiveNode = node; | KnuthNode bestActiveNode = node; | ||||
// use bestActiveNode to determine the optimum breakpoints | // use bestActiveNode to determine the optimum breakpoints |
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import java.util.Stack; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.BlockParent; | import org.apache.fop.area.BlockParent; | ||||
import org.apache.fop.fo.pagination.Flow; | import org.apache.fop.fo.pagination.Flow; | ||||
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; | |||||
import org.apache.fop.layoutmgr.inline.WrapperLayoutManager; | |||||
/** | /** | ||||
* LayoutManager for an fo:flow object. | * LayoutManager for an fo:flow object. | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public List getNextKnuthElements(LayoutContext context, int alignment) { | public List getNextKnuthElements(LayoutContext context, int alignment) { | ||||
// set layout dimensions | |||||
int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); | |||||
int flowBPD = getCurrentPV().getBodyRegion().getBPD(); | |||||
List elements = new LinkedList(); | |||||
// currently active LM | |||||
LayoutManager curLM; | |||||
List returnedList; | |||||
List returnList = new LinkedList(); | |||||
while ((curLM = getChildLM()) != null) { | |||||
if (!(curLM instanceof WrapperLayoutManager) | |||||
&& curLM instanceof InlineLevelLayoutManager) { | |||||
log.error("inline area not allowed under flow - ignoring"); | |||||
curLM.setFinished(true); | |||||
continue; | |||||
LayoutManager currentChildLM; | |||||
while ((currentChildLM = getChildLM()) != null) { | |||||
if (addChildElements(elements, currentChildLM, context, alignment) != null) { | |||||
return elements; | |||||
} | } | ||||
} | |||||
SpaceResolver.resolveElementList(elements); | |||||
setFinished(true); | |||||
assert !elements.isEmpty(); | |||||
return elements; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM) { | |||||
List elements = new LinkedList(); | |||||
int span = EN_NONE; | |||||
int disableColumnBalancing = EN_FALSE; | |||||
if (curLM instanceof BlockLayoutManager) { | |||||
span = ((BlockLayoutManager)curLM).getBlockFO().getSpan(); | |||||
disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO() | |||||
.getDisableColumnBalancing(); | |||||
} else if (curLM instanceof BlockContainerLayoutManager) { | |||||
span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); | |||||
disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO() | |||||
.getDisableColumnBalancing(); | |||||
LayoutManager currentChildLM = positionAtIPDChange.getLM(); | |||||
if (currentChildLM == null) { | |||||
throw new IllegalStateException( | |||||
"Cannot find layout manager from where to re-start layout after IPD change"); | |||||
} | |||||
if (restartAtLM != null && restartAtLM.getParent() == this) { | |||||
currentChildLM = restartAtLM; | |||||
setCurrentChildLM(currentChildLM); | |||||
currentChildLM.reset(); | |||||
if (addChildElements(elements, currentChildLM, context, alignment) != null) { | |||||
return elements; | |||||
} | |||||
} else { | |||||
Stack lmStack = new Stack(); | |||||
while (currentChildLM.getParent() != this) { | |||||
lmStack.push(currentChildLM); | |||||
currentChildLM = currentChildLM.getParent(); | |||||
} | } | ||||
setCurrentChildLM(currentChildLM); | |||||
if (addChildElements(elements, currentChildLM, context, alignment, lmStack, | |||||
positionAtIPDChange, restartAtLM) != null) { | |||||
return elements; | |||||
} | |||||
} | |||||
int currentSpan = context.getCurrentSpan(); | |||||
if (currentSpan != span) { | |||||
if (span == EN_ALL) { | |||||
context.setDisableColumnBalancing(disableColumnBalancing); | |||||
} | |||||
log.debug("span change from " + currentSpan + " to " + span); | |||||
context.signalSpanChange(span); | |||||
SpaceResolver.resolveElementList(returnList); | |||||
return returnList; | |||||
while ((currentChildLM = getChildLM()) != null) { | |||||
currentChildLM.reset(); // TODO won't work with forced breaks | |||||
if (addChildElements(elements, currentChildLM, context, alignment) != null) { | |||||
return elements; | |||||
} | } | ||||
} | |||||
// Set up a LayoutContext | |||||
//MinOptMax bpd = context.getStackLimit(); | |||||
SpaceResolver.resolveElementList(elements); | |||||
setFinished(true); | |||||
LayoutContext childLC = new LayoutContext(0); | |||||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||||
childLC.setRefIPD(context.getRefIPD()); | |||||
childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); | |||||
assert !elements.isEmpty(); | |||||
return elements; | |||||
} | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||||
//log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size()); | |||||
if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) { | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
childLC.clearKeepWithPreviousPending(); | |||||
} | |||||
private List addChildElements(List elements, LayoutManager childLM, LayoutContext context, | |||||
int alignment) { | |||||
return addChildElements(elements, childLM, context, alignment, null, null, null); | |||||
} | |||||
// "wrap" the Position inside each element | |||||
List tempList = returnedList; | |||||
returnedList = new LinkedList(); | |||||
wrapPositionElements(tempList, returnedList); | |||||
if (returnedList.size() == 1 | |||||
&& ElementListUtils.endsWithForcedBreak(returnedList)) { | |||||
// a descendant of this flow has break-before | |||||
returnList.addAll(returnedList); | |||||
SpaceResolver.resolveElementList(returnList); | |||||
return returnList; | |||||
} else if (returnedList.size() > 0) { | |||||
if (returnList.size() > 0 | |||||
&& !ElementListUtils.startsWithForcedBreak(returnedList)) { | |||||
addInBetweenBreak(returnList, context, childLC); | |||||
} | |||||
returnList.addAll(returnedList); | |||||
if (ElementListUtils.endsWithForcedBreak(returnList)) { | |||||
if (curLM.isFinished() && !hasNextChildLM()) { | |||||
//If the layout manager is finished at this point, the pending | |||||
//marks become irrelevant. | |||||
childLC.clearPendingMarks(); | |||||
//setFinished(true); | |||||
break; | |||||
} | |||||
// a descendant of this flow has break-after | |||||
SpaceResolver.resolveElementList(returnList); | |||||
return returnList; | |||||
} | |||||
private List addChildElements(List elements, LayoutManager childLM, LayoutContext context, | |||||
int alignment, Stack lmStack, Position position, LayoutManager restartAtLM) { | |||||
if (handleSpanChange(childLM, elements, context)) { | |||||
SpaceResolver.resolveElementList(elements); | |||||
return elements; | |||||
} | |||||
LayoutContext childLC = new LayoutContext(0); | |||||
List childrenElements = getNextChildElements(childLM, context, childLC, alignment, lmStack, | |||||
position, restartAtLM); | |||||
if (elements.isEmpty()) { | |||||
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); | |||||
} | |||||
if (!elements.isEmpty() | |||||
&& !ElementListUtils.startsWithForcedBreak(childrenElements)) { | |||||
addInBetweenBreak(elements, context, childLC); | |||||
} | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
elements.addAll(childrenElements); | |||||
if (ElementListUtils.endsWithForcedBreak(elements)) { | |||||
// a descendant of this flow has break-before or break-after | |||||
if (childLM.isFinished() && !hasNextChildLM()) { | |||||
setFinished(true); | |||||
} | } | ||||
SpaceResolver.resolveElementList(elements); | |||||
return elements; | |||||
} | |||||
return null; | |||||
} | |||||
//Propagate and clear | |||||
context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); | |||||
childLC.clearKeepWithNextPending(); | |||||
private boolean handleSpanChange(LayoutManager childLM, List elements, LayoutContext context) { | |||||
int span = EN_NONE; | |||||
int disableColumnBalancing = EN_FALSE; | |||||
if (childLM instanceof BlockLayoutManager) { | |||||
span = ((BlockLayoutManager)childLM).getBlockFO().getSpan(); | |||||
disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO() | |||||
.getDisableColumnBalancing(); | |||||
} else if (childLM instanceof BlockContainerLayoutManager) { | |||||
span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan(); | |||||
disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO() | |||||
.getDisableColumnBalancing(); | |||||
} | |||||
context.updateKeepWithNextPending(getKeepWithNext()); | |||||
int currentSpan = context.getCurrentSpan(); | |||||
if (currentSpan != span) { | |||||
if (span == EN_ALL) { | |||||
context.setDisableColumnBalancing(disableColumnBalancing); | |||||
} | |||||
log.debug("span change from " + currentSpan + " to " + span); | |||||
context.signalSpanChange(span); | |||||
return true; | |||||
} else { | |||||
return false; | |||||
} | } | ||||
} | |||||
SpaceResolver.resolveElementList(returnList); | |||||
setFinished(true); | |||||
private List getNextChildElements(LayoutManager childLM, LayoutContext context, | |||||
LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition, | |||||
LayoutManager restartLM) { | |||||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||||
childLC.setRefIPD(context.getRefIPD()); | |||||
childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); | |||||
if (returnList.size() > 0) { | |||||
return returnList; | |||||
List childrenElements; | |||||
if (lmStack == null) { | |||||
childrenElements = childLM.getNextKnuthElements(childLC, alignment); | |||||
} else { | } else { | ||||
return null; | |||||
childrenElements = childLM.getNextKnuthElements(childLC, | |||||
alignment, lmStack, restartPosition, restartLM); | |||||
} | } | ||||
assert !childrenElements.isEmpty(); | |||||
// "wrap" the Position inside each element | |||||
List tempList = childrenElements; | |||||
childrenElements = new LinkedList(); | |||||
wrapPositionElements(tempList, childrenElements); | |||||
return childrenElements; | |||||
} | } | ||||
/** | /** | ||||
return getCurrentPV().getBodyRegion().getBPD(); | return getCurrentPV().getBodyRegion().getBPD(); | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public boolean isRestartable() { | |||||
return true; | |||||
} | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/* (non-Javadoc) | |||||
* {@inheritDoc} | |||||
*/ | |||||
/** {@inheritDoc} */ | |||||
public boolean canAppendSequence(KnuthSequence sequence) { | public boolean canAppendSequence(KnuthSequence sequence) { | ||||
return sequence.isInlineSequence() && !isClosed; | return sequence.isInlineSequence() && !isClosed; | ||||
} | } | ||||
/* (non-Javadoc) | |||||
* {@inheritDoc} | |||||
*/ | |||||
/** {@inheritDoc} */ | |||||
public boolean appendSequence(KnuthSequence sequence) { | public boolean appendSequence(KnuthSequence sequence) { | ||||
if (!canAppendSequence(sequence)) { | if (!canAppendSequence(sequence)) { | ||||
return false; | return false; | ||||
return true; | return true; | ||||
} | } | ||||
/* (non-Javadoc) | |||||
* {@inheritDoc} | |||||
*/ | |||||
/** {@inheritDoc} */ | |||||
public boolean appendSequence(KnuthSequence sequence, boolean keepTogether, | public boolean appendSequence(KnuthSequence sequence, boolean keepTogether, | ||||
BreakElement breakElement) { | BreakElement breakElement) { | ||||
return appendSequence(sequence); | return appendSequence(sequence); | ||||
} | } | ||||
/* (non-Javadoc) | |||||
* {@inheritDoc} | |||||
*/ | |||||
/** {@inheritDoc} */ | |||||
public KnuthSequence endSequence() { | public KnuthSequence endSequence() { | ||||
if (!isClosed) { | if (!isClosed) { | ||||
add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); | add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); |
* level LM to allow them to optimize returned break possibilities. | * level LM to allow them to optimize returned break possibilities. | ||||
*/ | */ | ||||
private MinOptMax stackLimitBP; | private MinOptMax stackLimitBP; | ||||
/** | |||||
* Total available stacking dimension for a "galley-level" layout | |||||
* manager in inline-progression-direction. It is passed by the | |||||
* parent LM. For LineLM, the block LM determines this based on | |||||
* indent properties. | |||||
* These LM <b>may</b> wish to pass this information down to lower | |||||
* level LM to allow them to optimize returned break possibilities. | |||||
*/ | |||||
private MinOptMax stackLimitIP; | |||||
/** to keep track of spanning in multi-column layout */ | /** to keep track of spanning in multi-column layout */ | ||||
private int currentSpan = Constants.NOT_SET; | private int currentSpan = Constants.NOT_SET; | ||||
this.flags = parentLC.flags; | this.flags = parentLC.flags; | ||||
this.refIPD = parentLC.refIPD; | this.refIPD = parentLC.refIPD; | ||||
this.writingMode = parentLC.writingMode; | this.writingMode = parentLC.writingMode; | ||||
setStackLimitsFrom(parentLC); | |||||
setStackLimitBP(parentLC.getStackLimitBP()); | |||||
this.leadingSpace = parentLC.leadingSpace; //??? | this.leadingSpace = parentLC.leadingSpace; //??? | ||||
this.trailingSpace = parentLC.trailingSpace; //??? | this.trailingSpace = parentLC.trailingSpace; //??? | ||||
this.hyphContext = parentLC.hyphContext; | this.hyphContext = parentLC.hyphContext; | ||||
this.flags = flags; | this.flags = flags; | ||||
this.refIPD = 0; | this.refIPD = 0; | ||||
stackLimitBP = new MinOptMax(0); | stackLimitBP = new MinOptMax(0); | ||||
stackLimitIP = new MinOptMax(0); | |||||
leadingSpace = null; | leadingSpace = null; | ||||
trailingSpace = null; | trailingSpace = null; | ||||
} | } | ||||
return stackLimitBP; | return stackLimitBP; | ||||
} | } | ||||
/** | |||||
* Sets the stack limit in inline-progression-dimension. | |||||
* @param limit the stack limit | |||||
*/ | |||||
public void setStackLimitIP(MinOptMax limit) { | |||||
stackLimitIP = limit; | |||||
} | |||||
/** | |||||
* Returns the stack limit in inline-progression-dimension. | |||||
* @return the stack limit | |||||
*/ | |||||
public MinOptMax getStackLimitIP() { | |||||
return stackLimitIP; | |||||
} | |||||
/** | |||||
* Sets (Copies) the stack limits in both directions from another layout context. | |||||
* @param context the layout context to take the values from | |||||
*/ | |||||
public void setStackLimitsFrom(LayoutContext context) { | |||||
setStackLimitBP(context.getStackLimitBP()); | |||||
setStackLimitIP(context.getStackLimitIP()); | |||||
} | |||||
/** | /** | ||||
* Sets the inline-progression-dimension of the nearest ancestor reference area. | * Sets the inline-progression-dimension of the nearest ancestor reference area. | ||||
*/ | */ | ||||
return "Layout Context:" | return "Layout Context:" | ||||
+ "\nStack Limit BPD: \t" | + "\nStack Limit BPD: \t" | ||||
+ (getStackLimitBP() == null ? "null" : getStackLimitBP().toString()) | + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString()) | ||||
+ "\nStack Limit IPD: \t" | |||||
+ (getStackLimitIP() == null ? "null" : getStackLimitIP().toString()) | |||||
+ "\nTrailing Space: \t" | + "\nTrailing Space: \t" | ||||
+ (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) | + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) | ||||
+ "\nLeading Space: \t" | + "\nLeading Space: \t" |
package org.apache.fop.layoutmgr; | package org.apache.fop.layoutmgr; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Stack; | |||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.datatypes.PercentBaseContext; | import org.apache.fop.datatypes.PercentBaseContext; | ||||
* @return the same Position but with a position index | * @return the same Position but with a position index | ||||
*/ | */ | ||||
Position notifyPos(Position pos); | Position notifyPos(Position pos); | ||||
/** | |||||
* Re-initializes this layout manager in order to re-generate its Knuth | |||||
* elements according to a new IPD value. | |||||
*/ | |||||
void reset(); | |||||
/** | |||||
* Returns {@code true} if this layout manager is able to re-generate its | |||||
* Knuth elements after an IPD change. | |||||
* | |||||
* @return {@code true} if this layout manager can be restarted after an IPD | |||||
* change | |||||
*/ | |||||
boolean isRestartable(); | |||||
/** | |||||
* Returns an updated list of Knuth elements corresponding to this layout | |||||
* manager, after a change of IPD has been detected. | |||||
* | |||||
* @param context the layout context | |||||
* @param alignment the alignment | |||||
* @param lmStack the stack of LMs that are active at the IPD change | |||||
* @param positionAtIPDChange the position corresponding to the element | |||||
* finishing the page before the IPD change | |||||
* @param restartAtLM if not null, the layout manager from which to restart. | |||||
* That is, the IPD change occurs between two block elements and not inside | |||||
* a paragraph | |||||
* @return an updated list of elements, taking the new IPD into account | |||||
*/ | |||||
List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM); | |||||
} | } |
public class LeafPosition extends Position { | public class LeafPosition extends Position { | ||||
private int iLeafPos; | |||||
private int leafPos; | |||||
public LeafPosition(LayoutManager lm, int pos) { | public LeafPosition(LayoutManager lm, int pos) { | ||||
super(lm); | super(lm); | ||||
iLeafPos = pos; | |||||
leafPos = pos; | |||||
} | |||||
public LeafPosition(LayoutManager layoutManager, int pos, int index) { | |||||
super(layoutManager, index); | |||||
leafPos = pos; | |||||
} | } | ||||
public int getLeafPos() { | public int getLeafPos() { | ||||
return iLeafPos; | |||||
return leafPos; | |||||
} | } | ||||
public boolean generatesAreas() { | public boolean generatesAreas() { |
return pslm.getPageProvider(); | return pslm.getPageProvider(); | ||||
} | } | ||||
/** | |||||
* Starts the page breaking process. | |||||
* @param flowBPD the constant available block-progression-dimension (used for every part) | |||||
*/ | |||||
void doLayout(int flowBPD) { | |||||
doLayout(flowBPD, false); | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected PageBreakingLayoutListener createLayoutListener() { | protected PageBreakingLayoutListener createLayoutListener() { | ||||
return new PageBreakingLayoutListener() { | return new PageBreakingLayoutListener() { | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected int getNextBlockList(LayoutContext childLC, | protected int getNextBlockList(LayoutContext childLC, | ||||
int nextSequenceStartsOn) { | int nextSequenceStartsOn) { | ||||
return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, | |||||
Position positionAtIPDChange, LayoutManager restartLM, List firstElements) { | |||||
if (!firstPart) { | if (!firstPart) { | ||||
// if this is the first page that will be created by | // if this is the first page that will be created by | ||||
// the current BlockSequence, it could have a break | // the current BlockSequence, it could have a break | ||||
pageBreakHandled = true; | pageBreakHandled = true; | ||||
pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), | pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), | ||||
pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); | pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); | ||||
return super.getNextBlockList(childLC, nextSequenceStartsOn); | |||||
return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange, | |||||
restartLM, firstElements); | |||||
} | } | ||||
private boolean containsFootnotes(List contentList, LayoutContext context) { | private boolean containsFootnotes(List contentList, LayoutContext context) { | ||||
return contentList; | return contentList; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
protected List getNextKnuthElements(LayoutContext context, int alignment, | |||||
Position positionAtIPDChange, LayoutManager restartAtLM) { | |||||
List contentList = null; | |||||
do { | |||||
contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange, | |||||
restartAtLM); | |||||
} while (!childFLM.isFinished() && contentList == null); | |||||
// scan contentList, searching for footnotes | |||||
if (containsFootnotes(contentList, context)) { | |||||
// handle the footnote separator | |||||
handleFootnoteSeparator(); | |||||
} | |||||
return contentList; | |||||
} | |||||
/** | /** | ||||
* @return current display alignment | * @return current display alignment | ||||
*/ | */ |
//Controls whether a single part should be forced if possible (ex. block-container) | //Controls whether a single part should be forced if possible (ex. block-container) | ||||
private boolean favorSinglePart = false; | private boolean favorSinglePart = false; | ||||
private boolean ipdChange; | |||||
private KnuthNode bestNodeForIPDChange; | |||||
//Used to keep track of switches in keep-context | //Used to keep track of switches in keep-context | ||||
private int currentKeepContext = Constants.EN_AUTO; | private int currentKeepContext = Constants.EN_AUTO; | ||||
private KnuthNode lastBeforeKeepContextSwitch; | private KnuthNode lastBeforeKeepContextSwitch; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
protected boolean ipdChanged() { | |||||
return ipdChange; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
protected int handleIpdChange() { | |||||
log.trace("Best node for ipd change:" + bestNodeForIPDChange); | |||||
// TODO finish() | |||||
/* | |||||
* The third parameter is used to determine if this is the last page, so | |||||
* if the content must be vertically justified or not. If we are here | |||||
* this means that there is further content and the next page has a | |||||
* different ipd. So tweak the parameter to fall into the non-last-page | |||||
* case. | |||||
*/ | |||||
calculateBreakPoints(bestNodeForIPDChange, par, bestNodeForIPDChange.line + 1); | |||||
activeLines = null; | |||||
return bestNodeForIPDChange.line; | |||||
} | |||||
/** | |||||
* Add a node at the end of the given line's existing active nodes. | |||||
* If this is the first node in the line, adjust endLine accordingly. | |||||
* @param line number of the line ending at the node's corresponding breakpoint | |||||
* @param node the active node to add | |||||
*/ | |||||
protected void addNode(int line, KnuthNode node) { | |||||
if (node.position < par.size() - 1 && line > 0 && ipdChange(line - 1)) { | |||||
log.trace("IPD changes at page " + line); | |||||
ipdChange = true; | |||||
if (bestNodeForIPDChange == null | |||||
|| node.totalDemerits < bestNodeForIPDChange.totalDemerits) { | |||||
bestNodeForIPDChange = node; | |||||
} | |||||
} else { | |||||
if (node.position == par.size() - 1) { | |||||
/* | |||||
* The whole sequence could actually fit on the last page before | |||||
* the IPD change. No need to do any special handling. | |||||
*/ | |||||
ipdChange = false; | |||||
} | |||||
super.addNode(line, node); | |||||
} | |||||
} | |||||
KnuthNode getBestNodeBeforeIPDChange() { | |||||
return bestNodeForIPDChange; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
protected boolean ipdChange(int line) { | |||||
if (pageProvider == null) { | |||||
return false; | |||||
} | |||||
return pageProvider.ipdChange(line); | |||||
} | |||||
} | } |
return new int[] {colIndex, columnCount}; | return new int[] {colIndex, columnCount}; | ||||
} | } | ||||
/** | |||||
* Returns true if the part following the given one has a different IPD. | |||||
* | |||||
* @param index index of the current part | |||||
* @return true if the following part has a different IPD, false otherwise | |||||
*/ | |||||
public boolean ipdChange(int index) { | |||||
int columnCount = 0; | |||||
int colIndex = startColumnOfCurrentElementList + index; | |||||
int pageIndex = -1; | |||||
Page page; | |||||
do { | |||||
colIndex -= columnCount; | |||||
pageIndex++; | |||||
page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||||
columnCount = page.getPageViewport().getCurrentSpan().getColumnCount(); | |||||
} while (colIndex >= columnCount); | |||||
if (colIndex + 1 < columnCount) { | |||||
// Next part is a column on same page => same IPD | |||||
return false; | |||||
} else { | |||||
Page nextPage = getPage(false, pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST); | |||||
return page.getPageViewport().getBodyRegion().getIPD() | |||||
!= nextPage.getPageViewport().getBodyRegion().getIPD(); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Checks if a break at the passed index would start a new page | * Checks if a break at the passed index would start a new page | ||||
* @param index the index of the element before the break | * @param index the index of the element before the break |
layoutManager = lm; | layoutManager = lm; | ||||
} | } | ||||
public Position(LayoutManager lm, int index) { | |||||
this(lm); | |||||
setIndex(index); | |||||
} | |||||
public LayoutManager getLM() { | public LayoutManager getLM() { | ||||
return layoutManager; | return layoutManager; | ||||
} | } |
package org.apache.fop.layoutmgr; | package org.apache.fop.layoutmgr; | ||||
import java.util.LinkedList; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
public Position getOriginalBreakPosition() { | public Position getOriginalBreakPosition() { | ||||
return this.originalPosition; | return this.originalPosition; | ||||
} | } | ||||
public Position getPosition() { | |||||
return originalPosition; | |||||
} | |||||
} | } | ||||
/** | /** |
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | |||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.Block; | import org.apache.fop.area.Block; | ||||
import org.apache.fop.fo.pagination.SideRegion; | import org.apache.fop.fo.pagination.SideRegion; | ||||
import org.apache.fop.fo.pagination.StaticContent; | import org.apache.fop.fo.pagination.StaticContent; | ||||
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; | import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; | ||||
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; | |||||
import org.apache.fop.layoutmgr.inline.TextLayoutManager; | import org.apache.fop.layoutmgr.inline.TextLayoutManager; | ||||
import org.apache.fop.traits.MinOptMax; | |||||
import org.apache.fop.util.ListUtil; | |||||
/** | /** | ||||
* LayoutManager for an fo:flow object. | * LayoutManager for an fo:flow object. | ||||
*/ | */ | ||||
public class StaticContentLayoutManager extends BlockStackingLayoutManager { | public class StaticContentLayoutManager extends BlockStackingLayoutManager { | ||||
/** | |||||
* logging instance | |||||
*/ | |||||
private static Log log = LogFactory.getLog(StaticContentLayoutManager.class); | |||||
private RegionReference targetRegion; | private RegionReference targetRegion; | ||||
private Block targetBlock; | private Block targetBlock; | ||||
private SideRegion regionFO; | private SideRegion regionFO; | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public List getNextKnuthElements(LayoutContext context, int alignment) { | public List getNextKnuthElements(LayoutContext context, int alignment) { | ||||
if (true) { | |||||
throw new UnsupportedOperationException( | |||||
"Shouldn't this method be emptied because it's never called at all?"); | |||||
} | |||||
//TODO Empty this method?!? | |||||
// set layout dimensions | |||||
setContentAreaIPD(context.getRefIPD()); | |||||
setContentAreaBPD(context.getStackLimitBP().opt); | |||||
//TODO Copied from elsewhere. May be worthwhile to factor out the common parts. | |||||
// currently active LM | |||||
BlockLevelLayoutManager curLM; | |||||
BlockLevelLayoutManager prevLM = null; | |||||
MinOptMax stackSize = new MinOptMax(); | |||||
List returnedList; | |||||
List returnList = new LinkedList(); | |||||
while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) { | |||||
if (curLM instanceof InlineLevelLayoutManager) { | |||||
log.error("inline area not allowed under flow - ignoring"); | |||||
curLM.setFinished(true); | |||||
continue; | |||||
} | |||||
// Set up a LayoutContext | |||||
MinOptMax bpd = context.getStackLimitBP(); | |||||
LayoutContext childLC = new LayoutContext(0); | |||||
childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize)); | |||||
childLC.setRefIPD(context.getRefIPD()); | |||||
// get elements from curLM | |||||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||||
//log.debug("FLM.getNextKnuthElements> returnedList.size() = " | |||||
// + returnedList.size()); | |||||
// "wrap" the Position inside each element | |||||
List tempList = returnedList; | |||||
KnuthElement tempElement; | |||||
returnedList = new LinkedList(); | |||||
ListIterator listIter = tempList.listIterator(); | |||||
while (listIter.hasNext()) { | |||||
tempElement = (KnuthElement)listIter.next(); | |||||
tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition())); | |||||
returnedList.add(tempElement); | |||||
} | |||||
if (returnedList.size() == 1 | |||||
&& ((KnuthElement)returnedList.get(0)).isPenalty() | |||||
&& ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) { | |||||
// a descendant of this flow has break-before | |||||
returnList.addAll(returnedList); | |||||
return returnList; | |||||
} else { | |||||
if (!returnList.isEmpty()) { | |||||
// there is a block before this one | |||||
if (prevLM.mustKeepWithNext() | |||||
|| curLM.mustKeepWithPrevious()) { | |||||
// add an infinite penalty to forbid a break between blocks | |||||
returnList.add(new KnuthPenalty(0, | |||||
KnuthElement.INFINITE, false, | |||||
new Position(this), false)); | |||||
} else if (!((KnuthElement) ListUtil.getLast(returnList)) | |||||
.isGlue()) { | |||||
// add a null penalty to allow a break between blocks | |||||
returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); | |||||
} | |||||
} | |||||
/*LF*/ if (!returnedList.isEmpty()) { // controllare! | |||||
returnList.addAll(returnedList); | |||||
final KnuthElement last = (KnuthElement) ListUtil | |||||
.getLast(returnedList); | |||||
if (last.isPenalty() | |||||
&& ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) { | |||||
// a descendant of this flow has break-after | |||||
/*LF*/ //log.debug("FLM - break after!!"); | |||||
return returnList; | |||||
} | |||||
/*LF*/ } | |||||
} | |||||
prevLM = curLM; | |||||
} | |||||
setFinished(true); | |||||
if (returnList.isEmpty()) { | |||||
return null; | |||||
} else { | |||||
return returnList; | |||||
} | |||||
throw new IllegalStateException(); | |||||
} | } | ||||
/** | /** |
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.Block; | import org.apache.fop.area.Block; | ||||
import org.apache.fop.area.LineArea; | import org.apache.fop.area.LineArea; | ||||
import org.apache.fop.layoutmgr.Position; | import org.apache.fop.layoutmgr.Position; | ||||
import org.apache.fop.layoutmgr.PositionIterator; | import org.apache.fop.layoutmgr.PositionIterator; | ||||
import org.apache.fop.layoutmgr.SpaceSpecifier; | import org.apache.fop.layoutmgr.SpaceSpecifier; | ||||
import org.apache.fop.traits.MinOptMax; | |||||
/** | /** | ||||
* Content Layout Manager. | * Content Layout Manager. | ||||
LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA); | LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA); | ||||
childLC.setLeadingSpace(new SpaceSpecifier(false)); | childLC.setLeadingSpace(new SpaceSpecifier(false)); | ||||
childLC.setTrailingSpace(new SpaceSpecifier(false)); | childLC.setTrailingSpace(new SpaceSpecifier(false)); | ||||
// set stackLimit for lines | |||||
childLC.setStackLimitIP(new MinOptMax(ipd)); | |||||
childLC.setRefIPD(ipd); | childLC.setRefIPD(ipd); | ||||
int lineHeight = 14000; | int lineHeight = 14000; | ||||
stackSize = 0; | stackSize = 0; | ||||
List contentList = | |||||
getNextKnuthElements(childLC, Constants.EN_START); | |||||
List contentList = getNextKnuthElements(childLC, Constants.EN_START); | |||||
ListIterator contentIter = contentList.listIterator(); | ListIterator contentIter = contentList.listIterator(); | ||||
while (contentIter.hasNext()) { | while (contentIter.hasNext()) { | ||||
KnuthElement element = (KnuthElement) contentIter.next(); | KnuthElement element = (KnuthElement) contentIter.next(); | ||||
lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); | lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); | ||||
lc.setLeadingSpace(new SpaceSpecifier(false)); | lc.setLeadingSpace(new SpaceSpecifier(false)); | ||||
lc.setTrailingSpace(new SpaceSpecifier(false)); | lc.setTrailingSpace(new SpaceSpecifier(false)); | ||||
KnuthPossPosIter contentPosIter = | |||||
new KnuthPossPosIter(contentList, 0, contentList.size()); | |||||
KnuthPossPosIter contentPosIter = new KnuthPossPosIter(contentList, 0, contentList.size()); | |||||
curLM.addAreas(contentPosIter, lc); | curLM.addAreas(contentPosIter, lc); | ||||
} | } | ||||
List returnList = new LinkedList(); | List returnList = new LinkedList(); | ||||
KnuthSequence lastSequence = null; | KnuthSequence lastSequence = null; | ||||
SpaceSpecifier leadingSpace = context.getLeadingSpace(); | |||||
if (fobj instanceof Title) { | if (fobj instanceof Title) { | ||||
alignmentContext = new AlignmentContext(font, | alignmentContext = new AlignmentContext(font, | ||||
lineHeight.getOptimum(this).getLength().getValue(this), | lineHeight.getOptimum(this).getLength().getValue(this), | ||||
if (getSpaceStart() != null) { | if (getSpaceStart() != null) { | ||||
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this)); | context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this)); | ||||
} | } | ||||
// Check for "fence" | |||||
if (hasLeadingFence(!context.isFirstArea())) { | |||||
// Reset leading space sequence for child areas | |||||
leadingSpace = new SpaceSpecifier(false); | |||||
} | |||||
// Reset state variables | |||||
clearPrevIPD(); // Clear stored prev content dimensions | |||||
} | } | ||||
StringBuffer trace = new StringBuffer("InlineLM:"); | StringBuffer trace = new StringBuffer("InlineLM:"); |
package org.apache.fop.layoutmgr.inline; | package org.apache.fop.layoutmgr.inline; | ||||
import java.util.LinkedList; | |||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.LinkedList; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
import java.util.HashMap; | |||||
import org.apache.fop.area.Area; | |||||
import org.apache.fop.area.inline.Space; | |||||
import org.apache.fop.fo.FObj; | import org.apache.fop.fo.FObj; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | import org.apache.fop.fo.properties.SpaceProperty; | ||||
import org.apache.fop.layoutmgr.AbstractLayoutManager; | import org.apache.fop.layoutmgr.AbstractLayoutManager; | ||||
import org.apache.fop.layoutmgr.NonLeafPosition; | import org.apache.fop.layoutmgr.NonLeafPosition; | ||||
import org.apache.fop.layoutmgr.Position; | import org.apache.fop.layoutmgr.Position; | ||||
import org.apache.fop.layoutmgr.PositionIterator; | import org.apache.fop.layoutmgr.PositionIterator; | ||||
import org.apache.fop.area.Area; | |||||
import org.apache.fop.area.inline.Space; | |||||
import org.apache.fop.traits.MinOptMax; | import org.apache.fop.traits.MinOptMax; | ||||
/** | /** | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Size of any start or end borders and padding. | |||||
*/ | |||||
private MinOptMax allocIPD = new MinOptMax(0); | |||||
/** | /** | ||||
* Size of border and padding in BPD (ie, before and after). | * Size of border and padding in BPD (ie, before and after). | ||||
*/ | */ | ||||
/** The child layout context */ | /** The child layout context */ | ||||
protected LayoutContext childLC; | protected LayoutContext childLC; | ||||
/** Used to store previous content IPD for each child LM. */ | |||||
private HashMap hmPrevIPD = new HashMap(); | |||||
/** | /** | ||||
* Create an inline stacking layout manager. | * Create an inline stacking layout manager. | ||||
* This is used for fo's that create areas that | * This is used for fo's that create areas that | ||||
return null; | return null; | ||||
} | } | ||||
/** | |||||
* TODO: Explain this method | |||||
* @param lm ??? | |||||
* @return ??? | |||||
*/ | |||||
protected MinOptMax getPrevIPD(LayoutManager lm) { | |||||
return (MinOptMax) hmPrevIPD.get(lm); | |||||
} | |||||
/** | |||||
* Clear the previous IPD calculation. | |||||
*/ | |||||
protected void clearPrevIPD() { | |||||
hmPrevIPD.clear(); | |||||
} | |||||
/** | /** | ||||
* Returns the current area. | * Returns the current area. | ||||
* @return the current area | * @return the current area |
package org.apache.fop.layoutmgr.inline; | package org.apache.fop.layoutmgr.inline; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Iterator; | |||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
* inline break positions. | * inline break positions. | ||||
*/ | */ | ||||
private static class LineBreakPosition extends LeafPosition { | private static class LineBreakPosition extends LeafPosition { | ||||
private int iParIndex; // index of the Paragraph this Position refers to | |||||
private int iStartIndex; //index of the first element this Position refers to | |||||
private int parIndex; // index of the Paragraph this Position refers to | |||||
private int startIndex; //index of the first element this Position refers to | |||||
private int availableShrink; | private int availableShrink; | ||||
private int availableStretch; | private int availableStretch; | ||||
private int difference; | private int difference; | ||||
private int spaceAfter; | private int spaceAfter; | ||||
private int baseline; | private int baseline; | ||||
LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex, | |||||
LineBreakPosition(LayoutManager lm, int index, int startIndex, int breakIndex, | |||||
int shrink, int stretch, int diff, | int shrink, int stretch, int diff, | ||||
double ipdA, double adjust, int ind, | double ipdA, double adjust, int ind, | ||||
int lh, int lw, int sb, int sa, int bl) { | int lh, int lw, int sb, int sa, int bl) { | ||||
super(lm, iBreakIndex); | |||||
super(lm, breakIndex); | |||||
availableShrink = shrink; | availableShrink = shrink; | ||||
availableStretch = stretch; | availableStretch = stretch; | ||||
difference = diff; | difference = diff; | ||||
iParIndex = index; | |||||
this.iStartIndex = iStartIndex; | |||||
parIndex = index; | |||||
this.startIndex = startIndex; | |||||
ipdAdjust = ipdA; | ipdAdjust = ipdA; | ||||
dAdjust = adjust; | dAdjust = adjust; | ||||
startIndent = ind; | startIndent = ind; | ||||
private Length lineHeight; | private Length lineHeight; | ||||
private int lead; | private int lead; | ||||
private int follow; | private int follow; | ||||
private AlignmentContext alignmentContext = null; | |||||
private AlignmentContext alignmentContext; | |||||
private List knuthParagraphs = null; | |||||
private int iReturnedLBP = 0; | |||||
// parameters of Knuth's algorithm: | |||||
// penalty value for flagged penalties | |||||
private int flaggedPenalty = 50; | |||||
private List knuthParagraphs; | |||||
private LineLayoutPossibilities lineLayouts; | private LineLayoutPossibilities lineLayouts; | ||||
private List lineLayoutsList; | private List lineLayoutsList; | ||||
private int iLineWidth = 0; | |||||
private int ipd = 0; | |||||
/** | |||||
* When layout must be re-started due to a change of IPD, there is no need | |||||
* to perform hyphenation on the remaining Knuth sequence once again. | |||||
*/ | |||||
private boolean hyphenationPerformed; | |||||
/** | /** | ||||
* this constant is used to create elements when text-align is center: | * this constant is used to create elements when text-align is center: | ||||
} else { | } else { | ||||
lineFiller = new MinOptMax(lastLineEndIndent, | lineFiller = new MinOptMax(lastLineEndIndent, | ||||
lastLineEndIndent, | lastLineEndIndent, | ||||
layoutManager.iLineWidth); | |||||
layoutManager.ipd); | |||||
} | } | ||||
// add auxiliary elements at the beginning of the paragraph | // add auxiliary elements at the beginning of the paragraph | ||||
private int activePossibility; | private int activePossibility; | ||||
private int addedPositions; | private int addedPositions; | ||||
private int textIndent; | private int textIndent; | ||||
private int fillerMinWidth; | |||||
private int lineHeight; | private int lineHeight; | ||||
private int lead; | private int lead; | ||||
private int follow; | private int follow; | ||||
private int maxDiff; | |||||
private static final double MAX_DEMERITS = 10e6; | private static final double MAX_DEMERITS = 10e6; | ||||
public LineBreakingAlgorithm (int pageAlign, | public LineBreakingAlgorithm (int pageAlign, | ||||
super(textAlign, textAlignLast, first, false, maxFlagCount); | super(textAlign, textAlignLast, first, false, maxFlagCount); | ||||
pageAlignment = pageAlign; | pageAlignment = pageAlign; | ||||
textIndent = indent; | textIndent = indent; | ||||
fillerMinWidth = fillerWidth; | |||||
lineHeight = lh; | lineHeight = lh; | ||||
lead = ld; | lead = ld; | ||||
follow = fl; | follow = fl; | ||||
thisLLM = llm; | thisLLM = llm; | ||||
activePossibility = -1; | activePossibility = -1; | ||||
maxDiff = fobj.getWidows() >= fobj.getOrphans() | |||||
? fobj.getWidows() | |||||
: fobj.getOrphans(); | |||||
} | } | ||||
public void updateData1(int lineCount, double demerits) { | public void updateData1(int lineCount, double demerits) { | ||||
lineLayouts.addPossibility(lineCount, demerits); | lineLayouts.addPossibility(lineCount, demerits); | ||||
if (super.log.isTraceEnabled()) { | |||||
super.log.trace( | |||||
"Layout possibility in " + lineCount + " lines; break at position:"); | |||||
if (log.isTraceEnabled()) { | |||||
log.trace("Layout possibility in " + lineCount + " lines; break at position:"); | |||||
} | } | ||||
} | } | ||||
// true if this line contains only zero-height, auxiliary boxes | // true if this line contains only zero-height, auxiliary boxes | ||||
// and the actual line width is 0; in this case, the line "collapses" | // and the actual line width is 0; in this case, the line "collapses" | ||||
// i.e. the line area will have bpd = 0 | // i.e. the line area will have bpd = 0 | ||||
boolean bZeroHeightLine = (difference == iLineWidth); | |||||
boolean bZeroHeightLine = (difference == ipd); | |||||
// if line-stacking-strategy is "font-height", the line height | // if line-stacking-strategy is "font-height", the line height | ||||
// is not affected by its content | // is not affected by its content | ||||
firstElementIndex, lastElementIndex, | firstElementIndex, lastElementIndex, | ||||
availableShrink, availableStretch, | availableShrink, availableStretch, | ||||
difference, ratio, 0, indent, | difference, ratio, 0, indent, | ||||
0, iLineWidth, 0, 0, 0); | |||||
0, ipd, 0, 0, 0); | |||||
} else { | } else { | ||||
return new LineBreakPosition(thisLLM, | return new LineBreakPosition(thisLLM, | ||||
knuthParagraphs.indexOf(par), | knuthParagraphs.indexOf(par), | ||||
availableShrink, availableStretch, | availableShrink, availableStretch, | ||||
difference, ratio, 0, indent, | difference, ratio, 0, indent, | ||||
lineLead + lineFollow, | lineLead + lineFollow, | ||||
iLineWidth, spaceBefore, spaceAfter, | |||||
ipd, spaceBefore, spaceAfter, | |||||
lineLead); | lineLead); | ||||
} | } | ||||
} | } | ||||
public int findBreakingPoints(Paragraph par, /*int lineWidth,*/ | |||||
double threshold, boolean force, | |||||
int allowedBreaks) { | |||||
return super.findBreakingPoints(par, /*lineWidth,*/ | |||||
threshold, force, allowedBreaks); | |||||
} | |||||
protected int filterActiveNodes() { | protected int filterActiveNodes() { | ||||
KnuthNode bestActiveNode = null; | KnuthNode bestActiveNode = null; | ||||
FontInfo fi = fobj.getFOEventHandler().getFontInfo(); | FontInfo fi = fobj.getFOEventHandler().getFontInfo(); | ||||
FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); | FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); | ||||
Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); | Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); | ||||
alignmentContext | |||||
= new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode()); | |||||
alignmentContext = new AlignmentContext(fs, lineHeight.getValue(this), | |||||
context.getWritingMode()); | |||||
context.setAlignmentContext(alignmentContext); | context.setAlignmentContext(alignmentContext); | ||||
// Get a break from currently active child LM | |||||
// Set up constraints for inline level managers | |||||
clearPrevIPD(); | |||||
ipd = context.getRefIPD(); | |||||
//PHASE 1: Create Knuth elements | //PHASE 1: Create Knuth elements | ||||
if (knuthParagraphs == null) { | if (knuthParagraphs == null) { | ||||
//PHASE 2: Create line breaks | //PHASE 2: Create line breaks | ||||
return createLineBreaks(context.getBPAlignment(), context); | return createLineBreaks(context.getBPAlignment(), context); | ||||
/* | |||||
LineBreakPosition lbp = null; | |||||
if (breakpoints == null) { | |||||
// find the optimal line breaking points for each paragraph | |||||
breakpoints = new ArrayList(); | |||||
ListIterator paragraphsIterator | |||||
= knuthParagraphs.listIterator(knuthParagraphs.size()); | |||||
Paragraph currPar = null; | |||||
while (paragraphsIterator.hasPrevious()) { | |||||
currPar = (Paragraph) paragraphsIterator.previous(); | |||||
findBreakingPoints(currPar, context.getStackLimit().opt); | |||||
} | |||||
}*/ | |||||
} | |||||
//PHASE 3: Return lines | |||||
public List getNextKnuthElements(LayoutContext context, int alignment, | |||||
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()) { | |||||
iter.remove(); | |||||
} | |||||
if (!iter.hasNext()) { | |||||
knuthParagraphs.remove(parIndex); | |||||
} | |||||
/* | |||||
// get a break point from the list | |||||
lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++); | |||||
if (iReturnedLBP == breakpoints.size()) { | |||||
// return finished when there's no content | |||||
if (knuthParagraphs.size() == 0) { | |||||
setFinished(true); | setFinished(true); | ||||
return null; | |||||
} | } | ||||
BreakPoss curLineBP = new BreakPoss(lbp); | |||||
curLineBP.setFlag(BreakPoss.ISLAST, isFinished()); | |||||
curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight)); | |||||
return curLineBP; | |||||
*/ | |||||
ipd = context.getRefIPD(); | |||||
//PHASE 2: Create line breaks | |||||
return createLineBreaks(context.getBPAlignment(), context); | |||||
} | } | ||||
/** | /** | ||||
private void collectInlineKnuthElements(LayoutContext context) { | private void collectInlineKnuthElements(LayoutContext context) { | ||||
LayoutContext inlineLC = new LayoutContext(context); | LayoutContext inlineLC = new LayoutContext(context); | ||||
InlineLevelLayoutManager curLM; | |||||
List returnedList = null; | |||||
iLineWidth = context.getStackLimitIP().opt; | |||||
// convert all the text in a sequence of paragraphs made | // convert all the text in a sequence of paragraphs made | ||||
// of KnuthBox, KnuthGlue and KnuthPenalty objects | // of KnuthBox, KnuthGlue and KnuthPenalty objects | ||||
boolean bPrevWasKnuthBox = false; | |||||
boolean previousIsBox = false; | |||||
StringBuffer trace = new StringBuffer("LineLM:"); | StringBuffer trace = new StringBuffer("LineLM:"); | ||||
Paragraph lastPar = null; | Paragraph lastPar = null; | ||||
InlineLevelLayoutManager curLM; | |||||
while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { | while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { | ||||
returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment); | |||||
if (returnedList == null | |||||
|| returnedList.size() == 0) { | |||||
List inlineElements = curLM.getNextKnuthElements(inlineLC, effectiveAlignment); | |||||
if (inlineElements == null || inlineElements.size() == 0) { | |||||
/* curLM.getNextKnuthElements() returned null or an empty list; | /* curLM.getNextKnuthElements() returned null or an empty list; | ||||
* this can happen if there is nothing more to layout, | * this can happen if there is nothing more to layout, | ||||
* so just iterate once more to see if there are other children */ | * so just iterate once more to see if there are other children */ | ||||
} | } | ||||
if (lastPar != null) { | if (lastPar != null) { | ||||
KnuthSequence firstSeq = (KnuthSequence) returnedList.get(0); | |||||
KnuthSequence firstSeq = (KnuthSequence) inlineElements.get(0); | |||||
// finish last paragraph before a new block sequence | // finish last paragraph before a new block sequence | ||||
if (!firstSeq.isInlineSequence()) { | if (!firstSeq.isInlineSequence()) { | ||||
if (log.isTraceEnabled()) { | if (log.isTraceEnabled()) { | ||||
trace.append(" ]"); | trace.append(" ]"); | ||||
} | } | ||||
bPrevWasKnuthBox = false; | |||||
previousIsBox = false; | |||||
} | } | ||||
// does the first element of the first paragraph add to an existing word? | // does the first element of the first paragraph add to an existing word? | ||||
KnuthElement thisElement; | KnuthElement thisElement; | ||||
thisElement = (KnuthElement) firstSeq.get(0); | thisElement = (KnuthElement) firstSeq.get(0); | ||||
if (thisElement.isBox() && !thisElement.isAuxiliary() | if (thisElement.isBox() && !thisElement.isAuxiliary() | ||||
&& bPrevWasKnuthBox) { | |||||
&& previousIsBox) { | |||||
lastPar.addALetterSpace(); | lastPar.addALetterSpace(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// loop over the KnuthSequences (and single KnuthElements) in returnedList | // loop over the KnuthSequences (and single KnuthElements) in returnedList | ||||
ListIterator iter = returnedList.listIterator(); | |||||
ListIterator iter = inlineElements.listIterator(); | |||||
while (iter.hasNext()) { | while (iter.hasNext()) { | ||||
KnuthSequence sequence = (KnuthSequence) iter.next(); | KnuthSequence sequence = (KnuthSequence) iter.next(); | ||||
// the sequence contains inline Knuth elements | // the sequence contains inline Knuth elements | ||||
if (sequence.isInlineSequence()) { | if (sequence.isInlineSequence()) { | ||||
// look at the last element | // look at the last element | ||||
ListElement lastElement = sequence.getLast(); | ListElement lastElement = sequence.getLast(); | ||||
if (lastElement == null) { | |||||
throw new NullPointerException( | |||||
"Sequence was empty! lastElement is null"); | |||||
} | |||||
bPrevWasKnuthBox = lastElement.isBox() | |||||
&& !((KnuthElement) lastElement).isAuxiliary() | |||||
&& ((KnuthElement) lastElement).getW() != 0; | |||||
assert lastElement != null; | |||||
previousIsBox = lastElement.isBox() | |||||
&& !((KnuthElement) lastElement).isAuxiliary() | |||||
&& ((KnuthElement) lastElement).getW() != 0; | |||||
// if last paragraph is open, add the new elements to the paragraph | // if last paragraph is open, add the new elements to the paragraph | ||||
// else this is the last paragraph | // else this is the last paragraph | ||||
// finish last paragraph if it was closed with a linefeed | // finish last paragraph if it was closed with a linefeed | ||||
if (lastElement.isPenalty() | if (lastElement.isPenalty() | ||||
&& ((KnuthPenalty) lastElement).getP() | |||||
== -KnuthPenalty.INFINITE) { | |||||
&& ((KnuthPenalty) lastElement).getP() == -KnuthPenalty.INFINITE) { | |||||
// a penalty item whose value is -inf | // a penalty item whose value is -inf | ||||
// represents a preserved linefeed, | // represents a preserved linefeed, | ||||
// which forces a line break | // which forces a line break | ||||
if (!lastPar.containsBox()) { | if (!lastPar.containsBox()) { | ||||
//only a forced linefeed on this line | //only a forced linefeed on this line | ||||
//-> compensate with an auxiliary glue | //-> compensate with an auxiliary glue | ||||
lastPar.add(new KnuthGlue(iLineWidth, 0, iLineWidth, null, true)); | |||||
lastPar.add(new KnuthGlue(ipd, 0, ipd, null, true)); | |||||
} | } | ||||
lastPar.endParagraph(); | lastPar.endParagraph(); | ||||
ElementListObserver.observe(lastPar, "line", null); | ElementListObserver.observe(lastPar, "line", null); | ||||
if (log.isTraceEnabled()) { | if (log.isTraceEnabled()) { | ||||
trace.append(" ]"); | trace.append(" ]"); | ||||
} | } | ||||
bPrevWasKnuthBox = false; | |||||
previousIsBox = false; | |||||
} | } | ||||
} else { // the sequence is a block sequence | } else { // the sequence is a block sequence | ||||
// the positions will be wrapped with this LM in postProcessLineBreaks | // the positions will be wrapped with this LM in postProcessLineBreaks | ||||
log.trace(trace); | log.trace(trace); | ||||
} | } | ||||
/** | |||||
* Find a set of breaking points. | |||||
* This method is called only once by getNextBreakPoss, and it | |||||
* subsequently calls the other findBreakingPoints() method with | |||||
* different parameters, until a set of breaking points is found. | |||||
* | |||||
* @param par the list of elements that must be parted | |||||
* into lines | |||||
* @param lineWidth the desired length ot the lines | |||||
*/ | |||||
/* | |||||
private void findBreakingPoints(Paragraph par, int lineWidth) { | |||||
// maximum adjustment ratio permitted | |||||
float maxAdjustment = 1; | |||||
// first try | |||||
if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) { | |||||
// the first try failed, now try something different | |||||
log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment); | |||||
if (hyphenationProperties.hyphenate == Constants.EN_TRUE) { | |||||
// consider every hyphenation point as a legal break | |||||
findHyphenationPoints(par); | |||||
} else { | |||||
// try with a higher threshold | |||||
maxAdjustment = 5; | |||||
} | |||||
if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) { | |||||
// the second try failed too, try with a huge threshold; | |||||
// if this fails too, use a different algorithm | |||||
log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment | |||||
+ (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : "")); | |||||
maxAdjustment = 20; | |||||
if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) { | |||||
log.debug("No set of breaking points found, using first-fit algorithm"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
private boolean findBreakingPoints(Paragraph par, int lineWidth, | |||||
double threshold, boolean force) { | |||||
KnuthParagraph knuthPara = new KnuthParagraph(par); | |||||
int lines = knuthPara.findBreakPoints(lineWidth, threshold, force); | |||||
if (lines == 0) { | |||||
return false; | |||||
} | |||||
for (int i = lines-1; i >= 0; i--) { | |||||
int line = i+1; | |||||
if (log.isTraceEnabled()) { | |||||
log.trace("Making line from " + knuthPara.getStart(i) + " to " + | |||||
knuthPara.getEnd(i)); | |||||
} | |||||
// compute indent and adjustment ratio, according to | |||||
// the value of text-align and text-align-last | |||||
int difference = knuthPara.getDifference(i); | |||||
if (line == lines) { | |||||
difference += par.lineFillerWidth; | |||||
} | |||||
int textAlign = (line < lines) | |||||
? textAlignment : textAlignmentLast; | |||||
int indent = (textAlign == EN_CENTER) | |||||
? difference / 2 | |||||
: (textAlign == EN_END) ? difference : 0; | |||||
indent += (line == 1 && knuthParagraphs.indexOf(par) == 0) | |||||
? textIndent.getValue(this) : 0; | |||||
double ratio = (textAlign == EN_JUSTIFY) | |||||
? knuthPara.getAdjustRatio(i) : 0; | |||||
int start = knuthPara.getStart(i); | |||||
int end = knuthPara.getEnd(i); | |||||
makeLineBreakPosition(par, start, end, 0, ratio, indent); | |||||
} | |||||
return true; | |||||
} | |||||
private void makeLineBreakPosition(Paragraph par, | |||||
int firstElementIndex, int lastElementIndex, | |||||
int insertIndex, double ratio, int indent) { | |||||
// line height calculation | |||||
int halfLeading = (lineHeight - lead - follow) / 2; | |||||
// height above the main baseline | |||||
int lineLead = lead + halfLeading; | |||||
// maximum size of top and bottom alignment | |||||
int lineFollow = follow + halfLeading; | |||||
ListIterator inlineIterator | |||||
= par.listIterator(firstElementIndex); | |||||
for (int j = firstElementIndex; | |||||
j <= lastElementIndex; | |||||
j++) { | |||||
KnuthElement element = (KnuthElement) inlineIterator.next(); | |||||
if (element.isBox()) { | |||||
KnuthInlineBox box = (KnuthInlineBox)element; | |||||
if (box.getLead() > lineLead) { | |||||
lineLead = box.getLead(); | |||||
} | |||||
if (box.getTotal() > lineFollow) { | |||||
lineFollow = box.getTotal(); | |||||
} | |||||
if (box.getMiddle() > lineLead + middleShift) { | |||||
lineLead += box.getMiddle() | |||||
- lineLead - middleShift; | |||||
} | |||||
if (box.getMiddle() > middlefollow - middleShift) { | |||||
middlefollow += box.getMiddle() | |||||
- middlefollow + middleShift; | |||||
} | |||||
} | |||||
} | |||||
if (lineFollow - lineLead > middlefollow) { | |||||
middlefollow = lineFollow - lineLead; | |||||
} | |||||
breakpoints.add(insertIndex, | |||||
new LineBreakPosition(this, | |||||
knuthParagraphs.indexOf(par), | |||||
lastElementIndex , | |||||
ratio, 0, indent, | |||||
lineLead + middlefollow, | |||||
lineLead)); | |||||
}*/ | |||||
/** | /** | ||||
* Phase 2 of Knuth algorithm: find optimal break points. | * Phase 2 of Knuth algorithm: find optimal break points. | ||||
* @param alignment alignment in BP direction of the paragraph | * @param alignment alignment in BP direction of the paragraph | ||||
* @return a list of Knuth elements representing broken lines | * @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 | // find the optimal line breaking points for each paragraph | ||||
ListIterator paragraphsIterator | |||||
= knuthParagraphs.listIterator(knuthParagraphs.size()); | |||||
ListIterator paragraphsIterator = knuthParagraphs.listIterator(knuthParagraphs.size()); | |||||
lineLayoutsList = new ArrayList(knuthParagraphs.size()); | lineLayoutsList = new ArrayList(knuthParagraphs.size()); | ||||
LineLayoutPossibilities llPoss; | LineLayoutPossibilities llPoss; | ||||
while (paragraphsIterator.hasPrevious()) { | while (paragraphsIterator.hasPrevious()) { | ||||
this); | this); | ||||
if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE | if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE | ||||
&& fobj.getWrapOption() != EN_NO_WRAP) { | |||||
&& fobj.getWrapOption() != EN_NO_WRAP && !hyphenationPerformed) { | |||||
hyphenationPerformed = true; | |||||
findHyphenationPoints(currPar); | findHyphenationPoints(currPar); | ||||
} | } | ||||
} else { | } else { | ||||
allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES; | allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES; | ||||
} | } | ||||
alg.setConstantLineWidth(iLineWidth); | |||||
alg.setConstantLineWidth(ipd); | |||||
iBPcount = alg.findBreakingPoints(currPar, | iBPcount = alg.findBreakingPoints(currPar, | ||||
maxAdjustment, false, allowedBreaks); | maxAdjustment, false, allowedBreaks); | ||||
if (iBPcount == 0 || alignment == EN_JUSTIFY) { | if (iBPcount == 0 || alignment == EN_JUSTIFY) { | ||||
alg.resetAlgorithm(); | alg.resetAlgorithm(); | ||||
lineLayouts.savePossibilities(true); | lineLayouts.savePossibilities(true); | ||||
// try with shorter lines | // try with shorter lines | ||||
int savedLineWidth = iLineWidth; | |||||
iLineWidth = (int) (iLineWidth * 0.95); | |||||
int savedLineWidth = ipd; | |||||
ipd = (int) (ipd * 0.95); | |||||
iBPcount = alg.findBreakingPoints(currPar, | iBPcount = alg.findBreakingPoints(currPar, | ||||
maxAdjustment, true, allowedBreaks); | |||||
maxAdjustment, true, allowedBreaks); | |||||
// use normal lines, when possible | // use normal lines, when possible | ||||
lineLayouts.restorePossibilities(); | lineLayouts.restorePossibilities(); | ||||
iLineWidth = savedLineWidth; | |||||
ipd = savedLineWidth; | |||||
} | } | ||||
if (!lineLayouts.canUseLessLines()) { | if (!lineLayouts.canUseLessLines()) { | ||||
alg.resetAlgorithm(); | alg.resetAlgorithm(); | ||||
lineLayouts.savePossibilities(true); | lineLayouts.savePossibilities(true); | ||||
// try with longer lines | // try with longer lines | ||||
int savedLineWidth = iLineWidth; | |||||
iLineWidth = (int) (iLineWidth * 1.05); | |||||
alg.setConstantLineWidth(iLineWidth); | |||||
int savedLineWidth = ipd; | |||||
ipd = (int) (ipd * 1.05); | |||||
alg.setConstantLineWidth(ipd); | |||||
iBPcount = alg.findBreakingPoints(currPar, | iBPcount = alg.findBreakingPoints(currPar, | ||||
maxAdjustment, true, allowedBreaks); | maxAdjustment, true, allowedBreaks); | ||||
// use normal lines, when possible | // use normal lines, when possible | ||||
lineLayouts.restorePossibilities(); | lineLayouts.restorePossibilities(); | ||||
iLineWidth = savedLineWidth; | |||||
ipd = savedLineWidth; | |||||
} | } | ||||
//log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); | //log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); | ||||
//log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); | //log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); | ||||
List returnList = new LinkedList(); | List returnList = new LinkedList(); | ||||
int endIndex = -1; | |||||
for (int p = 0; p < knuthParagraphs.size(); p++) { | for (int p = 0; p < knuthParagraphs.size(); p++) { | ||||
// penalty between paragraphs | // penalty between paragraphs | ||||
if (p > 0) { | if (p > 0) { | ||||
} else { | } else { | ||||
/* "normal" vertical alignment: create a sequence whose boxes | /* "normal" vertical alignment: create a sequence whose boxes | ||||
represent effective lines, and contain LineBreakPositions */ | represent effective lines, and contain LineBreakPositions */ | ||||
Position returnPosition = new LeafPosition(this, p); | |||||
int startIndex = 0; | int startIndex = 0; | ||||
for (int i = 0; | for (int i = 0; | ||||
i < llPoss.getChosenLineCount(); | i < llPoss.getChosenLineCount(); | ||||
// penalty allowing a page break between lines | // penalty allowing a page break between lines | ||||
Keep keep = getKeepTogether(); | Keep keep = getKeepTogether(); | ||||
returnList.add(new BreakElement( | returnList.add(new BreakElement( | ||||
new Position(this), | |||||
new LeafPosition(this, p, endIndex), | |||||
keep.getPenalty(), | keep.getPenalty(), | ||||
keep.getContext(), | keep.getContext(), | ||||
context)); | context)); | ||||
} | } | ||||
int endIndex | |||||
= ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); | |||||
endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); | |||||
// create a list of the FootnoteBodyLM handling footnotes | // create a list of the FootnoteBodyLM handling footnotes | ||||
// whose citations are in this line | // whose citations are in this line | ||||
List footnoteList = new LinkedList(); | List footnoteList = new LinkedList(); | ||||
while (elementIterator.nextIndex() <= endIndex) { | while (elementIterator.nextIndex() <= endIndex) { | ||||
KnuthElement element = (KnuthElement) elementIterator.next(); | KnuthElement element = (KnuthElement) elementIterator.next(); | ||||
if (element instanceof KnuthInlineBox | if (element instanceof KnuthInlineBox | ||||
&& ((KnuthInlineBox) element).isAnchor()) { | |||||
&& ((KnuthInlineBox) element).isAnchor()) { | |||||
footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); | footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); | ||||
} else if (element instanceof KnuthBlockBox) { | } else if (element instanceof KnuthBlockBox) { | ||||
footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); | footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); | ||||
} | } | ||||
} | } | ||||
startIndex = endIndex + 1; | startIndex = endIndex + 1; | ||||
LineBreakPosition lbp | |||||
= (LineBreakPosition) llPoss.getChosenPosition(i); | |||||
LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i); | |||||
returnList.add(new KnuthBlockBox | returnList.add(new KnuthBlockBox | ||||
(lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, | (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, | ||||
footnoteList, lbp, false)); | footnoteList, lbp, false)); | ||||
Position pos = (Position) parentIter.next(); | Position pos = (Position) parentIter.next(); | ||||
boolean isLastPosition = !parentIter.hasNext(); | boolean isLastPosition = !parentIter.hasNext(); | ||||
if (pos instanceof LineBreakPosition) { | if (pos instanceof LineBreakPosition) { | ||||
addInlineArea(context, pos, isLastPosition); | |||||
addInlineArea(context, (LineBreakPosition) pos, isLastPosition); | |||||
} else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) { | } else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) { | ||||
addBlockArea(context, pos, isLastPosition); | addBlockArea(context, pos, isLastPosition); | ||||
} else { | } else { | ||||
* @param pos the position for which the line is generated | * @param pos the position for which the line is generated | ||||
* @param isLastPosition true if this is the last position of this LM | * @param isLastPosition true if this is the last position of this LM | ||||
*/ | */ | ||||
private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) { | |||||
ListIterator seqIterator = null; | |||||
KnuthElement tempElement = null; | |||||
// the TLM which created the last KnuthElement in this line | |||||
LayoutManager lastLM = null; | |||||
LineBreakPosition lbp = (LineBreakPosition) pos; | |||||
int iCurrParIndex; | |||||
iCurrParIndex = lbp.iParIndex; | |||||
KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex); | |||||
int iStartElement = lbp.iStartIndex; | |||||
int iEndElement = lbp.getLeafPos(); | |||||
LineArea lineArea | |||||
= new LineArea((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.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)); | |||||
alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline); | |||||
if (seq instanceof Paragraph) { | |||||
Paragraph currPar = (Paragraph) seq; | |||||
// ignore the first elements added by the LineLayoutManager | |||||
iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0; | |||||
// if this is the last line area that for this paragraph, | |||||
// ignore the last elements added by the LineLayoutManager and | |||||
// subtract the last-line-end-indent from the area ipd | |||||
if (iEndElement == (currPar.size() - 1)) { | |||||
iEndElement -= currPar.ignoreAtEnd; | |||||
lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this)); | |||||
} | |||||
private void addInlineArea(LayoutContext context, LineBreakPosition lbp, | |||||
boolean isLastPosition) { | |||||
// the TLM which created the last KnuthElement in this line | |||||
LayoutManager lastLM = null; | |||||
KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(lbp.parIndex); | |||||
int startElementIndex = lbp.startIndex; | |||||
int endElementIndex = lbp.getLeafPos(); | |||||
LineArea lineArea = new LineArea( | |||||
(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.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)); | |||||
alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline); | |||||
if (seq instanceof Paragraph) { | |||||
Paragraph currPar = (Paragraph) seq; | |||||
// ignore the first elements added by the LineLayoutManager | |||||
startElementIndex += (startElementIndex == 0) ? currPar.ignoreAtStart : 0; | |||||
// if this is the last line area that for this paragraph, | |||||
// ignore the last elements added by the LineLayoutManager and | |||||
// subtract the last-line-end-indent from the area ipd | |||||
if (endElementIndex == (currPar.size() - 1)) { | |||||
endElementIndex -= currPar.ignoreAtEnd; | |||||
lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this)); | |||||
} | } | ||||
} | |||||
// Remove trailing spaces if allowed so | |||||
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED | |||||
// Remove trailing spaces if allowed so | |||||
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED | |||||
|| whiteSpaceTreament == EN_IGNORE | || whiteSpaceTreament == EN_IGNORE | ||||
|| whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) { | || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) { | ||||
// ignore the last element in the line if it is a KnuthGlue object | |||||
seqIterator = seq.listIterator(iEndElement); | |||||
tempElement = (KnuthElement) seqIterator.next(); | |||||
if (tempElement.isGlue()) { | |||||
iEndElement--; | |||||
// this returns the same KnuthElement | |||||
seqIterator.previous(); | |||||
if (seqIterator.hasPrevious()) { | |||||
tempElement = (KnuthElement) seqIterator.previous(); | |||||
} else { | |||||
tempElement = null; | |||||
} | |||||
} | |||||
if (tempElement != null) { | |||||
lastLM = tempElement.getLayoutManager(); | |||||
// ignore the last element in the line if it is a KnuthGlue object | |||||
ListIterator seqIterator = seq.listIterator(endElementIndex); | |||||
KnuthElement lastElement = (KnuthElement) seqIterator.next(); | |||||
lastLM = lastElement.getLayoutManager(); | |||||
if (lastElement.isGlue()) { | |||||
endElementIndex--; | |||||
// this returns the same KnuthElement | |||||
seqIterator.previous(); | |||||
if (seqIterator.hasPrevious()) { | |||||
lastLM = ((KnuthElement) seqIterator.previous()).getLayoutManager(); | |||||
} | } | ||||
} | } | ||||
} | |||||
// Remove leading spaces if allowed so | |||||
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED | |||||
// Remove leading spaces if allowed so | |||||
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED | |||||
|| whiteSpaceTreament == EN_IGNORE | || whiteSpaceTreament == EN_IGNORE | ||||
|| whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) { | || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) { | ||||
// ignore KnuthGlue and KnuthPenalty objects | |||||
// at the beginning of the line | |||||
seqIterator = seq.listIterator(iStartElement); | |||||
tempElement = (KnuthElement) seqIterator.next(); | |||||
while (!tempElement.isBox() && seqIterator.hasNext()) { | |||||
tempElement = (KnuthElement) seqIterator.next(); | |||||
iStartElement++; | |||||
} | |||||
} | |||||
// Add the inline areas to lineArea | |||||
PositionIterator inlinePosIter | |||||
= new KnuthPossPosIter(seq, iStartElement, iEndElement + 1); | |||||
iStartElement = lbp.getLeafPos() + 1; | |||||
if (iStartElement == seq.size()) { | |||||
// advance to next paragraph | |||||
iStartElement = 0; | |||||
// ignore KnuthGlue and KnuthPenalty objects | |||||
// at the beginning of the line | |||||
ListIterator seqIterator = seq.listIterator(startElementIndex); | |||||
while (seqIterator.hasNext() && !((KnuthElement) seqIterator.next()).isBox()) { | |||||
startElementIndex++; | |||||
} | } | ||||
} | |||||
// Add the inline areas to lineArea | |||||
PositionIterator inlinePosIter = new KnuthPossPosIter(seq, startElementIndex, | |||||
endElementIndex + 1); | |||||
LayoutContext lc = new LayoutContext(0); | |||||
lc.setAlignmentContext(alignmentContext); | |||||
lc.setSpaceAdjust(lbp.dAdjust); | |||||
lc.setIPDAdjust(lbp.ipdAdjust); | |||||
lc.setLeadingSpace(new SpaceSpecifier(true)); | |||||
lc.setTrailingSpace(new SpaceSpecifier(false)); | |||||
lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); | |||||
/* | |||||
* extension (not in the XSL FO recommendation): if the left and right margins | |||||
* have been optimized, recompute indents and / or adjust ratio, according | |||||
* to the paragraph horizontal alignment | |||||
*/ | |||||
if (false && textAlignment == EN_JUSTIFY) { | |||||
// re-compute space adjust ratio | |||||
int updatedDifference = context.getStackLimitIP().opt | |||||
- lbp.lineWidth + lbp.difference; | |||||
double updatedRatio = 0.0; | |||||
if (updatedDifference > 0) { | |||||
updatedRatio = (float) updatedDifference / lbp.availableStretch; | |||||
} else if (updatedDifference < 0) { | |||||
updatedRatio = (float) updatedDifference / lbp.availableShrink; | |||||
} | |||||
lc.setIPDAdjust(updatedRatio); | |||||
//log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference); | |||||
//log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio); | |||||
} else if (false && textAlignment == EN_CENTER) { | |||||
// re-compute indent | |||||
int updatedIndent = lbp.startIndent | |||||
+ (context.getStackLimitIP().opt - lbp.lineWidth) / 2; | |||||
lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); | |||||
} else if (false && textAlignment == EN_END) { | |||||
// re-compute indent | |||||
int updatedIndent = lbp.startIndent | |||||
+ (context.getStackLimitIP().opt - lbp.lineWidth); | |||||
lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); | |||||
} | |||||
LayoutContext lc = new LayoutContext(0); | |||||
lc.setAlignmentContext(alignmentContext); | |||||
lc.setSpaceAdjust(lbp.dAdjust); | |||||
lc.setIPDAdjust(lbp.ipdAdjust); | |||||
lc.setLeadingSpace(new SpaceSpecifier(true)); | |||||
lc.setTrailingSpace(new SpaceSpecifier(false)); | |||||
lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); | |||||
setCurrentArea(lineArea); | |||||
setChildContext(lc); | |||||
LayoutManager childLM; | |||||
while ((childLM = inlinePosIter.getNextChildLM()) != null) { | |||||
lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM)); | |||||
childLM.addAreas(inlinePosIter, lc); | |||||
lc.setLeadingSpace(lc.getTrailingSpace()); | |||||
lc.setTrailingSpace(new SpaceSpecifier(false)); | |||||
/* | |||||
* extension (not in the XSL FO recommendation): if the left and right margins | |||||
* have been optimized, recompute indents and / or adjust ratio, according | |||||
* to the paragraph horizontal alignment | |||||
*/ | |||||
if (false && textAlignment == EN_JUSTIFY) { | |||||
// re-compute space adjust ratio | |||||
int updatedDifference = context.getRefIPD() | |||||
- lbp.lineWidth + lbp.difference; | |||||
double updatedRatio = 0.0; | |||||
if (updatedDifference > 0) { | |||||
updatedRatio = (float) updatedDifference / lbp.availableStretch; | |||||
} else if (updatedDifference < 0) { | |||||
updatedRatio = (float) updatedDifference / lbp.availableShrink; | |||||
} | } | ||||
lc.setIPDAdjust(updatedRatio); | |||||
//log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference); | |||||
//log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio); | |||||
} else if (false && textAlignment == EN_CENTER) { | |||||
// re-compute indent | |||||
int updatedIndent = lbp.startIndent | |||||
+ (context.getRefIPD() - lbp.lineWidth) / 2; | |||||
lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); | |||||
} else if (false && textAlignment == EN_END) { | |||||
// re-compute indent | |||||
int updatedIndent = lbp.startIndent | |||||
+ (context.getRefIPD() - lbp.lineWidth); | |||||
lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); | |||||
} | |||||
// when can this be null? | |||||
// if display-align is distribute, add space after | |||||
if (context.getSpaceAfter() > 0 | |||||
&& (!context.isLastArea() || !isLastPosition)) { | |||||
lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); | |||||
} | |||||
lineArea.finalise(); | |||||
parentLM.addChildArea(lineArea); | |||||
setCurrentArea(lineArea); | |||||
setChildContext(lc); | |||||
LayoutManager childLM; | |||||
while ((childLM = inlinePosIter.getNextChildLM()) != null) { | |||||
lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM)); | |||||
childLM.addAreas(inlinePosIter, lc); | |||||
lc.setLeadingSpace(lc.getTrailingSpace()); | |||||
lc.setTrailingSpace(new SpaceSpecifier(false)); | |||||
} | |||||
// if display-align is distribute, add space after | |||||
if (context.getSpaceAfter() > 0 | |||||
&& (!context.isLastArea() || !isLastPosition)) { | |||||
lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); | |||||
} | |||||
lineArea.finalise(); | |||||
parentLM.addChildArea(lineArea); | |||||
} | } | ||||
/** | /** | ||||
// set last area flag | // set last area flag | ||||
blocklc.setFlags(LayoutContext.LAST_AREA, | blocklc.setFlags(LayoutContext.LAST_AREA, | ||||
(context.isLastArea() && childLM == lastLM)); | (context.isLastArea() && childLM == lastLM)); | ||||
blocklc.setStackLimitsFrom(context); | |||||
blocklc.setStackLimitBP(context.getStackLimitBP()); | |||||
// Add the line areas to Area | // Add the line areas to Area | ||||
childLM.addAreas(childPosIter, blocklc); | childLM.addAreas(childPosIter, blocklc); | ||||
blocklc.setLeadingSpace(blocklc.getTrailingSpace()); | blocklc.setLeadingSpace(blocklc.getTrailingSpace()); | ||||
public boolean getGeneratesLineArea() { | public boolean getGeneratesLineArea() { | ||||
return true; | return true; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public boolean isRestartable() { | |||||
return true; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void reset() { | |||||
super.reset(); | |||||
label.reset(); | |||||
body.reset(); | |||||
} | |||||
} | } | ||||
<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en"> | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<catalogue xml:lang="en"> | |||||
<message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message> | <message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message> | ||||
</catalogue> | </catalogue> |
<?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 blocks of texts are re-laid out after a change of the flow ipd. | |||||
</p> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body" language="en" hyphenate="true"> | |||||
<fo:block text-align="justify" id="surrounding" | |||||
space-before.minimum="10pt" | |||||
space-before.optimum="12pt" | |||||
space-before.maximum="50pt"> | |||||
<fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b3" border-top="1pt solid black" | |||||
border-before-width.conditionality="retain">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face. In olden times when wishing still helped one, there lived a king | |||||
whose daughters were all beautiful, but the youngest was so beautiful that the sun | |||||
itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
<fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times | |||||
when wishing still helped one, there lived a king whose daughters were all beautiful, | |||||
but the youngest was so beautiful that the sun itself, which has seen so much, was | |||||
astonished whenever it shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<eval expected="13100" xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/> | |||||
<eval expected="13100" xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[1]//flow/block/block[3]/@border-before"/> | |||||
<eval expected="In" xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/> | |||||
<eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/> | |||||
</checks> | |||||
</testcase> |
<?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 blocks of texts are re-laid out after a change of the flow ipd. | |||||
</p> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body"> | |||||
<fo:block id="b1">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face.</fo:block> | |||||
<fo:block id="b2">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face.</fo:block> | |||||
<fo:block id="b3">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face. In olden times | |||||
when wishing still helped one, there lived a king whose daughters were all beautiful, | |||||
but the youngest was so beautiful that the sun itself, which has seen so much, was | |||||
astonished whenever it shone in her face.</fo:block> | |||||
<fo:block id="b4">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face.</fo:block> | |||||
<fo:block id="b5">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<eval expected="has" xpath="//pageViewport[1]//flow/block[3]/lineArea[3]/text/word[position()=last()]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/@ipd"/> | |||||
<eval expected="seen" xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/text/word[1]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/lineArea[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/lineArea[1]/@ipd"/> | |||||
</checks> | |||||
</testcase> |
<?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 a change of IPD between two blocks is correctly handled. | |||||
</p> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body"> | |||||
<fo:block text-align="justify" id="surrounding" | |||||
space-before.minimum="10pt" | |||||
space-before.optimum="12pt" | |||||
space-before.maximum="50pt"> | |||||
<fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b3">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block border-top="1pt solid black" space-before.minimum="10pt" | |||||
space-before.optimum="12pt" space-before.maximum="50pt" | |||||
space-before.conditionality="retain" id="b4">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<eval expected="face." xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block[1]/@space-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/> | |||||
<eval expected="In" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[2]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/> | |||||
</checks> | |||||
</testcase> |
<?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 non-restartable elements still show up at IPD change, even if not | |||||
re-laid out. | |||||
</p> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body" text-align="justify"> | |||||
<fo:block space-before="10pt" id="b1_1">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
<fo:block space-before="10pt" id="b1_2">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
<fo:table table-layout="fixed" width="100%" border="1pt solid black" space-before="10pt" | |||||
padding="2pt" border-collapse="separate"> | |||||
<fo:table-body> | |||||
<fo:table-row> | |||||
<fo:table-cell> | |||||
<fo:block space-before="10pt" id="b1_3">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was | |||||
so beautiful that the sun itself, which has seen so much, was astonished | |||||
whenever it shone in her face.</fo:block> | |||||
<fo:block space-before="10pt" id="b1_4">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was | |||||
so beautiful that the sun itself, which has seen so much, was astonished | |||||
whenever it shone in her face.</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
</fo:table-body> | |||||
</fo:table> | |||||
<fo:block space-before="10pt" id="b1_5" border-top="1pt solid red">In olden times when | |||||
wishing still helped one, there lived a king whose daughters were all beautiful, but the | |||||
youngest was so beautiful that the sun itself, which has seen so much, was astonished | |||||
whenever it shone in her face.</fo:block> | |||||
<fo:block space-before="10pt" id="b1_6">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body" text-align="justify"> | |||||
<fo:block space-before="10pt" id="b2_1">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
<fo:block space-before="10pt" id="b2_2">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
<fo:list-block space-before="10pt" provisional-distance-between-starts="0.5cm"> | |||||
<fo:list-item> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block start-indent="2pt">•</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block space-before="10pt" id="b2_3">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was | |||||
so beautiful that the sun itself, which has seen so much, was | |||||
astonished…</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
<fo:list-item> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block start-indent="2pt">•</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block id="b2_4">In olden times when wishing still helped one, there lived a king | |||||
whose daughters were all beautiful, but the youngest was so beautiful that the sun | |||||
itself, which has seen so much, was astonished…</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
</fo:list-block> | |||||
<fo:block space-before="10pt" space-before.conditionality="retain" border-top="1pt solid | |||||
red" id="b2_5">In olden times when wishing still helped one, there lived a king whose | |||||
daughters were all beautiful, but the youngest was so beautiful that the sun itself, | |||||
which has seen so much, was astonished whenever it shone in her face.</fo:block> | |||||
<fo:block space-before="10pt" id="b2_6">In olden times when wishing still helped one, there | |||||
lived a king whose daughters were all beautiful, but the youngest was so beautiful that | |||||
the sun itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<!-- First page sequence – table --> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/@border-after"/> | |||||
<eval expected="300000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/@ipd"/> | |||||
<eval expected="b1_4" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/block/block/@prod-id"/> | |||||
<eval expected="In" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[2]"/> | |||||
<eval expected="her" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()-1]"/> | |||||
<eval expected="face." xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="b1_5" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@prod-id"/> | |||||
<eval expected="500000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@ipd"/> | |||||
<eval expected="(solid,#ff0000,1000)" | |||||
xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@border-before"/> | |||||
<eval expected="In" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/> | |||||
<!-- Second page sequence – list --> | |||||
<eval expected="300000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/@ipd"/> | |||||
<eval expected="b2_4" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/@prod-id"/> | |||||
<eval expected="In" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[2]"/> | |||||
<eval expected="was" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()-1]"/> | |||||
<eval expected="astonished…" | |||||
xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="b2_5" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@prod-id"/> | |||||
<eval expected="500000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@ipd"/> | |||||
<eval expected="10000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@space-before"/> | |||||
<eval expected="(solid,#ff0000,1000)" | |||||
xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@border-before"/> | |||||
<eval expected="In" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/> | |||||
</checks> | |||||
</testcase> |
<?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 block-container elements correctly support an IPD change. | |||||
</p> | |||||
<!-- NOTE: This test case is a copy of flow_changing-ipd_1.xml, modified to simply surround | |||||
block 3 with a block-container. --> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body"> | |||||
<fo:block text-align="justify" id="surrounding" | |||||
space-before.minimum="10pt" | |||||
space-before.optimum="12pt" | |||||
space-before.maximum="50pt"> | |||||
<fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block-container space-before="inherit"> | |||||
<fo:block id="b3" border-top="1pt solid black" | |||||
border-before-width.conditionality="retain">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face. In olden times when wishing still helped one, there lived a king | |||||
whose daughters were all beautiful, but the youngest was so beautiful that the sun | |||||
itself, which has seen so much, was astonished whenever it shone in her | |||||
face.</fo:block> | |||||
</fo:block-container> | |||||
<fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times | |||||
when wishing still helped one, there lived a king whose daughters were all beautiful, | |||||
but the youngest was so beautiful that the sun itself, which has seen so much, was | |||||
astonished whenever it shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<eval expected="13100" xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/> | |||||
<eval expected="13100" xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[1]//flow/block/block[3]/block/block/@border-before"/> | |||||
<eval expected="In" xpath="//pageViewport[1]//flow/block/block[3]/block/block/lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block[1]/block/block/@border-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/@ipd"/> | |||||
<eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/text/word[1]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/> | |||||
</checks> | |||||
</testcase> |
<?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 a change of IPD between two blocks surrounded by a block-container is | |||||
correctly handled. | |||||
</p> | |||||
<!-- NOTE: This test case is a copy of flow_changing-ipd_3.xml, with the surrounding block | |||||
replaced with a block-container. --> | |||||
</info> | |||||
<fo> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="narrow" | |||||
page-height="300pt" page-width="400pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:simple-page-master master-name="wide" | |||||
page-height="300pt" page-width="600pt" margin="50pt"> | |||||
<fo:region-body background-color="#F0F0F0"/> | |||||
</fo:simple-page-master> | |||||
<fo:page-sequence-master master-name="pages"> | |||||
<fo:single-page-master-reference master-reference="narrow"/> | |||||
<fo:repeatable-page-master-reference master-reference="wide"/> | |||||
</fo:page-sequence-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="pages"> | |||||
<fo:flow flow-name="xsl-region-body"> | |||||
<fo:block-container text-align="justify" id="surrounding" | |||||
space-before.minimum="10pt" | |||||
space-before.optimum="12pt" | |||||
space-before.maximum="50pt"> | |||||
<fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b3">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block border-top="1pt solid black" space-before.minimum="10pt" | |||||
space-before.optimum="12pt" space-before.maximum="50pt" | |||||
space-before.conditionality="retain" id="b4">In olden times when wishing still helped | |||||
one, there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
<fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, | |||||
there lived a king whose daughters were all beautiful, but the youngest was so | |||||
beautiful that the sun itself, which has seen so much, was astonished whenever it | |||||
shone in her face.</fo:block> | |||||
</fo:block-container> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> | |||||
</fo> | |||||
<checks> | |||||
<eval expected="face." xpath="//pageViewport[1]//flow/block/block/block[3]/lineArea[4]/text/word[position()=last()]"/> | |||||
<eval expected="12000" xpath="//pageViewport[2]//flow/block/block/block[1]/@space-before"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/@ipd"/> | |||||
<eval expected="(solid,#000000,1000)" | |||||
xpath="//pageViewport[2]//flow/block/block/block[1]/@border-before"/> | |||||
<eval expected="In" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[1]"/> | |||||
<eval expected="olden" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[2]"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/@ipd"/> | |||||
<eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/lineArea[1]/@ipd"/> | |||||
</checks> | |||||
</testcase> |