diff options
author | Jeremias Maerki <jeremias@apache.org> | 2005-05-13 19:16:54 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2005-05-13 19:16:54 +0000 |
commit | e835307c9373bed3a8f41bb43e4faee15fdaf548 (patch) | |
tree | 04b42bcff456d9216e96b4cfcb572c59baba76e1 /src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java | |
parent | da85c0b44d79ca790b51fcc0e2700c30e72e9260 (diff) | |
download | xmlgraphics-fop-e835307c9373bed3a8f41bb43e4faee15fdaf548.tar.gz xmlgraphics-fop-e835307c9373bed3a8f41bb43e4faee15fdaf548.zip |
Merge of branch Temp_KnuthStylePageBreaking back into HEAD.
Temp_KnuthStylePageBreaking branch and HEAD have been tagged prior to the merge, so merging uncommitted work from the branch should be easier.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198627 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java')
-rw-r--r-- | src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java | 1077 |
1 files changed, 1076 insertions, 1 deletions
diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 582c5c285..ca9c8caad 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -18,17 +18,28 @@ package org.apache.fop.layoutmgr; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + import org.apache.fop.area.Area; import org.apache.fop.area.BlockParent; import org.apache.fop.area.Block; +import org.apache.fop.datatypes.PercentBase; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonMarginBlock; +import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.traits.MinOptMax; /** * Base LayoutManager class for all areas which stack their child * areas in the block-progression direction, such as Flow, Block, ListBlock. */ -public abstract class BlockStackingLayoutManager extends AbstractLayoutManager { +public abstract class BlockStackingLayoutManager extends AbstractLayoutManager + implements BlockLevelLayoutManager { /** * Reference to FO whose areas it's managing or to the traits * of the FO. @@ -36,8 +47,24 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager { //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this! protected BlockParent parentArea = null; + /*LF*/ + /** Value of the block-progression-unit (non-standard property) */ + protected int bpUnit = 0; + /** space-before value adjusted for block-progression-unit handling */ + protected int adjustedSpaceBefore = 0; + /** space-after value adjusted for block-progression-unit handling */ + protected int adjustedSpaceAfter = 0; + /** Only used to store the original list when createUnitElements is called */ + protected LinkedList storedList = null; + protected FObj fobj; + private boolean bBreakBeforeServed = false; + private boolean bSpaceBeforeServed = false; + protected int referenceIPD = 0; + /*LF*/ + public BlockStackingLayoutManager(FObj node) { super(node); + fobj = node; } private BreakCost evaluateBreakCost(Area parent, Area child) { @@ -135,5 +162,1053 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager { } } + /** + * @param len length in millipoints to span with bp units + * @return the minimum integer n such that n * bpUnit >= len + */ + protected int neededUnits(int len) { + return (int) Math.ceil((float)len / bpUnit); + } + + public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { + /* LF *///System.err.println("BLM.getNextKnuthElements> keep-together = " + // + layoutProps.keepTogether.getType()); + /* LF *///System.err.println(" keep-with-previous = " + + // layoutProps.keepWithPrevious.getType()); + /* LF *///System.err.println(" keep-with-next = " + + // layoutProps.keepWithNext.getType()); + BlockLevelLayoutManager curLM; // currently active LM + BlockLevelLayoutManager prevLM = null; // previously active LM + + referenceIPD = context.getRefIPD(); + int iIndents = 0; + int bIndents = 0; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + iIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().startIndent.getValue() + + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().endIndent.getValue(); + bIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground().getBPPaddingAndBorder(false); + } + int ipd = referenceIPD - iIndents; + + MinOptMax stackSize = new MinOptMax(); + + // Set context for percentage property values. + fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd); + fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1); + + LinkedList returnedList = null; + LinkedList contentList = new LinkedList(); + LinkedList returnList = new LinkedList(); + Position returnPosition = new NonLeafPosition(this, null); + + if (!bBreakBeforeServed) { + try { + if (addKnuthElementsForBreakBefore(returnList, returnPosition)) { + return returnList; + } + } finally { + bBreakBeforeServed = true; + } + } + + if (!bSpaceBeforeServed) { + addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment); + bSpaceBeforeServed = true; + } + + addKnuthElementsForBorderPaddingBefore(returnList, returnPosition); + + while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { + LayoutContext childLC = new LayoutContext(0); + if (curLM instanceof LineLayoutManager) { + // curLM is a LineLayoutManager + // set stackLimit for lines + childLC.setStackLimit(new MinOptMax(ipd/* + * - iIndents - + * iTextIndent + */)); + childLC.setRefIPD(ipd); + } else { + // curLM is a ? + childLC.setStackLimit(MinOptMax.subtract(context + .getStackLimit(), stackSize)); + childLC.setRefIPD(referenceIPD); + } + + // get elements from curLM + returnedList = curLM.getNextKnuthElements(childLC, alignment); + if (returnedList.size() == 1 + && ((KnuthElement) returnedList.getFirst()).isPenalty() + && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) { + // 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); + + /* extension: conversione di tutta la sequenza fin'ora ottenuta */ + if (bpUnit > 0) { + storedList = contentList; + contentList = createUnitElements(contentList); + } + /* end of extension */ + + // "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 + if (mustKeepTogether() + || prevLM.mustKeepWithNext() + || curLM.mustKeepWithPrevious()) { + // add an infinite penalty to forbid a break between + // blocks + contentList.add(new KnuthPenalty(0, + KnuthElement.INFINITE, false, + new Position(this), false)); + } else if (!((KnuthElement) contentList.getLast()).isGlue()) { + // add a null penalty to allow a break between blocks + contentList.add(new KnuthPenalty(0, 0, false, + new Position(this), false)); + } else { + // the last element in contentList is a glue; + // it is a feasible breakpoint, there is no need to add + // a penalty + } + } + if (returnedList.size() == 0) { + //Avoid NoSuchElementException below (happens with empty blocks) + continue; + } + contentList.addAll(returnedList); + if (((KnuthElement) returnedList.getLast()).isPenalty() + && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) { + // 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); + } + + /* extension: conversione di tutta la sequenza fin'ora ottenuta */ + if (bpUnit > 0) { + storedList = contentList; + contentList = createUnitElements(contentList); + } + /* end of extension */ + + returnedList = new LinkedList(); + wrapPositionElements(contentList, returnList); + + return returnList; + } + /* + if (allocatedSpace.min > context.getStackLimit().max) { + log.debug("Allocated space exceeds stack limit, returning early."); + return returnList; + }*/ + } + 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(); + wrapPositionElements(contentList, returnList); + + addKnuthElementsForBorderPaddingAfter(returnList, returnPosition); + addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment); + addKnuthElementsForBreakAfter(returnList, returnPosition); + + setFinished(true); + + return returnList; + } + + public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> " + adj); +/*LF*/ //System.out.println(" lastElement e' " + (lastElement.isPenalty() ? "penalty" : (lastElement.isGlue() ? "glue" : "box" ))); +/*LF*/ //System.out.println(" position e' " + lastElement.getPosition().getClass().getName()); +/*LF*/ //System.out.println(" " + (bpUnit > 0 ? "unit" : "")); + Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition(); + + if (innerPosition == null && lastElement.isGlue()) { + // this adjustment applies to space-before or space-after of this block + if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) { + // this adjustment applies to space-before + adjustedSpaceBefore += adj; +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> spazio prima: " + adj); + } else { + // this adjustment applies to space-after + adjustedSpaceAfter += adj; +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> spazio dopo: " + adj); + } + return adj; + } else if (innerPosition instanceof MappingPosition) { + // this block has block-progression-unit > 0: the adjustment can concern + // - the space-before or space-after of this block, + // - the line number of a descendant of this block + if (lastElement.isGlue()) { + // lastElement is a glue +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> bpunit con glue"); + ListIterator storedListIterator = storedList.listIterator(((MappingPosition) innerPosition).getFirstIndex()); + int newAdjustment = 0; + while (storedListIterator.nextIndex() <= ((MappingPosition) innerPosition).getLastIndex()) { + KnuthElement storedElement = (KnuthElement) storedListIterator.next(); + if (storedElement.isGlue()) { + newAdjustment += ((BlockLevelLayoutManager) storedElement.getLayoutManager()).negotiateBPDAdjustment(adj - newAdjustment, storedElement); +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> (progressivo) righe: " + newAdjustment); + } + } + newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment) + : - bpUnit * neededUnits(- newAdjustment)); + return newAdjustment; + } else { + // lastElement is a penalty: this means that the paragraph + // has been split between consecutive pages: + // this may involve a change in the number of lines +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> bpunit con penalty"); + KnuthPenalty storedPenalty = (KnuthPenalty) + storedList.get(((MappingPosition) innerPosition).getLastIndex()); + if (storedPenalty.getW() > 0) { + // the original penalty has width > 0 +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> chiamata passata"); + return ((BlockLevelLayoutManager) storedPenalty.getLayoutManager()) + .negotiateBPDAdjustment(storedPenalty.getW(), (KnuthElement) storedPenalty); + } else { + // the original penalty has width = 0 + // the adjustment involves only the spaces before and after +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> chiamata gestita"); + return adj; + } + } + } else if (innerPosition.getLM() != this) { + // this adjustment concerns another LM + NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition(); + lastElement.setPosition(innerPosition); + int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement); + lastElement.setPosition(savedPos); +/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> righe: " + returnValue); + return returnValue; + } else { + // this should never happen + System.err.println("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position"); + return 0; + } + } + + public void discardSpace(KnuthGlue spaceGlue) { +/*LF*/ //System.out.println(" BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName()); + Position innerPosition = ((NonLeafPosition) spaceGlue.getPosition()).getPosition(); + +/*LF*/ if (innerPosition == null || innerPosition.getLM() == this) { + // if this block has block-progression-unit > 0, innerPosition can be + // a MappingPosition + // spaceGlue represents space before or space after of this block + if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) { + // space-before must be discarded + adjustedSpaceBefore = 0; + } else { + // space-after must be discarded + adjustedSpaceAfter = 0; + //TODO Why are both cases handled in the same way? + } +/*LF*/ } else { + // this element was not created by this BlockLM + NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition(); + spaceGlue.setPosition(innerPosition); + ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue); + spaceGlue.setPosition(savedPos); + } + } + + public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) { +/*LF*/ //System.out.println(""); +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> inizio: oldList.size() = " + oldList.size()); + ListIterator oldListIterator = oldList.listIterator(); + KnuthElement returnedElement; + KnuthElement currElement = null; + KnuthElement prevElement = null; + LinkedList returnedList = new LinkedList(); + LinkedList returnList = new LinkedList(); + int fromIndex = 0; + + // "unwrap" the Positions stored in the elements + KnuthElement oldElement = null; + while (oldListIterator.hasNext()) { + oldElement = (KnuthElement)oldListIterator.next(); + Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition(); +/*LF*/ //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box " : (oldElement.isGlue() ? "glue " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName()); +/*LF*/ //System.out.println(" BLM> unwrapping: " + oldElement.getPosition().getClass().getName()); + if (innerPosition != null) { + // oldElement was created by a descendant of this BlockLM + oldElement.setPosition(innerPosition); + } else { + // thisElement was created by this BlockLM + // modify its position in order to recognize it was not created + // by a child + oldElement.setPosition(new Position(this)); + } + } + + // create the iterator + List workList; + if (bpUnit == 0) { + workList = oldList; + } else { + // the storedList must be used instead of oldList; + // find the index of the first element of returnedList + // corresponding to the first element of oldList + oldListIterator = oldList.listIterator(); + KnuthElement el = (KnuthElement) oldListIterator.next(); + while (!(el.getPosition() instanceof MappingPosition)) { + el = (KnuthElement) oldListIterator.next(); + } + int iFirst = ((MappingPosition) el.getPosition()).getFirstIndex(); + + // find the index of the last element of returnedList + // corresponding to the last element of oldList + oldListIterator = oldList.listIterator(oldList.size()); + el = (KnuthElement) oldListIterator.previous(); + while (!(el.getPosition() instanceof MappingPosition)) { + el = (KnuthElement) oldListIterator.previous(); + } + int iLast = ((MappingPosition) el.getPosition()).getLastIndex(); + +/*LF*/ //System.out.println(" si usa storedList da " + iFirst + " a " + iLast + " compresi su " + storedList.size() + " elementi totali"); + workList = storedList.subList(iFirst, iLast + 1); + } + ListIterator workListIterator = workList.listIterator(); + +/*LF*/ //System.out.println(""); +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> workList.size() = " + workList.size() + " da 0 a " + (workList.size() - 1)); + + while (workListIterator.hasNext()) { + currElement = (KnuthElement) workListIterator.next(); +/*LF*/ //System.out.println("elemento n. " + workListIterator.previousIndex() + " nella workList"); + if (prevElement != null + && prevElement.getLayoutManager() != currElement.getLayoutManager()) { + // prevElement is the last element generated by the same LM + BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager) + prevElement.getLayoutManager(); + BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) + currElement.getLayoutManager(); + boolean bSomethingAdded = false; + if (prevLM != this) { +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName()); + returnedList.addAll(prevLM.getChangedKnuthElements(workList.subList(fromIndex, workListIterator.previousIndex()), + /*flaggedPenalty,*/ alignment)); + bSomethingAdded = true; + } else { + // prevLM == this + // do nothing +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName()); + } + fromIndex = workListIterator.previousIndex(); + + // there is another block after this one + if (bSomethingAdded + && (this.mustKeepTogether() + || prevLM.mustKeepWithNext() + || currLM.mustKeepWithPrevious())) { + // add an infinite penalty to forbid a break between blocks + returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false)); + } else if (bSomethingAdded && !((KnuthElement) returnedList.getLast()).isGlue()) { + // add a null penalty to allow a break between blocks + returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); + } + } + prevElement = currElement; + } + if (currElement != null) { + BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) + currElement.getLayoutManager(); + if (currLM != this) { +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size() + " su " + currLM.getClass().getName()); + returnedList.addAll(currLM.getChangedKnuthElements(workList.subList(fromIndex, workList.size()), + /*flaggedPenalty,*/ alignment)); + } else { + // currLM == this + // there are no more elements to add + // remove the last penalty added to returnedList + if (returnedList.size() > 0) { + returnedList.removeLast(); + } +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workList.size()); + } + } + + // append elements representing space-before + boolean spaceBeforeIsConditional = true; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + spaceBeforeIsConditional = + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard(); + } + if (bpUnit > 0 + || adjustedSpaceBefore != 0) { + if (!spaceBeforeIsConditional) { + // add elements to prevent the glue to be discarded + returnList.add(new KnuthBox(0, + new NonLeafPosition(this, null), false)); + returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, + new NonLeafPosition(this, null), false)); + } + if (bpUnit > 0) { + returnList.add(new KnuthGlue(0, 0, 0, + SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true)); + } else { + returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0, + SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true)); + } + } + +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> intermedio: returnedList.size() = " + returnedList.size()); + +/* estensione: conversione complessiva */ +/*LF*/ if (bpUnit > 0) { +/*LF*/ storedList = returnedList; +/*LF*/ returnedList = createUnitElements(returnedList); +/*LF*/ } +/* estensione */ + + // "wrap" the Position stored in each element of returnedList + // and add elements to returnList + ListIterator listIter = returnedList.listIterator(); + while (listIter.hasNext()) { + returnedElement = (KnuthElement)listIter.next(); + returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition())); + returnList.add(returnedElement); + } + + // append elements representing space-after + boolean spaceAfterIsConditional = true; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + spaceAfterIsConditional = + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard(); + } + if (bpUnit > 0 + || adjustedSpaceAfter != 0) { + if (!spaceAfterIsConditional) { + returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, + new NonLeafPosition(this, null), false)); + } + if (bpUnit > 0) { + returnList.add(new KnuthGlue(0, 0, 0, + SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null), + (!spaceAfterIsConditional) ? false : true)); + } else { + returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0, + SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null), + (!spaceAfterIsConditional) ? false : true)); + } + if (!spaceAfterIsConditional) { + returnList.add(new KnuthBox(0, + new NonLeafPosition(this, null), true)); + } + } + +/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> fine: returnList.size() = " + returnList.size()); + return returnList; + } + + /** + * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() + */ + public boolean mustKeepTogether() { + return false; + } + + /** + * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() + */ + public boolean mustKeepWithPrevious() { + return false; + } + + /** + * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() + */ + public boolean mustKeepWithNext() { + return false; + } + + /** + * Creates Knuth elements for before border padding and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + */ + protected void addKnuthElementsForBorderPaddingBefore(LinkedList returnList, + Position returnPosition) { + //Border and Padding (before) + CommonBorderPaddingBackground borderAndPadding = null; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + borderAndPadding = + ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground(); + } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { + borderAndPadding = + ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground(); + } + if (borderAndPadding != null) { + //TODO Handle conditionality + int bpBefore = borderAndPadding.getBorderBeforeWidth(false) + + borderAndPadding.getPaddingBefore(false); + if (bpBefore > 0) { + returnList.add(new KnuthBox(bpBefore, returnPosition, true)); + } + } + } + + /** + * Creates Knuth elements for after border padding and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + */ + protected void addKnuthElementsForBorderPaddingAfter(LinkedList returnList, + Position returnPosition) { + //Border and Padding (after) + CommonBorderPaddingBackground borderAndPadding = null; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + borderAndPadding = + ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground(); + } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { + borderAndPadding = + ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground(); + } + if (borderAndPadding != null) { + //TODO Handle conditionality + int bpAfter = borderAndPadding.getBorderAfterWidth(false) + + borderAndPadding.getPaddingAfter(false); + if (bpAfter > 0) { + returnList.add(new KnuthBox(bpAfter, returnPosition, true)); + } + } + } + + /** + * Creates Knuth elements for break-before and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + * @return true if an element has been added due to a break-before. + */ + protected boolean addKnuthElementsForBreakBefore(LinkedList returnList, + Position returnPosition) { + int breakBefore = -1; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + breakBefore = ((org.apache.fop.fo.flow.Block) fobj).getBreakBefore(); + } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { + breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakBefore(); + } + if (breakBefore == EN_PAGE + || breakBefore == EN_COLUMN + || breakBefore == EN_EVEN_PAGE + || breakBefore == EN_ODD_PAGE) { + // return a penalty element, representing a forced page break + returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, + breakBefore, returnPosition, false)); + return true; + } else { + return false; + } + } + + /** + * Creates Knuth elements for break-after and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + * @return true if an element has been added due to a break-after. + */ + protected boolean addKnuthElementsForBreakAfter(LinkedList returnList, + Position returnPosition) { + int breakAfter = -1; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + breakAfter = ((org.apache.fop.fo.flow.Block) fobj).getBreakAfter(); + } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { + breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakAfter(); + } + if (breakAfter == EN_PAGE + || breakAfter == EN_COLUMN + || breakAfter == EN_EVEN_PAGE + || breakAfter == EN_ODD_PAGE) { + // add a penalty element, representing a forced page break + returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, + breakAfter, returnPosition, false)); + return true; + } else { + return false; + } + } + + /** + * Creates Knuth elements for space-before and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + * @param alignment vertical alignment + */ + protected void addKnuthElementsForSpaceBefore(LinkedList returnList, + Position returnPosition, int alignment) { + SpaceProperty spaceBefore = null; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + spaceBefore = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore; + } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) { + spaceBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceBefore; + } + // append elements representing space-before + if (bpUnit > 0 + || spaceBefore != null + && !(spaceBefore.getMinimum().getLength().getValue() == 0 + && spaceBefore.getMaximum().getLength().getValue() == 0)) { + if (spaceBefore != null && !spaceBefore.getSpace().isDiscard()) { + // add elements to prevent the glue to be discarded + returnList.add(new KnuthBox(0, returnPosition, false)); + returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, + false, returnPosition, false)); + } + if (bpUnit > 0) { + returnList.add(new KnuthGlue(0, 0, 0, + BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, + returnPosition, true)); + } else /*if (alignment == EN_JUSTIFY)*/ { + returnList.add(new KnuthGlue( + spaceBefore.getOptimum().getLength().getValue(), + spaceBefore.getMaximum().getLength().getValue() + - spaceBefore.getOptimum().getLength().getValue(), + spaceBefore.getOptimum().getLength().getValue() + - spaceBefore.getMinimum().getLength().getValue(), + BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, + returnPosition, true)); + } /*else { + returnList.add(new KnuthGlue( + spaceBefore.getOptimum().getLength().getValue(), + 0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, + returnPosition, true)); + }*/ + } + } + + /** + * Creates Knuth elements for space-after and adds them to the return list. + * @param returnList return list to add the additional elements to + * @param returnPosition applicable return position + * @param alignment vertical alignment + */ + protected void addKnuthElementsForSpaceAfter(LinkedList returnList, Position returnPosition, + int alignment) { + SpaceProperty spaceAfter = null; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + spaceAfter = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter; + } else if (fobj instanceof org.apache.fop.fo.flow.Block) { + spaceAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceAfter; + } + // append elements representing space-after + if (bpUnit > 0 + || spaceAfter != null + && !(spaceAfter.getMinimum().getLength().getValue() == 0 + && spaceAfter.getMaximum().getLength().getValue() == 0)) { + if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) { + returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, + false, returnPosition, false)); + } + if (bpUnit > 0) { + returnList.add(new KnuthGlue(0, 0, 0, + BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, + returnPosition, true)); + } else /*if (alignment == EN_JUSTIFY)*/ { + returnList.add(new KnuthGlue( + spaceAfter.getOptimum().getLength().getValue(), + spaceAfter.getMaximum().getLength().getValue() + - spaceAfter.getOptimum().getLength().getValue(), + spaceAfter.getOptimum().getLength().getValue() + - spaceAfter.getMinimum().getLength().getValue(), + BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition, + (!spaceAfter.getSpace().isDiscard()) ? false : true)); + } /*else { + returnList.add(new KnuthGlue( + spaceAfter.getOptimum().getLength().getValue(), 0, 0, + BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition, + (!spaceAfter.getSpace().isDiscard()) ? false : true)); + }*/ + if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) { + returnList.add(new KnuthBox(0, returnPosition, true)); + } + } + } + + protected LinkedList createUnitElements(LinkedList oldList) { + //System.out.println(" "); + //System.out.println("Inizio conversione: " + oldList.size() + " elementi, spazio minimo prima= " + layoutProps.spaceBefore.getSpace().min + // + " spazio minimo dopo= " + layoutProps.spaceAfter.getSpace().min); + // add elements at the beginning and at the end of oldList + // representing minimum spaces + LayoutManager lm = ((KnuthElement)oldList.getFirst()).getLayoutManager(); + boolean bAddedBoxBefore = false; + boolean bAddedBoxAfter = false; + if (adjustedSpaceBefore > 0) { + oldList.addFirst(new KnuthBox(adjustedSpaceBefore, + new Position(lm), true)); + bAddedBoxBefore = true; + } + if (adjustedSpaceAfter > 0) { + oldList.addLast(new KnuthBox(adjustedSpaceAfter, + new Position(lm), true)); + bAddedBoxAfter = true; + } + + MinOptMax totalLength = new MinOptMax(0); + MinOptMax totalUnits = new MinOptMax(0); + LinkedList newList = new LinkedList(); + + //System.out.println(" "); + //System.out.println(" Prima scansione"); + // scan the list once to compute total min, opt and max length + ListIterator oldListIterator = oldList.listIterator(); + while (oldListIterator.hasNext()) { + KnuthElement element = (KnuthElement) oldListIterator.next(); + if (element.isBox()) { +/*LF*/ totalLength.add(new MinOptMax(element.getW())); +/*LF*/ //System.out.println("box " + element.getW()); +/*LF*/ } else if (element.isGlue()) { +/*LF*/ totalLength.min -= ((KnuthGlue) element).getZ(); +/*LF*/ totalLength.max += ((KnuthGlue) element).getY(); +/*LF*/ //leafValue = ((LeafPosition) element.getPosition()).getLeafPos(); +/*LF*/ //System.out.println("glue " + element.getW() + " + " + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ()); +/*LF*/ } else { +/*LF*/ //System.out.println((((KnuthPenalty)element).getP() == KnuthElement.INFINITE ? "PENALTY " : "penalty ") + element.getW()); + } + } + // compute the total amount of "units" + totalUnits = new MinOptMax(neededUnits(totalLength.min), + neededUnits(totalLength.opt), + neededUnits(totalLength.max)); + //System.out.println(" totalLength= " + totalLength); + //System.out.println(" unita'= " + totalUnits); + + //System.out.println(" "); + //System.out.println(" Seconda scansione"); + // scan the list once more, stopping at every breaking point + // in order to compute partial min, opt and max length + // and create the new elements + oldListIterator = oldList.listIterator(); + boolean bPrevIsBox = false; + MinOptMax lengthBeforeBreak = new MinOptMax(0); + MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone(); + MinOptMax unitsBeforeBreak; + MinOptMax unitsAfterBreak; + MinOptMax unsuppressibleUnits = new MinOptMax(0); + int firstIndex = 0; + int lastIndex = -1; + while (oldListIterator.hasNext()) { + KnuthElement element = (KnuthElement) oldListIterator.next(); + lastIndex ++; + if (element.isBox()) { + lengthBeforeBreak.add(new MinOptMax(element.getW())); + lengthAfterBreak.subtract(new MinOptMax(element.getW())); + bPrevIsBox = true; + } else if (element.isGlue()) { + lengthBeforeBreak.min -= ((KnuthGlue) element).getZ(); + lengthAfterBreak.min += ((KnuthGlue) element).getZ(); + lengthBeforeBreak.max += ((KnuthGlue) element).getY(); + lengthAfterBreak.max -= ((KnuthGlue) element).getY(); + bPrevIsBox = false; + } else { + lengthBeforeBreak.add(new MinOptMax(element.getW())); + bPrevIsBox = false; + } + + // create the new elements + if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE + || element.isGlue() && bPrevIsBox + || !oldListIterator.hasNext()) { + // suppress elements after the breaking point + int iStepsForward = 0; + while (oldListIterator.hasNext()) { + KnuthElement el = (KnuthElement) oldListIterator.next(); + iStepsForward++; + if (el.isGlue()) { + // suppressed glue + lengthAfterBreak.min += ((KnuthGlue) el).getZ(); + lengthAfterBreak.max -= ((KnuthGlue) el).getY(); + } else if (el.isPenalty()) { + // suppressed penalty, do nothing + } else { + // box, end of suppressions + break; + } + } + // compute the partial amount of "units" before and after the break + unitsBeforeBreak = new MinOptMax(neededUnits(lengthBeforeBreak.min), + neededUnits(lengthBeforeBreak.opt), + neededUnits(lengthBeforeBreak.max)); + unitsAfterBreak = new MinOptMax(neededUnits(lengthAfterBreak.min), + neededUnits(lengthAfterBreak.opt), + neededUnits(lengthAfterBreak.max)); + + // rewind the iterator and lengthAfterBreak + for (int i = 0; i < iStepsForward; i++) { + KnuthElement el = (KnuthElement) oldListIterator.previous(); + if (el.isGlue()) { + lengthAfterBreak.min -= ((KnuthGlue) el).getZ(); + lengthAfterBreak.max += ((KnuthGlue) el).getY(); + } + } + + // compute changes in length, stretch and shrink + int uLengthChange = unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt; + int uStretchChange = (unitsBeforeBreak.max + unitsAfterBreak.max - totalUnits.max) + - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt); + int uShrinkChange = (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt) + - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min); + + // compute the number of normal, stretch and shrink unit + // that must be added to the new sequence + int uNewNormal = unitsBeforeBreak.opt - unsuppressibleUnits.opt; + int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt) + - (unsuppressibleUnits.max - unsuppressibleUnits.opt); + int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min) + - (unsuppressibleUnits.opt - unsuppressibleUnits.min); + +/*LF*/ //System.out.println("(" + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-" + unsuppressibleUnits.max + ") " +/*LF*/ // + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-" + unitsBeforeBreak.max +/*LF*/ // + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-" + unitsAfterBreak.max +/*LF*/ // + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "") +/*LF*/ // + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "") +/*LF*/ // + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : "") +/*LF*/ // ); + + // create the MappingPosition which will be stored in the new elements + // correct firstIndex and lastIndex + int firstIndexCorrection = 0; + int lastIndexCorrection = 0; + if (bAddedBoxBefore) { + if (firstIndex != 0) { + firstIndexCorrection ++; + } + lastIndexCorrection ++; + } + if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) { + lastIndexCorrection ++; + } + MappingPosition mappingPos = new MappingPosition(this, + firstIndex - firstIndexCorrection, + lastIndex - lastIndexCorrection); + + // new box + newList.add(new KnuthBox((uNewNormal - uLengthChange) * bpUnit, + mappingPos, + false)); + unsuppressibleUnits.add(new MinOptMax(uNewNormal - uLengthChange)); + //System.out.println(" box " + (uNewNormal - uLengthChange)); + + // new infinite penalty, glue and box, if necessary + if (uNewStretch - uStretchChange > 0 + || uNewShrink - uShrinkChange > 0) { + int iStretchUnits = (uNewStretch - uStretchChange > 0 ? (uNewStretch - uStretchChange) : 0); + int iShrinkUnits = (uNewShrink - uShrinkChange > 0 ? (uNewShrink - uShrinkChange) : 0); + newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, + mappingPos, + false)); + newList.add(new KnuthGlue(0, + iStretchUnits * bpUnit, + iShrinkUnits * bpUnit, + LINE_NUMBER_ADJUSTMENT, + mappingPos, + false)); + //System.out.println(" PENALTY"); + //System.out.println(" glue 0 " + iStretchUnits + " " + iShrinkUnits); + unsuppressibleUnits.max += iStretchUnits; + unsuppressibleUnits.min -= iShrinkUnits; + if (!oldListIterator.hasNext()) { + newList.add(new KnuthBox(0, + mappingPos, + false)); + //System.out.println(" box 0"); + } + } + + // new breaking sequence + if (uStretchChange != 0 + || uShrinkChange != 0) { + // new infinite penalty, glue, penalty and glue + newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, + mappingPos, + false)); + newList.add(new KnuthGlue(0, + uStretchChange * bpUnit, + uShrinkChange * bpUnit, + LINE_NUMBER_ADJUSTMENT, + mappingPos, + false)); + newList.add(new KnuthPenalty(uLengthChange * bpUnit, + 0, false, element.getPosition(), false)); + newList.add(new KnuthGlue(0, + - uStretchChange * bpUnit, + - uShrinkChange * bpUnit, + LINE_NUMBER_ADJUSTMENT, + mappingPos, + false)); + //System.out.println(" PENALTY"); + //System.out.println(" glue 0 " + uStretchChange + " " + uShrinkChange); + //System.out.println(" penalty " + uLengthChange + " * unit"); + //System.out.println(" glue 0 " + (- uStretchChange) + " " + (- uShrinkChange)); + } else if (oldListIterator.hasNext()){ + // new penalty + newList.add(new KnuthPenalty(uLengthChange * bpUnit, + 0, false, + mappingPos, + false)); + //System.out.println(" penalty " + uLengthChange + " * unit"); + } + // update firstIndex + firstIndex = lastIndex + 1; + } + + if (element.isPenalty()) { + lengthBeforeBreak.add(new MinOptMax(-element.getW())); + } + + } + + // remove elements at the beginning and at the end of oldList + // representing minimum spaces + if (adjustedSpaceBefore > 0) { + oldList.removeFirst(); + } + if (adjustedSpaceAfter > 0) { + oldList.removeLast(); + } + + // if space-before.conditionality is "discard", correct newList + boolean correctFirstElement = false; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + correctFirstElement = + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard(); + } + if (correctFirstElement) { + // remove the wrong element + KnuthBox wrongBox = (KnuthBox) newList.removeFirst(); + // if this paragraph is at the top of a page, the space before + // must be ignored; compute the length change + int decreasedLength = (neededUnits(totalLength.opt) + - neededUnits(totalLength.opt - adjustedSpaceBefore)) + * bpUnit; + // insert the correct elements + newList.addFirst(new KnuthBox(wrongBox.getW() - decreasedLength, + wrongBox.getPosition(), false)); + newList.addFirst(new KnuthGlue(decreasedLength, 0, 0, SPACE_BEFORE_ADJUSTMENT, + wrongBox.getPosition(), false)); + //System.out.println(" rimosso box " + neededUnits(wrongBox.getW())); + //System.out.println(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0"); + //System.out.println(" aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength)); + } + + // if space-after.conditionality is "discard", correct newList + boolean correctLastElement = false; + if (fobj instanceof org.apache.fop.fo.flow.Block) { + correctLastElement = + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard(); + } + if (correctLastElement) { + // remove the wrong element + KnuthBox wrongBox = (KnuthBox) newList.removeLast(); + // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0) + // (it cannot be parted and has some stretch or shrink) + // the wrong box is the first one, not the last one + LinkedList preserveList = new LinkedList(); + if (wrongBox.getW() == 0) { + preserveList.add(wrongBox); + preserveList.addFirst((KnuthGlue) newList.removeLast()); + preserveList.addFirst((KnuthPenalty) newList.removeLast()); + wrongBox = (KnuthBox) newList.removeLast(); + } + + // if this paragraph is at the bottom of a page, the space after + // must be ignored; compute the length change + int decreasedLength = (neededUnits(totalLength.opt) + - neededUnits(totalLength.opt - adjustedSpaceAfter)) + * bpUnit; + // insert the correct box + newList.addLast(new KnuthBox(wrongBox.getW() - decreasedLength, + wrongBox.getPosition(), false)); + // add preserved elements + if (preserveList.size() > 0) { + newList.addAll(preserveList); + } + // insert the correct glue + newList.addLast(new KnuthGlue(decreasedLength, 0, 0, SPACE_AFTER_ADJUSTMENT, + wrongBox.getPosition(), false)); + //System.out.println(" rimosso box " + neededUnits(wrongBox.getW())); + //System.out.println(" aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength)); + //System.out.println(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0"); + } + + return newList; + } + + protected static class StackingIter extends PositionIterator { + StackingIter(Iterator parentIter) { + super(parentIter); + } + + protected LayoutManager getLM(Object nextObj) { + return ((Position) nextObj).getLM(); + } + + protected Position getPos(Object nextObj) { + return ((Position) nextObj); + } + } + + protected static class MappingPosition extends Position { + private int iFirstIndex; + private int iLastIndex; + + public MappingPosition(LayoutManager lm, int first, int last) { + super(lm); + iFirstIndex = first; + iLastIndex = last; + } + + public int getFirstIndex() { + return iFirstIndex; + } + + public int getLastIndex() { + return iLastIndex; + } + } + + /** + * "wrap" the Position inside each element moving the elements from + * SourceList to targetList + * @param sourceList source list + * @param targetList target list receiving the wrapped position elements + */ + protected void wrapPositionElements(List sourceList, List targetList) { + ListIterator listIter = sourceList.listIterator(); + while (listIter.hasNext()) { + KnuthElement tempElement; + tempElement = (KnuthElement) listIter.next(); + if (tempElement.getLayoutManager() != this) { + tempElement.setPosition(new NonLeafPosition(this, + tempElement.getPosition())); + } + targetList.add(tempElement); + } + } + } |