klease 02/04/28 14:31:00 Added: src/org/apache/fop/layoutmgr AbstractBPLayoutManager.java BPLayoutManager.java BreakPoss.java BreakPossPosIter.java LayoutContext.java LineBPLayoutManager.java PositionIterator.java TextBPLayoutManager.java Log: New files for the BreakPoss(ibility) Layout Manager scheme Revision Changes Path 1.1 xml-fop/src/org/apache/fop/layoutmgr/AbstractBPLayoutManager.java Index: AbstractBPLayoutManager.java =================================================================== /* * $Id: AbstractBPLayoutManager.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyManager; import org.apache.fop.fo.FONode; import org.apache.fop.area.Area; import java.util.ListIterator; import java.util.ArrayList; /** * The base class for all BPLayoutManagers. */ public abstract class AbstractBPLayoutManager extends AbstractLayoutManager implements BPLayoutManager { /** True if this LayoutManager has handled all of its content. */ private boolean m_bFinished = false; public AbstractBPLayoutManager(FObj fobj) { super(fobj); } /** * This method provides a hook for a LayoutManager to intialize traits * for the areas it will create, based on Properties set on its FO. */ protected final void initProperties() { if (fobj != null) { initProperties(fobj.getPropertyManager()); } } /** * This method provides a hook for a LayoutManager to intialize traits * for the areas it will create, based on Properties set on its FO. */ protected void initProperties(PropertyManager pm) { System.err.println("AbstractBPLayoutManager.initProperties"); } /** * Tell whether this LayoutManager has handled all of its content. * @return True if there are no more break possibilities, * ie. the last one returned represents the end of the content. */ public boolean isFinished() { return m_bFinished; } public void setFinished(boolean bFinished) { m_bFinished = bFinished; } // /** // * Get the BreakPoss at the start of the next "area". // * @param lc The LayoutContext for this LayoutManager. // * @param bpPrevEnd The Position returned by the previous call // * to getNextBreakPoss, or null if none. // */ // public BreakPoss getStartBreakPoss(LayoutContext lc, // BreakPoss.Position bpPrevEnd) { // return null; // } /** * Generate and return the next break possibility. * Each layout manager must implement this. * TODO: should this be abstract or is there some reasonable * default implementation? */ public BreakPoss getNextBreakPoss(LayoutContext context) { return getNextBreakPoss(context, null); } public BreakPoss getNextBreakPoss(LayoutContext context, BreakPoss.Position prevBreakPoss) { return null; } /** * Return value indicating whether the next area to be generated could * start a new line or flow area. * In general, if can't break at the current level, delegate to * the first child LM. * NOTE: should only be called if the START_AREA flag is set in context, * since the previous sibling LM must have returned a BreakPoss which * does not allow break-after. * QUESTION: in block-stacked areas, does this mean some kind of keep * condition, or is it only used for inline-stacked areas? * Default implementation always returns true. */ public boolean canBreakBefore(LayoutContext context) { return true; } public void addAreas(PositionIterator parentIter) { } /* --------------------------------------------------------- * PROVIDE NULL IMPLEMENTATIONS OF METHODS from LayoutManager * interface which are declared abstract in AbstractLayoutManager. * ---------------------------------------------------------*/ public Area getParentArea(Area childArea) { return null; } protected boolean flush() { return false; } public boolean addChild(Area childArea) { return false; } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/BPLayoutManager.java Index: BPLayoutManager.java =================================================================== /* * $Id: BPLayoutManager.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.area.Area; /** * The interface for all BreakPoss LayoutManagers. */ public interface BPLayoutManager extends LayoutManager { /** * Return true if the next area which would be generated by this * LayoutManager could start a new line (or flow for block-level FO). */ public boolean canBreakBefore(LayoutContext lc); /** * Generate and return the next break possibility. * @param context The layout context contains information about pending * space specifiers from ancestor areas or previous areas, reference * area inline-progression-dimension and various other layout-related * information. * @param prevBreakPosition If not null, gives a Position returned by * this layout manager on a previous call to getNextBreakPoss. It may not * be the previous one returned. The Layout Manager should return the next * potential Break Possibility after prevBreakPosition. * If prevBreakPosition is null, it should return the first possible * BreakPoss. */ public BreakPoss getNextBreakPoss(LayoutContext context, BreakPoss.Position prevBreakPosition); public BreakPoss getNextBreakPoss(LayoutContext context); /** CURRENTLY NOT USED public BreakPoss getStartBreakPoss(LayoutContext lc, BreakPoss.Position bpPrevEnd); **/ /** * Return a value indicating whether this LayoutManager has laid out * all its content (or generated BreakPossibilities for all content.) */ public boolean isFinished() ; /** * Set a flag indicating whether the LayoutManager has laid out all * its content. This is generally called by the LM itself, but can * be called by a parentLM when backtracking. */ public void setFinished(boolean isFinished) ; /** * Tell the layout manager to add all the child areas implied * by BreakPoss.Position objectw which will be returned by the * Iterator. */ public void addAreas(PositionIterator posIter) ; } 1.1 xml-fop/src/org/apache/fop/layoutmgr/BreakPoss.java Index: BreakPoss.java =================================================================== /* * $Id: BreakPoss.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2002 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.area.MinOptMax; import org.apache.fop.traits.LayoutProps; /** * Represents a break possibility for the layout manager. * Used to pass information between different levels of layout manager concerning * the break positions. In an inline context, before and after are interpreted as * start and end. * The m_position field is opaque but should represent meaningful information to * the layout manager stored in m_lm. * @author Karen Lease */ public class BreakPoss { /** * Marker interface. Generally a LayoutManager class will include * a class implementing this interface which it uses to store its * own Break Position information. */ public interface Position { } /** Values for m_flags returned from lower level LM. */ public static final int CAN_BREAK_AFTER= 0x01; // May break after public static final int ISLAST= 0x02; // Last area generated by FO public static final int ISFIRST= 0x04; // First area generated by FO public static final int FORCE= 0x08; // Forced break (ie LF) public static final int CAN_BREAK_BEFORE=0x10; public static final int NEED_IPD = 0x20; public static final int HAS_ANCHORS = 0x40; // Set this flag if all fo:character generated Areas would // suppressed at the end or beginning of a line public static final int ALL_ARE_SUPPRESS_AT_LB = 0x80; /** The top-level layout manager which generated this BreakPoss. */ private BPLayoutManager m_lm; /** The opaque position object used by m_lm to record its * break position. */ private Position m_position; /** * The size range in the stacking direction of the area which would be * generated if this BreakPoss were used. */ private MinOptMax m_stackSize; /** * Max height above and below the baseline. These are cumulative. */ private int m_iMaxAscender; private int m_iMaxDescender; /** Size in the non-stacking direction (perpendicular). */ private MinOptMax m_nonStackSize; private long m_flags = 0; private LayoutProps m_layoutProps = new LayoutProps(); /** private boolean m_bIsFirst=false; private boolean m_bIsLast=false; private boolean m_bCanBreakAfter; private boolean m_bCanBreakBefore; **/ /** Store space-after (or end) and space-before (or start) to be * added if this break position is used. */ private SpaceSpecifier m_spaceSpecTrailing; private SpaceSpecifier m_spaceSpecLeading; public BreakPoss(BPLayoutManager lm, Position position) { this(lm,position,0); } public BreakPoss(BPLayoutManager lm, Position position, int flags) { m_lm = lm; m_position = position; m_flags = flags; } /** * The top-level layout manager responsible for this break */ public BPLayoutManager getLayoutManager() { return m_lm; } public void setLayoutManager(BPLayoutManager lm) { m_lm = lm; } /** * An object representing the break position in this layout manager. */ public Position getPosition() { return m_position; } public void setPosition(Position pos) { m_position = pos; } public void setStackingSize(MinOptMax size) { this.m_stackSize = size; } public MinOptMax getStackingSize() { return this.m_stackSize ; } public void setNonStackingSize(MinOptMax size) { this.m_nonStackSize = size; } public MinOptMax getNonStackingSize() { return this.m_nonStackSize ; } public void setFlag(int flagBit) { setFlag(flagBit, true); } public void setFlag(int flagBit, boolean bSet) { if (bSet) { m_flags |= flagBit; } else { m_flags &= ~flagBit; } } public boolean isLastArea() { return ((m_flags & ISLAST) != 0); } public boolean isFirstArea() { return ((m_flags & ISFIRST) != 0); } public boolean canBreakAfter() { return ((m_flags & CAN_BREAK_AFTER) != 0); } public boolean canBreakBefore() { return ((m_flags & CAN_BREAK_BEFORE) != 0); } public boolean isForcedBreak() { return ((m_flags & FORCE) != 0); } public boolean isSuppressible() { return ((m_flags & ALL_ARE_SUPPRESS_AT_LB) != 0); } public SpaceSpecifier getLeadingSpace() { return m_spaceSpecLeading; } public MinOptMax resolveLeadingSpace() { if (m_spaceSpecLeading != null) { return m_spaceSpecLeading.resolve(false); } else return new MinOptMax(0); } public SpaceSpecifier getTrailingSpace() { return m_spaceSpecTrailing; } public MinOptMax resolveTrailingSpace(boolean bEndsRefArea) { if (m_spaceSpecTrailing != null) { return m_spaceSpecTrailing.resolve(bEndsRefArea); } else return new MinOptMax(0); } public void setLeadingSpace(SpaceSpecifier spaceSpecLeading) { m_spaceSpecLeading = spaceSpecLeading; } public void setTrailingSpace(SpaceSpecifier spaceSpecTrailing) { m_spaceSpecTrailing = spaceSpecTrailing; } public LayoutProps getLayoutProps() { return m_layoutProps; } public boolean checkIPD() { return ((m_flags & NEED_IPD) != 0); } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/BreakPossPosIter.java Index: BreakPossPosIter.java =================================================================== /* * $Id: BreakPossPosIter.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import java.util.List; public class BreakPossPosIter extends PositionIterator { private int m_iterCount ; BreakPossPosIter(List bpList, int startPos, int endPos) { super(bpList.listIterator(startPos)); m_iterCount = endPos - startPos; } // Check position < endPos protected boolean checkNext() { return (m_iterCount > 0 && super.checkNext()); } public Object next() { --m_iterCount; return super.next(); } protected BPLayoutManager getLM(Object nextObj) { return ((BreakPoss)nextObj).getLayoutManager(); } protected BreakPoss.Position getPos(Object nextObj) { return ((BreakPoss)nextObj).getPosition(); } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/LayoutContext.java Index: LayoutContext.java =================================================================== /* * $Id: LayoutContext.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2002 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.area.MinOptMax; /** * This class is used to pass information to the getNextBreakPoss() * method. It is set up by higher level LM and used by lower level LM. */ 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 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 * without looking for a line break. */ public static final int CHECK_REF_AREA = 0x08; /** * If this flag is set, it indicates that any leading fo:character * objects with suppress-at-line-break="suppress" should not generate * areas. This is the case at the beginning of each new LineArea * except the first. */ public static final int SUPPRESS_LEADING_SPACE = 0x10; public int flags; // Contains some set of flags defined above /** * Total available stacking dimension for a "galley-level" layout * manager (Line or Flow). 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. */ MinOptMax 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; /** inline-progression-dimension of nearest ancestor reference area */ int refIPD; /** Current pending space-after or space-end from preceding area */ SpaceSpecifier m_pendingSpace; public LayoutContext(LayoutContext parentLC) { this.flags = parentLC.flags; this.refIPD = parentLC.refIPD; this.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; this.refIPD = 0; stackLimit = new MinOptMax(0); } public void setFlags(int flags) { this.flags |= flags; } public void unsetFlags(int flags) { this.flags &= ~flags; } public boolean isStart() { return ((this.flags & START_BLOCK) != 0); } public void setPendingSpace(SpaceSpecifier space) { m_pendingSpace = space; } public SpaceSpecifier getPendingSpace() { return m_pendingSpace; } public void setStackSize(MinOptMax stackSize) { m_stackSize = stackSize; } public MinOptMax getStackSize() { return m_stackSize ; } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/LineBPLayoutManager.java Index: LineBPLayoutManager.java =================================================================== /* * $Id: LineBPLayoutManager.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.fo.FObj; import org.apache.fop.fo.TextInfo; import org.apache.fop.fo.PropertyManager; import org.apache.fop.layout.MarginProps; import org.apache.fop.traits.BlockProps; import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; import org.apache.fop.area.MinOptMax; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.fo.properties.TextAlign; import org.apache.fop.area.inline.Word; import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.Character; import java.util.ListIterator; import java.util.List; import java.util.Vector; import java.util.ArrayList; /** * BPLayoutManager for lines. It builds one or more lines containing * inline areas generated by its sub layout managers. */ public class LineBPLayoutManager extends LineLayoutManager { /** * Private class to store information about inline breaks. * Each value holds the start and end indexes into a List of * inline break positions. */ private static class LineBreakPosition implements BreakPoss.Position { int m_iPos; LineBreakPosition(int iBreakIndex) { m_iPos = iBreakIndex; } } private BPLayoutManager m_curChildLM=null; private ListIterator m_childLMiter; private LineArea m_lineArea; // LineArea currently being filled /** 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; private int m_iIndents = 0; public LineBPLayoutManager(FObj fobj, List lms, int lh, int l, int f) { super(fobj, lms, lh, l, f); m_childLMiter = lms.listIterator(); initProperties(); } protected void initProperties(PropertyManager propMgr) { super.initProperties(propMgr); System.err.println("LineBPLayoutManager.initProperties called"); MarginProps marginProps = propMgr.getMarginProps(); m_iIndents = marginProps.startIndent + marginProps.endIndent; BlockProps blockProps = propMgr.getBlockProps(); m_bJustify = (blockProps.textAlign == TextAlign.JUSTIFY); m_iTextIndent = blockProps.firstIndent; } /** * Return next child LayoutManager or null if there is none. * Note: child must implement BPLayoutManager! If it doesn't, skip it * and print a warning. * The list of all child layout managers is in lmList (in superclass!) */ private BPLayoutManager getChildLM() { if (m_curChildLM != null && !m_curChildLM.isFinished()) { return m_curChildLM; } while (m_childLMiter.hasNext()) { Object obj = m_childLMiter.next(); if (obj instanceof BPLayoutManager) { m_curChildLM = (BPLayoutManager)obj; m_curChildLM.setParentLM(this); return m_curChildLM; } else { m_childLMiter.remove(); System.err.println("WARNING: child of LineLPLayoutManager not a BPLayoutManager: " + obj.getClass().getName()); } } return null; } /** * Reset the layoutmanager "iterator" so that it will start * with the passed bplm on the next call to getChildLM. * @param bplm Reset iterator to this LayoutManager. */ private void resetChildLM(BPLayoutManager bplm) { if (bplm == null) return; while (m_curChildLM != bplm && m_childLMiter.hasPrevious()) { m_curChildLM = (BPLayoutManager)m_childLMiter.previous(); } if ( m_curChildLM.isFinished()) { m_curChildLM.setFinished(false); } } /** * Call child layout managers to generate content as long as they * generate inline areas. If a block-level generating LM is found, * finish any line being filled and return to the parent LM. */ public BreakPoss getNextBreakPoss(LayoutContext context, BreakPoss.Position prevLineBP) { // Get a break from currently active child LM // Set up constraints for inline level managers if ((context.flags & LayoutContext.CHECK_REF_AREA) != 0) { /* Return a BreakPoss indicating that higher level LM * (page) should check reference area and possibly * create a new one. */ return new BreakPoss(this, null, BreakPoss.NEED_IPD); } BPLayoutManager prevLM = null; // previous active LM BPLayoutManager curLM ; // currently active LM BreakPoss bp=null; // proposed BreakPoss // IPD remaining in line MinOptMax availIPD = context.stackLimit; // IPD of any unbreakable finished FO content MinOptMax pendingIPD = null; // QUESTION: maybe LayoutContext holds the Properties which // come from block-level? LayoutContext inlineLC = new LayoutContext(context); inlineLC.setPendingSpace(new SpaceSpecifier(true)); 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); } /* If first BP in this line but line is not first in this * LM and previous possible linebreak 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); } // GET NEXT POSSIBLE BREAK FROM CHILD LM if ((bp = curLM.getNextBreakPoss(inlineLC, (m_prevBP !=null ? m_prevBP.getPosition() : null))) != null) { // 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. */ bpDim.add(bp.resolveTrailingSpace(true)); } // 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. */ pendingIPD = bpDim; // Add BP to the pending list } } else if (bpDim.min > availIPD.max) { // This break position doesn't fit if (m_bJustify || m_prevBP == null) { // try to find a hyphenation point in the word // which spans the queued breaks and the proposed bp // Even if not justified, we must try to hyphenate if // there is no breakpoint at all up to this point! do { bp = findHyphenPoss(m_prevBP, bp); } while (bp != null && (bp.getStackingSize().min > availIPD.max)); if (bp == null) { // Couldn't find a hyphenation point. The line // will be "short". } else { m_prevBP = bp; } // Handle pendingIPD if any. The hyphenation point // may be within the "pending" content or after it. /* Make sure child LM are updated concerning the actual * hyphenation BreakPoss for their next call! */ } /* If we are not in justified text, we can end the line at * prevBP. */ 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; //??? } } // 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. */ } } // end of while on child LM if ((curLM = getChildLM())== null) { // No more content to layout! setFinished(true); } // Backup layoutmanager if necessary resetChildLM(m_prevBP.getLayoutManager()); return makeLineBreak(m_prevBP); } /** * Return whether we could end the line at the proposed Position. * TODO: take keeps into account and distinguish the cost of a * the break-completely forbidden or some non-0 cost. * QUESTION: do we need to pass the current LineLM or childLM * LayoutContext? */ private 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)); } } private BreakPoss findHyphenPoss(BreakPoss prevBP, BreakPoss newBP) { return null; } private BreakPoss makeLineBreak(BreakPoss inlineBP) { // make a new BP BreakPoss curLineBP = new BreakPoss(this, new LineBreakPosition(m_vecInlineBreaks.size()-1)); /* FIX ME!! * Need to calculate line height based on all inline BP info * for this line not just the current inlineBP! */ curLineBP.setFlag(BreakPoss.ISLAST, isFinished()); curLineBP.setStackingSize(inlineBP.getNonStackingSize()); return curLineBP; } // Generate and add areas to parent area // Set size etc public void addAreas(PositionIterator parentIter) { BPLayoutManager childLM ; int iStartPos = 0; while (parentIter.hasNext()) { LineBreakPosition lbp = (LineBreakPosition)parentIter.next(); System.err.println("lbp.endpos=" + lbp.m_iPos); m_lineArea = new LineArea(); // Add the inline areas to lineArea PositionIterator inlinePosIter = new BreakPossPosIter(m_vecInlineBreaks, iStartPos, lbp.m_iPos+1); iStartPos = lbp.m_iPos+1; while (inlinePosIter.hasNext() && (childLM = inlinePosIter.getNextChildLM())!= null) { childLM.addAreas(inlinePosIter); } verticalAlign(m_lineArea); parentLM.addChild(m_lineArea); } m_lineArea = null; } public boolean addChild(Area childArea) { // Make sure childArea is inline area if (childArea instanceof InlineArea) { m_lineArea.addInlineArea((InlineArea)childArea); } return false; } // NOTE: PATCHED FOR NOW TO ADD BreakPoss stuff to Kerion's changes public boolean generateAreas() { // Make break positions and return lines! // Set up a LayoutContext int ipd = 0; 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; while (!isFinished()) { if ((bp = getNextBreakPoss(childLC, null)) != null) { if (bp.checkIPD()) { // Need IPD in order to layout lines! // This is supposed to bubble up to PageLM to // make the necessary flow reference area, depending // on span and break-before flags set as the BreakPoss // makes its way back up the call stack. // Fake it for now! parentLM.getParentArea(null); ipd = parentLM.getContentIPD(); childLC.flags &= ~LayoutContext.CHECK_REF_AREA; childLC.stackLimit = new MinOptMax(ipd - m_iIndents - m_iTextIndent); } else { vecBreakPoss.add(bp); // Reset stackLimit for non-first lines childLC.stackLimit = new MinOptMax(ipd - m_iIndents); } } } addAreas(new BreakPossPosIter(vecBreakPoss, 0, vecBreakPoss.size())); return false; } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/PositionIterator.java Index: PositionIterator.java =================================================================== /* * $Id: PositionIterator.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import java.util.Iterator; import java.util.NoSuchElementException; abstract class PositionIterator implements Iterator { Iterator m_parentIter; Object m_nextObj; BPLayoutManager m_childLM; boolean m_bHasNext; PositionIterator(Iterator parentIter) { m_parentIter = parentIter; lookAhead(); //checkNext(); } BPLayoutManager getNextChildLM() { // Move to next "segment" of iterator, ie: new childLM if (m_childLM == null && m_nextObj != null) { m_childLM = getLM(m_nextObj); m_bHasNext = true; } return m_childLM; } abstract protected BPLayoutManager getLM(Object nextObj); abstract protected BreakPoss.Position getPos(Object nextObj); private void lookAhead() { if (m_parentIter.hasNext()) { m_bHasNext = true; m_nextObj = m_parentIter.next(); } else { endIter(); } } protected boolean checkNext() { BPLayoutManager lm = getLM(m_nextObj); if (m_childLM==null) { m_childLM = lm; } else if (m_childLM != lm) { // End of this sub-sequence with same child LM m_bHasNext = false; m_childLM = null; return false; } return true; } protected void endIter() { m_bHasNext = false; m_nextObj = null; m_childLM = null; } public boolean hasNext() { return (m_bHasNext && checkNext()); } public Object next() throws NoSuchElementException { if (m_bHasNext) { Object retObj = getPos(m_nextObj); lookAhead(); return retObj; } else { throw new NoSuchElementException("PosIter"); } } public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException("PositionIterator doesn't support remove"); } } 1.1 xml-fop/src/org/apache/fop/layoutmgr/TextBPLayoutManager.java Index: TextBPLayoutManager.java =================================================================== /* * $Id: TextBPLayoutManager.java,v 1.1 2002/04/28 21:31:00 klease Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.layoutmgr; import org.apache.fop.fo.FObj; import org.apache.fop.fo.TextInfo; import org.apache.fop.traits.SpaceVal; import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; import org.apache.fop.area.MinOptMax; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.Word; import org.apache.fop.area.inline.Space; import org.apache.fop.util.CharUtilities; import org.apache.fop.fo.properties.VerticalAlign; //import org.apache.fop.fo.properties.*; import java.util.Vector; // or use ArrayList ??? /** * LayoutManager for text (a sequence of characters) which generates one * or more inline areas. */ public class TextBPLayoutManager extends AbstractBPLayoutManager { /** * Private class to store information about the break index. * the field stores the index in the vector of AreaInfo which * corresponds to this break position. * Note: fields are directly readable in this class */ private static class TextBreakPosition implements BreakPoss.Position { short m_iAreaIndex; TextBreakPosition(int iAreaIndex) { m_iAreaIndex = (short)iAreaIndex; } } /** * Store information about each potential word area. * Index of character which ends the area, IPD of area, including * any word-space and letter-space. * Number of word-spaces? */ private class AreaInfo { short m_iStartIndex; short m_iBreakIndex; MinOptMax m_ipdArea; AreaInfo(short iStartIndex, short iBreakIndex, MinOptMax ipdArea) { m_iStartIndex = iStartIndex; m_iBreakIndex = iBreakIndex; m_ipdArea = ipdArea; } } // Hold all possible breaks for the text in this LM's FO. private Vector m_vecAreaInfo; /** Non-space characters on which we can end a line. */ static private final String s_breakChars = "-/" ; private char[] chars; private TextInfo textInfo; private static final char NEWLINE = '\n'; private static final char RETURN = '\r'; private static final char TAB = '\t'; private static final char SPACE = ' '; private static final char LINEBREAK = '\u2028'; private static final char ZERO_WIDTH_SPACE = '\u200B'; // byte order mark private static final char ZERO_WIDTH_NOBREAK_SPACE = '\uFEFF'; /* values that prev (below) may take */ protected static final int NOTHING = 0; protected static final int WHITESPACE = 1; protected static final int TEXT = 2; /** Start index of first character in this parent Area */ private short m_iAreaStart = 0; /** Start index of next "word" */ private short m_iNextStart = 0; /** Size since last makeArea call, except for last break */ private MinOptMax m_ipdTotal ; /** Size including last break possibility returned */ // private MinOptMax m_nextIPD= new MinOptMax(0); /** size of a space character (U+0020) glyph in current font */ private int m_spaceIPD; /** 1/2 of word-spacing value */ private SpaceVal m_halfWS; /** Number of space characters after previous possible break position. */ private int m_iNbSpacesPending; public TextBPLayoutManager(FObj fobj, char[] chars, TextInfo textInfo) { super(fobj); this.chars = chars; this.textInfo = textInfo; this.m_vecAreaInfo = new Vector(chars.length/5); // Guess // With CID fonts, space isn't neccesary currentFontState.width(32) m_spaceIPD = CharUtilities.getCharWidth(' ', textInfo.fs); // Make half-space: <space> on either side of a word-space) SpaceVal ws = textInfo.wordSpacing; m_halfWS = new SpaceVal(MinOptMax.multiply(ws.space, 0.5), ws.bConditional, ws.bForcing, ws.iPrecedence); } public boolean generatesInlineAreas() { return true; } /* METHODS FROM LeafNodeLayoutManager, * used in Keiron's implemenation, but not here (yet at least). */ public int size() { return 0; } public InlineArea get(int index) { return null; } /** * Generate inline areas for words in text. */ public boolean generateAreas() { // Handle white-space characteristics. Maybe there is no area to // generate.... // Iterate over characters and make text areas. // Add each one to parent. Handle word-space. return false; } // NOTE: currently not used. Remove if decide it isn't necessary! // /** // * Get the BreakPoss at the start of the next line. // * @param bpPrevEnd The BreakPoss at the end of the previous line // * or null if we should return the point at the beginning of this // * text run. // */ // public BreakPoss getStartBreakPoss(LayoutContext lc, // BreakPoss.Position bpPrevEnd) { // BreakPoss bp = null; // if (bpPrevEnd == null) { // bp = new BreakPoss(this, new TextBreakPosition(0)); // // Set minimum bpd (character ascent and descent) // // Or do this at the line level??? // } // else { // // Skip suppressible white-space // // ASSERT (((TextBreakPosition)bpPrevEnd).m_iAreaIndex = // // m_iNextStart) // if ((lc.flags & LayoutContext.SUPPRESS_LEADING_SPACE)!=0) { // /* Skip any leading word-space characters. */ // for (; m_iNextStart < chars.length && // chars[m_iNextStart]==SPACE; m_iNextStart++); // } // // If now at end, nothing to compose here! // if (m_iNextStart >= chars.length) { // return null; // Or an "empty" BreakPoss? // } // else { // bp = new BreakPoss(this, // new TextBreakPosition(m_iNextStart)); // } // } // return bp; // } /** * Return value indicating whether the next area to be generated could * start a new line. This should only be called in the "START" condition * if a previous inline BP couldn't end the line. * Return true if the first character is a potential linebreak character. */ public boolean canBreakBefore(LayoutContext context) { char c = chars[m_iNextStart]; return ((c == NEWLINE) || ((context.flags & LayoutContext.LINEBREAK_AT_LF_ONLY)==0 && (CharUtilities.isSpace(c) || s_breakChars.indexOf(c)>=0))); } /** * Return the next break possibility that fits the constraints. * @param context An object specifying the flags and input information * concerning the context of the BreakPoss. * @para prevPos An object specifying the previous Position returned * by a BreakPoss from this LM. It may be earlier than the current * pointer when doing hyphenation or starting a new line. * @return BreakPoss An object containing information about the next * legal break position or the end of the text run if no break * was found. * <p>Assumptions: white-space-treatment and * linefeed-treatment processing * are already done, so there are no TAB or RETURN characters remaining. * white-space-collapse handling is also done * (but perhaps this shouldn't be true!) * There may be LINEFEED characters if they weren't converted * into spaces. A LINEFEED always forces a break. */ public BreakPoss getNextBreakPoss(LayoutContext context, BreakPoss.Position prevPos) { /* On first call in a new Line, the START_AREA * flag in LC is set. */ int iFlags = 0; if ((context.flags & LayoutContext.START_AREA)!=0) { /* 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; } if (prevPos != null) { TextBreakPosition tbp = (TextBreakPosition)prevPos; AreaInfo ai = (AreaInfo) m_vecAreaInfo.elementAt(tbp.m_iAreaIndex); if (ai.m_iBreakIndex != m_iNextStart) { m_iNextStart = ai.m_iBreakIndex; m_vecAreaInfo.setSize(tbp.m_iAreaIndex+1); System.err.println("Discarded previous text break pos"); } } // HANDLE SUPPRESSED LEADING SPACES if ((context.flags & LayoutContext.SUPPRESS_LEADING_SPACE)!=0) { /* If any leading space characters, ignore them. */ // NOTE: Skips word-space chars only, not other white-space! for (; m_iNextStart < chars.length && chars[m_iNextStart]==SPACE; m_iNextStart++); // If now at end, nothing to compose here! if (m_iNextStart >= chars.length) { return null; // Or an "empty" BreakPoss? } } // Start of this "word", plus any non-suppressed leading space // This is any kind of white-space, not just word spaces short iThisStart = m_iNextStart; MinOptMax spaceIPD = new MinOptMax(0); // Variable IPD int wordIPD = 0; // Non-stretching IPD (length in base units) // Handle inter-character spacing (word-space + letter-space) // What about context.getPendingSpace() on first char in word? SpaceSpecifier pendingSpace = new SpaceSpecifier(false); for (; m_iNextStart < chars.length; m_iNextStart++) { char c = chars[m_iNextStart]; if (CharUtilities.isAnySpace(c)==false) break; if (c==SPACE) { pendingSpace.addSpace(m_halfWS); spaceIPD.add(pendingSpace.resolve(false)); wordIPD += m_spaceIPD; // Space glyph IPD pendingSpace.clear(); pendingSpace.addSpace(m_halfWS); } else { // If we have letter-space, so we apply this to fixed- // width spaces (which are not word-space) also? spaceIPD.add(pendingSpace.resolve(false)); pendingSpace.clear(); wordIPD += CharUtilities.getCharWidth(c, textInfo.fs); } } if (m_iNextStart < chars.length) { spaceIPD.add(pendingSpace.resolve(false)); } else { // This FO ended with spaces. Return the BP iFlags |= BreakPoss.ALL_ARE_SUPPRESS_AT_LB; // lc.trailingSpaceSeq.addSpace(m_halfWS); // Need to make SpaceSpecifier from m_halfWS! // Or at least a spaceval return makeBreakPoss(iThisStart, spaceIPD, 0, pendingSpace, iFlags); } // Look for a legal line-break: breakable white-space and certain // characters such as '-' which can serve as word breaks. // Don't look for hyphenation points here though for (; m_iNextStart < chars.length; m_iNextStart++) { char c = chars[m_iNextStart]; if ((c == NEWLINE) || // Include any breakable white-space as break char // even if fixed width (textInfo.bWrap && (CharUtilities.isSpace(c) || s_breakChars.indexOf(c)>=0))) { iFlags |= BreakPoss.CAN_BREAK_AFTER; if (c != SPACE) { m_iNextStart++; if (c != NEWLINE) { wordIPD += CharUtilities.getCharWidth(c, textInfo.fs); } else { iFlags |= BreakPoss.FORCE; } } return makeBreakPoss(iThisStart, spaceIPD, wordIPD, null, iFlags); } wordIPD += CharUtilities.getCharWidth(c, textInfo.fs); // Note, if a normal non-breaking space, is it stretchable??? // If so, keep a count of these embedded spaces. } return makeBreakPoss(iThisStart, spaceIPD, wordIPD, null, iFlags); } private BreakPoss makeBreakPoss(short iWordStart, MinOptMax spaceIPD, int wordDim, SpaceSpecifier trailingSpace, int flags) { MinOptMax ipd = new MinOptMax(wordDim); ipd.add(spaceIPD); // Position is the index of the info for this word in the vector m_vecAreaInfo.add(new AreaInfo(iWordStart, m_iNextStart, ipd)); BreakPoss bp = new BreakPoss(this, new TextBreakPosition(m_vecAreaInfo.size()-1)); ipd.add(m_ipdTotal); // sum of all words so far in line bp.setStackingSize(ipd); m_ipdTotal = ipd; // TODO: make this correct (see Keiron's code below!) bp.setNonStackingSize(new MinOptMax(textInfo.lineHeight)); /* Set max ascender and descender (offset from baseline), * used for calculating the bpd of the line area containing * this text. */ //bp.setDescender(textInfo.fs.getDescender()); //bp.setAscender(textInfo.fs.getAscender()); if (m_iNextStart == chars.length) { flags |= BreakPoss.ISLAST; setFinished(true); } bp.setFlag(flags); if (trailingSpace != null) { bp.setTrailingSpace(trailingSpace); } return bp; } /** * Add an area for each word and space (or one big one????) */ public void addAreas(PositionIterator posIter) { // Add word areas TextBreakPosition tbpStart, tbpNext; while (posIter.hasNext()) { tbpNext = (TextBreakPosition)posIter.next(); // System.err.println("tbp.pos = " + tbpNext.m_iAreaIndex); AreaInfo ai = (AreaInfo)m_vecAreaInfo. elementAt(tbpNext.m_iAreaIndex); // Make an area containing all characters between start and end. Word word = createWord(new String(chars, ai.m_iStartIndex, ai.m_iBreakIndex- ai.m_iStartIndex), ai.m_ipdArea.opt); parentLM.addChild(word); } } protected Word createWord(String str, int width) { Word curWordArea = new Word(); curWordArea.setWidth(width); curWordArea.setHeight(textInfo.fs.getAscender() - textInfo.fs.getDescender()); curWordArea.setOffset(textInfo.fs.getAscender()); curWordArea.info = new LayoutInfo(); curWordArea.info.lead = textInfo.fs.getAscender(); curWordArea.info.alignment = VerticalAlign.BASELINE; curWordArea.info.blOffset = true; curWordArea.setWord(str); Trait prop = new Trait(); prop.propType = Trait.FONT_STATE; prop.data = textInfo.fs; curWordArea.addTrait(prop); return curWordArea; } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]