diff options
author | Karen Lease <klease@apache.org> | 2002-05-13 06:12:42 +0000 |
---|---|---|
committer | Karen Lease <klease@apache.org> | 2002-05-13 06:12:42 +0000 |
commit | eb253d81c05132eefa53e762c403ca7236382a16 (patch) | |
tree | f63108d81354d6fef2d45292c8e551e47fbdb85c /src | |
parent | b45cb4b5005f233e501e3ab815d362539af885e3 (diff) | |
download | xmlgraphics-fop-eb253d81c05132eefa53e762c403ca7236382a16.tar.gz xmlgraphics-fop-eb253d81c05132eefa53e762c403ca7236382a16.zip |
Refactor Line and InlineStacking; correct and extend space-handling code
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194820 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r-- | src/org/apache/fop/layoutmgr/BreakCost.java | 16 | ||||
-rw-r--r-- | src/org/apache/fop/layoutmgr/InlineStackingBPLayoutManager.java | 274 | ||||
-rw-r--r-- | src/org/apache/fop/layoutmgr/LayoutContext.java | 56 | ||||
-rw-r--r-- | src/org/apache/fop/layoutmgr/LineBPLayoutManager.java | 295 | ||||
-rw-r--r-- | src/org/apache/fop/layoutmgr/TextBPLayoutManager.java | 9 |
5 files changed, 423 insertions, 227 deletions
diff --git a/src/org/apache/fop/layoutmgr/BreakCost.java b/src/org/apache/fop/layoutmgr/BreakCost.java index 98b05e19f..42a695ed1 100644 --- a/src/org/apache/fop/layoutmgr/BreakCost.java +++ b/src/org/apache/fop/layoutmgr/BreakCost.java @@ -15,6 +15,7 @@ import org.apache.fop.area.Area; */ public class BreakCost { private Area breakArea; + private BreakPoss bp; private int cost; // Will be more complicated than this! @@ -23,8 +24,21 @@ public class BreakCost { this.cost = cost; } + public BreakCost(BreakPoss bp, int cost) { + this.bp = bp; + this.cost = cost; + } + + BreakPoss getBP() { + return this.bp; + } + Area getArea() { - return breakArea; + return this.breakArea; + } + + int getCost() { + return this.cost; } public BreakCost chooseLowest(BreakCost otherCost) { diff --git a/src/org/apache/fop/layoutmgr/InlineStackingBPLayoutManager.java b/src/org/apache/fop/layoutmgr/InlineStackingBPLayoutManager.java index be87055c4..2a1763ea3 100644 --- a/src/org/apache/fop/layoutmgr/InlineStackingBPLayoutManager.java +++ b/src/org/apache/fop/layoutmgr/InlineStackingBPLayoutManager.java @@ -18,6 +18,7 @@ import org.apache.fop.area.inline.InlineParent; import java.util.Iterator; import java.util.ListIterator; +import java.util.HashMap; /** * LayoutManager for objects which stack children in the inline direction, @@ -58,12 +59,6 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { /** - * Holds IPD of all areas which would be generated by previously - * complete childLM since last area was generated. - */ - private MinOptMax m_prevContentIPD = new MinOptMax(0); - - /** * Size of any start or end borders and padding. */ private MinOptMax m_allocIPD = new MinOptMax(0); @@ -73,23 +68,20 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { */ private MinOptMax m_extraBPD; - /** Holds IPD of a child BP which had no break-after flag. We don't - * add this to m_previousIPD until we are sure that the next break - * position can really fit. - */ - private MinOptMax m_pendingIPD = new MinOptMax(0); private InlineProps m_inlineProps = null; private BorderAndPadding m_borderProps = null; private InlineParent m_inlineArea; - private boolean m_bFirstArea; private BreakPoss m_prevBP; + private LayoutContext m_childLC ; + + /** Used to store previous content IPD for each child LM. */ + private HashMap m_hmPrevIPD = new HashMap(); public InlineStackingBPLayoutManager(FObj fobj, ListIterator childLMiter) { super(fobj, childLMiter); - m_bFirstArea = true; // Initialize inline properties (borders, padding, space) // initProperties(); } @@ -119,13 +111,13 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { return new MinOptMax(iBP); } - private boolean hasLeadingFence(boolean bNotFirst) { + protected boolean hasLeadingFence(boolean bNotFirst) { int iBP = m_borderProps.getPadding(BorderAndPadding.START, bNotFirst); iBP += m_borderProps.getBorderWidth(BorderAndPadding.START, bNotFirst); return (iBP > 0); } - private boolean hasTrailingFence(boolean bNotLast) { + protected boolean hasTrailingFence(boolean bNotLast) { int iBP = m_borderProps.getPadding(BorderAndPadding.END, bNotLast); iBP += m_borderProps.getBorderWidth(BorderAndPadding.END, bNotLast); return (iBP > 0); @@ -139,13 +131,21 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { if (wrappedPos != null) { // Back up the layout manager iterator reset(wrappedPos.m_childLM, wrappedPos.m_childPosition); + if (m_prevBP != null && + m_prevBP.getLayoutManager() !=wrappedPos.m_childLM) { + m_childLC = null; + } + m_prevBP = new BreakPoss(wrappedPos.m_childLM, + wrappedPos.m_childPosition); } else { - // Backup to first child layout manager + // Backup to start of first child layout manager System.err.println("InlineStackingBPLM: resetPosition(null)"); + m_prevBP = null; super.resetPosition(prevPos); } // Do we need to reset some context like pending or prevContent? + // What about m_prevBP? } @@ -168,99 +168,119 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { else return false; // ??? NO child LM? } + protected MinOptMax getPrevIPD(LayoutManager lm) { + return (MinOptMax)m_hmPrevIPD.get(lm); + } + + protected void clearPrevIPD() { + m_hmPrevIPD.clear(); + } + public BreakPoss getNextBreakPoss(LayoutContext lc, BreakPoss.Position pbp) { // Get a break from currently active child LM BreakPoss bp =null; BPLayoutManager curLM ; - // Handle space before - boolean bIncludeStartSpace=false; - LayoutContext childLC = new LayoutContext(lc); - if (lc.isStart()) { - // First call to this LM in new parent "area", ie, this may + SpaceSpecifier leadingSpace = lc.getPendingSpace(); + + if (lc.startsNewArea()) { + // First call to this LM in new parent "area", but this may // not be the first area created by this inline + m_childLC = new LayoutContext(lc); lc.getPendingSpace().addSpace(m_inlineProps.spaceStart); // Check for "fence" - if (hasLeadingFence(!m_bFirstArea)) { - bIncludeStartSpace=true; + if (hasLeadingFence(!lc.isFirstArea())) { // Reset leading space sequence for child areas - childLC.setPendingSpace(new SpaceSpecifier(false)); + leadingSpace = new SpaceSpecifier(false); } // Reset state variables - m_pendingIPD = new MinOptMax(0); - m_prevContentIPD = new MinOptMax(0); + clearPrevIPD(); // Clear stored prev content dimensions } - else { - // Handle pending IPD: if we are called again, we assume previous - // break was OK - m_prevContentIPD.add(m_pendingIPD); - m_pendingIPD = new MinOptMax(0); - } -// if (m_prevChildLM != curLM && m_prevChildLM != null) { -// // Change child LM -// m_prevContentIPD.add(m_pendingIPD); -// } + // We only do this loop more than once if a childLM returns + // a null BreakPoss, meaning it has nothing (more) to layout. while ((curLM = getChildLM()) != null) { - // ???? - /* If first break for this child, set START_AREA flag */ - if (m_prevBP == null || m_prevBP.getLayoutManager()!=curLM) { - childLC.setFlags(LayoutContext.START_AREA); - if (m_prevBP != null) { - childLC.setPendingSpace(m_prevBP.getTrailingSpace()); - } - } + /* If first break for this child LM, set START_AREA flag + * and initialize pending space from previous LM sibling's + * trailing space specifiers. + */ + boolean bFirstChildBP = (m_prevBP == null || + m_prevBP.getLayoutManager()!=curLM); + + initChildLC(m_childLC, m_prevBP, lc.startsNewArea(), bFirstChildBP, + leadingSpace); - if (((bp = curLM.getNextBreakPoss(childLC)) != null)) { - // && couldEndLine(bp)) { + if (((bp = curLM.getNextBreakPoss(m_childLC)) != null)) { break; } -// if (bp.isLastArea()) { -// // NORMALLY IT MUST BE! -// m_pendingIPD.add(bp.getStackingSize()); -// m_prevBP = bp; -// } + // If LM has no content, should it generate any area? If not, + // should trailing space from a previous area interact with + // leading space from a following area? } if (bp==null) { setFinished(true); - return null; // There was no childLM + return null; // There was no childLM with anything to layout // Alternative is to return a BP with the isLast flag set } else { - // TODO! need to know whether this BP is in the first area for FO! - return makeBreakPoss(bp, lc.isStart(), - (getChildLM() == null), lc); + return makeBreakPoss(bp, lc, (getChildLM() == null)); } } - protected boolean couldEndLine(BreakPoss bp) { - if (bp.canBreakAfter()) { - return true; // no keep, ends on break char + /** ATTENTION: ALSO USED BY LineBPLayoutManager! */ + protected void initChildLC(LayoutContext childLC, BreakPoss prevBP, + boolean bStartParent, boolean bFirstChildBP, + SpaceSpecifier leadingSpace) { + + childLC.setFlags(LayoutContext.NEW_AREA, + (bFirstChildBP || bStartParent)); + if (bStartParent) { + // Start of a new line area or inline parent area + childLC.setFlags(LayoutContext.FIRST_AREA, bFirstChildBP ); + childLC.setPendingSpace(leadingSpace); } - else if (bp.isSuppressible()) { - // NOTE: except at end of content for this LM!! - // Never break after only space chars or any other sequence - // of areas which would be suppressed at the end of the line. - return false; + else if (bFirstChildBP) { + // Space-after sequence from previous "area" + childLC.setFlags(LayoutContext.FIRST_AREA, true); + childLC.setPendingSpace(prevBP.getTrailingSpace()); } else { - // See if could break before next area - LayoutContext lc=new LayoutContext(); - BPLayoutManager nextLM = getChildLM(); - return (nextLM == null || - nextLM.canBreakBefore(lc)); + childLC.setPendingSpace(null); } + } +// protected boolean couldEndLine(BreakPoss bp) { +// if (bp.canBreakAfter()) { +// return true; // no keep, ends on break char +// } +// else if (bp.isSuppressible()) { +// // NOTE: except at end of content for this LM!! +// // Never break after only space chars or any other sequence +// // of areas which would be suppressed at the end of the line. +// return false; +// } +// else { +// // See if could break before next area +// LayoutContext lc=new LayoutContext(); +// BPLayoutManager nextLM = getChildLM(); +// return (nextLM == null || +// nextLM.canBreakBefore(lc)); +// } +// } - protected BreakPoss makeBreakPoss(BreakPoss bp, boolean bIsFirst, - boolean bIsLast, LayoutContext lc) { + + private BreakPoss makeBreakPoss(BreakPoss bp, LayoutContext lc, + boolean bIsLast) { WrappedPosition inlbp = new WrappedPosition(bp.getLayoutManager(), bp.getPosition()); BreakPoss myBP = new BreakPoss(this, inlbp, bp.getFlags()); + myBP.setFlag(BreakPoss.ISFIRST, lc.isFirstArea()); + myBP.setFlag(BreakPoss.ISLAST, bIsLast); + // Update dimension information for our allocation area, // including child areas // generated by previous childLM which have completed layout @@ -268,45 +288,105 @@ public class InlineStackingBPLayoutManager extends AbstractBPLayoutManager { // This includes all previous breakinfo MinOptMax bpDim = (MinOptMax)bp.getStackingSize().clone(); + MinOptMax prevIPD = updatePrevIPD(bp, m_prevBP, + lc.startsNewArea(), lc.isFirstArea()); - if (m_prevBP == null || - m_prevBP.getLayoutManager() != bp.getLayoutManager()) { - /* This is first bp generated by child (in this parent area). - * Calculate space-start on this area in combination with any - * pending space-end on previously generated break possibilities. - * Can also have leading space if this FO has fence-preceding. - */ - bpDim.add(bp.resolveLeadingSpace()); - } - if (bp.isLastArea()) { - m_pendingIPD.add(bpDim); - // See if our area is also done - - } + if (lc.startsNewArea()) { + myBP.setLeadingSpace(lc.getPendingSpace()); + } - // Start and end borders and padding - bpDim.add(m_prevContentIPD); - bpDim.add(getExtraIPD(!bIsFirst, !bIsLast)); +// if (lc.startsNewArea()) { +// if (hasLeadingFence(!lc.isFirstArea())) { +// // Space-start before first child area placed +// prevIPD.add(bp.resolveLeadingSpace()); +// } +// // Space-start sequence passed to ancestors +// myBP.setLeadingSpace(lc.getPendingSpace()); +// m_hmPrevIPD.put(bp.getLayoutManager(), prevIPD); +// } +// else { +// // In case of reset to a previous position, it may already +// // be calculated +// prevIPD = (MinOptMax)m_hmPrevIPD.get(bp.getLayoutManager()); +// if (prevIPD == null) { +// // ASSERT(m_prevBP.getLayoutManager() != bp.getLayoutManager()); +// /* This is first bp generated by child (in this parent area). +// * Calculate space-start on this area in combination with any +// * pending space-end with previous break. +// * Corresponds to Space between two child areas. +// */ +// prevIPD = +// (MinOptMax)m_hmPrevIPD.get(m_prevBP.getLayoutManager()); +// prevIPD = MinOptMax.add(prevIPD, bp.resolveLeadingSpace()); +// prevIPD.add(m_prevBP.getStackingSize()); +// m_hmPrevIPD.put(bp.getLayoutManager(), prevIPD); +// } +// } + // Add size of previous child areas which are finished + bpDim.add(prevIPD); +// if (bp.isLastArea()) { +// m_childLC.setPendingSpace((SpaceSpecifier)bp.getTrailingSpace(). +// clone()); +// } + + SpaceSpecifier trailingSpace = bp.getTrailingSpace(); + if (hasTrailingFence(!bIsLast)) { + bpDim.add(bp.resolveTrailingSpace(false)); + trailingSpace = new SpaceSpecifier(false); + } + trailingSpace.addSpace(m_inlineProps.spaceEnd); + myBP.setTrailingSpace(trailingSpace); + + // Add start and end borders and padding + bpDim.add(getExtraIPD(!lc.isFirstArea(), !bIsLast)); myBP.setStackingSize(bpDim); myBP.setNonStackingSize(MinOptMax.add(bp.getNonStackingSize(), m_extraBPD)); + + m_prevBP = bp; if (bIsLast) { setFinished(true); // Our last area, so indicate done - myBP.setFlag(BreakPoss.ISLAST, true); } - else { - myBP.setFlag(BreakPoss.ISLAST, false); - } - myBP.setTrailingSpace((SpaceSpecifier)bp.getTrailingSpace().clone()); - myBP.getTrailingSpace().addSpace(m_inlineProps.spaceEnd); - // If this FO doesn't have fence-start, then this value should - // come from the lower level BP! - myBP.setLeadingSpace(new SpaceSpecifier(false)); - m_prevBP = bp; return myBP; } + /** ATTENTION: ALSO USED BY LineBPLayoutManager! */ + protected MinOptMax updatePrevIPD(BreakPoss bp, BreakPoss prevBP, + boolean bStartParent, boolean bFirstArea) + { + MinOptMax prevIPD = new MinOptMax(0); + + if (bStartParent) { + if (hasLeadingFence(!bFirstArea)) { + // Space-start before first child area placed + prevIPD.add(bp.resolveLeadingSpace()); + } + // Space-start sequence passed to ancestors + // myBP.setLeadingSpace(lc.getPendingSpace()); + m_hmPrevIPD.put(bp.getLayoutManager(), prevIPD); + } + else { + // In case of reset to a previous position, it may already + // be calculated + prevIPD = (MinOptMax)m_hmPrevIPD.get(bp.getLayoutManager()); + if (prevIPD == null) { + // ASSERT(prevBP.getLayoutManager() != bp.getLayoutManager()); + /* This is first bp generated by child (in this parent area). + * Calculate space-start on this area in combination with any + * pending space-end with previous break. + * Corresponds to Space between two child areas. + */ + prevIPD = + (MinOptMax)m_hmPrevIPD.get(prevBP.getLayoutManager()); + prevIPD = MinOptMax.add(prevIPD, bp.resolveLeadingSpace()); + prevIPD.add(prevBP.getStackingSize()); + m_hmPrevIPD.put(bp.getLayoutManager(), prevIPD); + } + } + return prevIPD; + } + /****** protected BreakableText getText(BreakPoss prevBP, BreakPoss lastBP) { diff --git a/src/org/apache/fop/layoutmgr/LayoutContext.java b/src/org/apache/fop/layoutmgr/LayoutContext.java index bad0a871f..12e693716 100644 --- a/src/org/apache/fop/layoutmgr/LayoutContext.java +++ b/src/org/apache/fop/layoutmgr/LayoutContext.java @@ -17,8 +17,8 @@ public class LayoutContext { * Values for flags. */ public static final int LINEBREAK_AT_LF_ONLY = 0x01; - public static final int START_BLOCK = 0x02; - public static final int START_AREA = 0x02; // inline too + /** Generated break possibility is first in a new area */ + public static final int NEW_AREA = 0x02; public static final int IPD_UNKNOWN = 0x04; /** Signal to a Line LM that a higher level LM may provoke a change * in the reference area, thus ref area IPD. The LineLM should return @@ -33,6 +33,7 @@ public class LayoutContext { * except the first. */ public static final int SUPPRESS_LEADING_SPACE = 0x10; + public static final int FIRST_AREA = 0x20; public int flags; // Contains some set of flags defined above @@ -43,13 +44,8 @@ public class LayoutContext { * These LM <b>may</b> wish to pass this information down to lower * level LM to allow them to optimize returned break possibilities. */ - MinOptMax stackLimit; + MinOptMax m_stackLimit; - /** Current stacking dimension, either IPD or BPD, depending on - * caller. This is generally the value returned by a previous call - * to getNextBreakPoss(). - */ - MinOptMax m_stackSize; /** True if current top-level reference area is spanning. */ boolean bIsSpan; @@ -63,27 +59,49 @@ public class LayoutContext { public LayoutContext(LayoutContext parentLC) { this.flags = parentLC.flags; this.refIPD = parentLC.refIPD; - this.stackLimit = null; // Don't reference parent MinOptMax! + this.m_stackLimit = null; // Don't reference parent MinOptMax! this.m_pendingSpace = parentLC.m_pendingSpace; //??? // Copy other fields as necessary. Use clone??? } - public LayoutContext() { - this.flags = 0; + public LayoutContext(int flags) { + this.flags = flags; this.refIPD = 0; - stackLimit = new MinOptMax(0); + m_stackLimit = new MinOptMax(0); + m_pendingSpace = null; } public void setFlags(int flags) { - this.flags |= flags; + setFlags(flags, true); + } + + public void setFlags(int flags, boolean bSet) { + if (bSet) { + this.flags |= flags; + } + else { + this.flags &= ~flags; + } } public void unsetFlags(int flags) { - this.flags &= ~flags; + setFlags(flags, false); } public boolean isStart() { - return ((this.flags & START_BLOCK) != 0); + return ((this.flags & NEW_AREA) != 0); + } + + public boolean startsNewArea() { + return ((this.flags & NEW_AREA) != 0 && m_pendingSpace != null); + } + + public boolean isFirstArea() { + return ((this.flags & FIRST_AREA) != 0); + } + + public boolean suppressLeadingSpace() { + return ((this.flags & SUPPRESS_LEADING_SPACE) != 0); } public void setPendingSpace(SpaceSpecifier space) { @@ -94,11 +112,11 @@ public class LayoutContext { return m_pendingSpace; } - public void setStackSize(MinOptMax stackSize) { - m_stackSize = stackSize; + public void setStackLimit(MinOptMax stackLimit) { + m_stackLimit = stackLimit; } - public MinOptMax getStackSize() { - return m_stackSize ; + public MinOptMax getStackLimit() { + return m_stackLimit ; } } diff --git a/src/org/apache/fop/layoutmgr/LineBPLayoutManager.java b/src/org/apache/fop/layoutmgr/LineBPLayoutManager.java index 71339821d..ef2883b86 100644 --- a/src/org/apache/fop/layoutmgr/LineBPLayoutManager.java +++ b/src/org/apache/fop/layoutmgr/LineBPLayoutManager.java @@ -24,6 +24,7 @@ import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.Character; import java.util.ListIterator; +import java.util.Iterator; import java.util.List; import java.util.Vector; import java.util.ArrayList; @@ -34,7 +35,7 @@ import java.util.ArrayList; * inline areas generated by its sub layout managers. */ public class LineBPLayoutManager extends - InlineStackingBPLayoutManager { + InlineStackingBPLayoutManager { /** * Private class to store information about inline breaks. @@ -43,9 +44,11 @@ public class LineBPLayoutManager extends */ private static class LineBreakPosition implements BreakPoss.Position { int m_iPos; + double m_dAdjust; // Percentage to adjust (stretch or shrink) - LineBreakPosition(int iBreakIndex) { + LineBreakPosition(int iBreakIndex, double dAdjust) { m_iPos = iBreakIndex; + m_dAdjust = dAdjust; } } @@ -55,7 +58,6 @@ public class LineBPLayoutManager extends /** Break positions returned by inline content. */ private Vector m_vecInlineBreaks = new Vector(100); - private SpaceSpecifier m_pendingSpace; private BreakPoss m_prevBP = null; // Last confirmed break position private boolean m_bJustify = false; // True if fo:block text-align=JUSTIFY private int m_iTextIndent = 0; @@ -63,9 +65,9 @@ public class LineBPLayoutManager extends public LineBPLayoutManager(FObj fobj, List lms, int lh, int l, int f) { - //super(fobj, lms, lh, l, f); + //super(fobj, lms.listIterator(), lh, l, f); super(fobj, lms.listIterator()); - // initProperties(); + init(); // Normally done when started by parent! } protected void initProperties(PropertyManager propMgr) { @@ -79,7 +81,6 @@ public class LineBPLayoutManager extends } - /** * Call child layout managers to generate content as long as they * generate inline areas. If a block-level generating LM is found, @@ -98,105 +99,85 @@ public class LineBPLayoutManager extends return new BreakPoss(this, null, BreakPoss.NEED_IPD); } - BPLayoutManager prevLM = null; // previous active LM BPLayoutManager curLM ; // currently active LM + BreakPoss prevBP=null; BreakPoss bp=null; // proposed BreakPoss - // IPD remaining in line - MinOptMax availIPD = context.stackLimit; + Vector vecPossEnd = new Vector(); - // IPD of any unbreakable finished FO content - MinOptMax pendingIPD = null; + // IPD remaining in line + MinOptMax availIPD = context.getStackLimit(); // QUESTION: maybe LayoutContext holds the Properties which // come from block-level? LayoutContext inlineLC = new LayoutContext(context); - inlineLC.setPendingSpace(new SpaceSpecifier(true)); + + clearPrevIPD(); while ((curLM = getChildLM()) != null) { - // INITIALIZE FLAGS FOR CALL TO CHILD LM - boolean bFirstBPforLM = (prevLM != curLM); - if (bFirstBPforLM) { - prevLM = curLM; - inlineLC.setFlags(LayoutContext.START_AREA); - if (bp != null) { - inlineLC.setPendingSpace(bp.getTrailingSpace()); - } - } - else { - inlineLC.unsetFlags(LayoutContext.START_AREA); - inlineLC.setPendingSpace(null); - } + // INITIALIZE LAYOUT CONTEXT FOR CALL TO CHILD LM + // First break for the child LM in each of its areas + boolean bFirstBPforLM = + (m_vecInlineBreaks.isEmpty() || + (((BreakPoss)m_vecInlineBreaks.lastElement()). + getLayoutManager() != curLM)); + + initChildLC(inlineLC, bp, (bp==null), bFirstBPforLM, + new SpaceSpecifier(true)); + +// if (bp == null) { +// // Start of a new line area +// inlineLC.setFlags(LayoutContext.FIRST_AREA, bFirstBPforLM ); +// inlineLC.setPendingSpace(new SpaceSpecifier(true)); +// } +// else if (bFirstBPforLM) { +// // Space-after sequence from previous "area" +// inlineLC.setFlags(LayoutContext.FIRST_AREA, true); +// inlineLC.setPendingSpace(bp.getTrailingSpace()); +// } +// else { +// inlineLC.setPendingSpace(null); +// } + + /* If first BP in this line but line is not first in this - * LM and previous possible linebreak was not forced (LINEFEED), + * LM and previous line end decision was not forced (LINEFEED), * then set the SUPPRESS_LEADING_SPACE flag. */ - if (bp == null && !m_vecInlineBreaks.isEmpty() && - ((BreakPoss)m_vecInlineBreaks.lastElement()). - isForcedBreak()==false) { - inlineLC.setFlags(LayoutContext.SUPPRESS_LEADING_SPACE); - } - else { - inlineLC.unsetFlags(LayoutContext.SUPPRESS_LEADING_SPACE); - } + inlineLC.setFlags(LayoutContext.SUPPRESS_LEADING_SPACE, + (bp == null && !m_vecInlineBreaks.isEmpty() && + ((BreakPoss)m_vecInlineBreaks.lastElement()). + isForcedBreak()==false)); + // GET NEXT POSSIBLE BREAK FROM CHILD LM + prevBP = bp; if ((bp = curLM.getNextBreakPoss(inlineLC, null)) != null) { + // Add any space before and previous content dimension + MinOptMax prevIPD = updatePrevIPD(bp, prevBP, (prevBP==null), + inlineLC.isFirstArea()); + MinOptMax bpDim = MinOptMax.add(bp.getStackingSize(), prevIPD); + // check if this bp fits in line - MinOptMax bpDim = (MinOptMax)bp.getStackingSize().clone(); - /* If first BP for this LM (in this call) - * add any leading space. - */ - if (bFirstBPforLM) { - if (pendingIPD != null) { - bpDim.add(pendingIPD); - } - bpDim.add(bp.resolveLeadingSpace()); - } boolean bBreakOK = couldEndLine(bp); if (bBreakOK) { - /* Add any non-conditional trailing space. */ + /* Add any non-conditional trailing space, assuming we + * end the line here. If we can't break here, we just + * check if the content fits. */ bpDim.add(bp.resolveTrailingSpace(true)); } + // TODO: stop if linebreak is forced (NEWLINE) + // PROBLEM: interaction with wrap which can be set + // at lower levels! + System.err.println("BPdim=" + bpDim.opt); + // Check if proposed area would fit in line - if (bpDim.max < availIPD.min) { - // Break fits buts is short - if (bBreakOK) { - if (pendingIPD != null) { - availIPD.subtract(pendingIPD); - // Subtract space-start for this area, since - // we know at least part of it fits. - availIPD.subtract(bp.getLeadingSpace(). - resolve(false)); - pendingIPD = null; - // Add all pending BP list members to BP list - } - m_prevBP = bp; // Save reference to this BP - m_vecInlineBreaks.add(bp); - // Handle end of this LM's areas - if (bp.isLastArea()) { - /* NOTE: this doesn't include space-end since - * it may combine with space-start on the - * following FO's first area. - */ - availIPD.subtract(bp.getStackingSize()); - } - } - else { - /* Can't end line here, so mark size pending. - * This includes any previosly pending size, - * already calculated above. - */ - if (bp.isLastArea()) { - pendingIPD = bpDim; - } - // Add BP to the list even though we can't break here - // We need to keep it for area generation - m_vecInlineBreaks.add(bp); - } - } - else if (bpDim.min > availIPD.max) { + if (bpDim.min > availIPD.max) { + // See if we have already found a potential break + if (vecPossEnd.size() > 0) break; + // This break position doesn't fit + // TODO: If we are in nowrap, we use it as is! if (m_bJustify || m_prevBP == null) { // try to find a hyphenation point in the word // which spans the queued breaks and the proposed bp @@ -225,23 +206,38 @@ public class LineBPLayoutManager extends break; } else { - /* This is a possible line BP (line could be filled) - * bpDim.max >= availIPD.min - * Keep this as a possible break, depending on "cost". - * We will choose lowest cost. Cost depends on stretch - * (ie, bpDim.opt closes to availIPD.opt), keeps - * and hyphenation. - */ - m_prevBP = bp; - break; //??? - } + // Add the BP to the list whether or not we can break + m_vecInlineBreaks.add(bp); + // Handle end of this LM's areas + if (bBreakOK) { + m_prevBP = bp; // Save reference to this BP + if (bp.isForcedBreak()) { + break; + } + if (bpDim.max >= availIPD.min) { + /* This is a possible line BP (line could be filled) + * bpDim.max >= availIPD.min + * Keep this as a possible break, depending on + * "cost". We will choose lowest cost. + * Cost depends on stretch + * (ie, bpDim.opt closes to availIPD.opt), keeps + * and hyphenation. + */ + System.err.println("Found potential linebreak"); + vecPossEnd.add(new BreakCost(bp, + Math.abs(availIPD.opt - bpDim.opt ))); + } + // Otherwise it's short + } + else { + /* Can't end line here. */ + } + } // end of bpDim.min <= availIPD.max } // end of getNextBreakPoss!=null on current child LM else { - /* What if the childLM returns null? - * No further break possibility in current sequence of - * inline LM. Previous break or end of area will be the - * ending for the current line. - * Otherwise we have filled the current line. + /* The child LM can return a null BreakPoss if it has + * nothing (more) to layout. This can happen when backing + * up. Just try the next child LM. */ } } // end of while on child LM @@ -249,6 +245,10 @@ public class LineBPLayoutManager extends // No more content to layout! setFinished(true); } + // Choose the best break + if (!bp.isForcedBreak() && vecPossEnd.size()>0) { + m_prevBP = getBestBP(vecPossEnd); + } // Backup child LM if necessary if (bp != m_prevBP) { // Remove any pending breaks from the vector @@ -257,20 +257,104 @@ public class LineBPLayoutManager extends } reset(m_prevBP.getLayoutManager(), m_prevBP.getPosition()); } - return makeLineBreak(m_prevBP); + // Distribute space in the line + MinOptMax actual = MinOptMax.add(m_prevBP.getStackingSize(), + getPrevIPD(m_prevBP.getLayoutManager())); + // ATTENTION: make sure this hasn't gotten start space for next + // LM added onto it! + actual.add(m_prevBP.resolveTrailingSpace(true)); + System.err.println("Target opt=" + availIPD.opt + + " bp.opt=" + actual.opt + " bp.max=" + actual.max + + " bm.min=" + actual.min); + + // Don't justify last line in the sequence or if forced line-end + boolean bJustify = (m_bJustify && !m_prevBP.isForcedBreak() && + !isFinished()); + return makeLineBreak(m_prevBP, availIPD, actual, bJustify); + } + + protected boolean couldEndLine(BreakPoss bp) { + if (bp.canBreakAfter()) { + return true; // no keep, ends on break char + } + else if (bp.isSuppressible()) { + // NOTE: except at end of content for this LM!! + // Never break after only space chars or any other sequence + // of areas which would be suppressed at the end of the line. + return false; + } + else { + // See if could break before next area + // TODO: do we need to set anything on the layout context? + LayoutContext lc=new LayoutContext(0); + BPLayoutManager nextLM = getChildLM(); + return (nextLM == null || + nextLM.canBreakBefore(lc)); + } } + private BreakPoss getBestBP(Vector vecPossEnd) { + if (vecPossEnd.size()==1) { + return ((BreakCost)vecPossEnd.elementAt(0)).getBP(); + } + // Choose the best break (use a sort on cost!) + Iterator iter = vecPossEnd.iterator(); + int minCost= Integer.MAX_VALUE; + BreakPoss bestBP = null; + while (iter.hasNext()) { + BreakCost bc = (BreakCost)iter.next(); + if (bc.getCost() < minCost) { + minCost = bc.getCost(); + bestBP = bc.getBP(); + } + } + return bestBP; + } + + /** Line area is always considered to act as a fence. */ + protected boolean hasLeadingFence(boolean bNotFirst) { + return true; + } + + /** Line area is always considered to act as a fence. */ + protected boolean hasTrailingFence(boolean bNotLast) { + return true; + } + + private BreakPoss findHyphenPoss(BreakPoss prevBP, BreakPoss newBP) { + // Get a "word" to hyphenate by getting characters from all + // pending break poss which are in m_vecInlineBreaks, starting + // with the position just AFTER prevBP.getPosition() return null; } - private BreakPoss makeLineBreak(BreakPoss inlineBP) { + private BreakPoss makeLineBreak(BreakPoss inlineBP, MinOptMax target, + MinOptMax actual, boolean bJustify) { // make a new BP + // Store information needed to make areas in the LineBreakPosition! + // Calculate stretch or shrink factor + + double dAdjust=0.0; + if (bJustify) { + if (actual.opt < target.opt) { + // Stretch + dAdjust = (double)(target.opt - actual.opt)/ + (double)(actual.max - actual.opt); + } + else { + // Shrink + dAdjust = (double)(target.opt - actual.opt)/ + (double)( actual.opt - actual.min); + } + } + System.err.println("Adjustment factor=" + dAdjust); BreakPoss curLineBP = new BreakPoss(this, - new LineBreakPosition(m_vecInlineBreaks.size()-1)); + new LineBreakPosition(m_vecInlineBreaks.size()-1, + dAdjust)); /* FIX ME!! * Need to calculate line height based on all inline BP info @@ -321,10 +405,9 @@ public class LineBPLayoutManager extends BreakPoss bp; Vector vecBreakPoss = new Vector(20); - LayoutContext childLC = new LayoutContext(); // Force area creation on first call - // NOTE: normally not necessary - childLC.flags |= LayoutContext.CHECK_REF_AREA; + // NOTE: normally not necessary when fully integrated! + LayoutContext childLC = new LayoutContext(LayoutContext.CHECK_REF_AREA); while (!isFinished()) { if ((bp = getNextBreakPoss(childLC, null)) != null) { @@ -338,13 +421,13 @@ public class LineBPLayoutManager extends parentLM.getParentArea(null); ipd = parentLM.getContentIPD(); childLC.flags &= ~LayoutContext.CHECK_REF_AREA; - childLC.stackLimit = new MinOptMax(ipd - m_iIndents - - m_iTextIndent); + childLC.setStackLimit(new MinOptMax(ipd - m_iIndents - + m_iTextIndent)); } else { vecBreakPoss.add(bp); // Reset stackLimit for non-first lines - childLC.stackLimit = new MinOptMax(ipd - m_iIndents); + childLC.setStackLimit(new MinOptMax(ipd - m_iIndents)); } } } diff --git a/src/org/apache/fop/layoutmgr/TextBPLayoutManager.java b/src/org/apache/fop/layoutmgr/TextBPLayoutManager.java index b0c28ec17..f1bec939d 100644 --- a/src/org/apache/fop/layoutmgr/TextBPLayoutManager.java +++ b/src/org/apache/fop/layoutmgr/TextBPLayoutManager.java @@ -190,8 +190,7 @@ public class TextBPLayoutManager extends AbstractBPLayoutManager { */ public boolean canBreakBefore(LayoutContext context) { char c = chars[m_iNextStart]; - return ((c == NEWLINE) || - ((context.flags & LayoutContext.LINEBREAK_AT_LF_ONLY)==0 && + return ((c == NEWLINE) || (textInfo.bWrap && (CharUtilities.isSpace(c) || s_breakChars.indexOf(c)>=0))); } @@ -245,17 +244,19 @@ public class TextBPLayoutManager extends AbstractBPLayoutManager { int iFlags = 0; - if ((context.flags & LayoutContext.START_AREA)!=0) { + if (context.startsNewArea()) { /* This could be first call on this LM, or the first call * in a new (possible) LineArea. */ m_ipdTotal = new MinOptMax(0); iFlags |= BreakPoss.ISFIRST; + // May have leading space too which can combine with a + // leading word-space or letter-space } // HANDLE SUPPRESSED LEADING SPACES - if ((context.flags & LayoutContext.SUPPRESS_LEADING_SPACE)!=0) { + if (context.suppressLeadingSpace()) { /* If any leading space characters, ignore them. */ // NOTE: Skips word-space chars only, not other white-space! for (; m_iNextStart < chars.length && |