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]