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]

Reply via email to