Browse Source

Merged back ChangingIPDHack branch into Trunk


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@808157 13f79535-47bb-0310-9956-ffa450edef68
pull/37/head
Vincent Hennebert 15 years ago
parent
commit
79e38db1f5
32 changed files with 1945 additions and 816 deletions
  1. 20
    0
      src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
  2. 129
    17
      src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
  3. 33
    7
      src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
  4. 5
    0
      src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
  5. 1
    1
      src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
  6. 306
    0
      src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
  7. 25
    11
      src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
  8. 282
    120
      src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
  9. 14
    7
      src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
  10. 133
    86
      src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
  11. 4
    12
      src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
  12. 1
    38
      src/java/org/apache/fop/layoutmgr/LayoutContext.java
  13. 33
    0
      src/java/org/apache/fop/layoutmgr/LayoutManager.java
  14. 8
    3
      src/java/org/apache/fop/layoutmgr/LeafPosition.java
  15. 34
    1
      src/java/org/apache/fop/layoutmgr/PageBreaker.java
  16. 62
    0
      src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
  17. 27
    0
      src/java/org/apache/fop/layoutmgr/PageProvider.java
  18. 5
    0
      src/java/org/apache/fop/layoutmgr/Position.java
  19. 5
    1
      src/java/org/apache/fop/layoutmgr/SpaceResolver.java
  20. 1
    102
      src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
  21. 3
    7
      src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
  22. 0
    10
      src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
  23. 3
    29
      src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
  24. 195
    363
      src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
  25. 7
    0
      src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
  26. 2
    1
      src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
  27. 97
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_1.xml
  28. 75
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_2.xml
  29. 86
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_3.xml
  30. 159
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_4.xml
  31. 101
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml
  32. 89
    0
      test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml

+ 20
- 0
src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java View File



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");
}

} }

+ 129
- 17
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java View File



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);

+ 33
- 7
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java View File

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;
}

} }

+ 5
- 0
src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java View File

} }
} }


/** {@inheritDoc} */
public void reset() {
throw new IllegalStateException();
}

} }

+ 1
- 1
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java View File

// 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);
} }



+ 306
- 0
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java View File

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;



+ 25
- 11
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java View File

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;
}

} }



+ 282
- 120
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java View File

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
}

} }



+ 14
- 7
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java View File

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

+ 133
- 86
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java View File

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;
}

} }



+ 4
- 12
src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java View File

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));

+ 1
- 38
src/java/org/apache/fop/layoutmgr/LayoutContext.java View File

* 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"

+ 33
- 0
src/java/org/apache/fop/layoutmgr/LayoutManager.java View File

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);
} }

+ 8
- 3
src/java/org/apache/fop/layoutmgr/LeafPosition.java View File



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() {

+ 34
- 1
src/java/org/apache/fop/layoutmgr/PageBreaker.java View File

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
*/ */

+ 62
- 0
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java View File

//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);
}

} }

+ 27
- 0
src/java/org/apache/fop/layoutmgr/PageProvider.java View File

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

+ 5
- 0
src/java/org/apache/fop/layoutmgr/Position.java View File

layoutManager = lm; layoutManager = lm;
} }


public Position(LayoutManager lm, int index) {
this(lm);
setIndex(index);
}

public LayoutManager getLM() { public LayoutManager getLM() {
return layoutManager; return layoutManager;
} }

+ 5
- 1
src/java/org/apache/fop/layoutmgr/SpaceResolver.java View File



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;
}

} }


/** /**

+ 1
- 102
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java View File



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();
} }


/** /**

+ 3
- 7
src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java View File



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);
} }



+ 0
- 10
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java View File

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:");

+ 3
- 29
src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java View File



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

+ 195
- 363
src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java View File

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;
}

} }



+ 7
- 0
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java View File

} }
} }


/** {@inheritDoc} */
public void reset() {
super.reset();
label.reset();
body.reset();
}



} }



+ 2
- 1
src/java/org/apache/fop/render/pdf/PDFEventProducer.xml View File

<?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>

+ 97
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_1.xml View File

<?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>

+ 75
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_2.xml View File

<?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>

+ 86
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_3.xml View File

<?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>

+ 159
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_4.xml View File

<?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>

+ 101
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml View File

<?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>

+ 89
- 0
test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml View File

<?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>

Loading…
Cancel
Save