Browse Source

Patch 31206 by Luca Furini:

- A new interface InlineLevelLayoutManager, implemented by LeafNodeLM,
  InlineLM, TextLM, BasicLinkLM and ContentLM

- Leaders with pattern "use-content" and "dots" now works

- Better handling of preserved linefeeds

- Correct calculation of the lineheight, according to the value of the
  property vertical-align

For a few points which may need further work, see the bugzilla page.


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198142 13f79535-47bb-0310-9956-ffa450edef68
pull/30/head
Simon Pepping 19 years ago
parent
commit
609da8edaa

+ 24
- 0
src/java/org/apache/fop/area/inline/FilledArea.java View File

@@ -19,6 +19,7 @@
package org.apache.fop.area.inline;

import java.util.List;
import java.util.ListIterator;
import java.util.ArrayList;

/**
@@ -39,6 +40,29 @@ public class FilledArea extends InlineParent {
public FilledArea() {
}

/**
* Set the offset of the descendant TextAreas,
* instead of the offset of the FilledArea itself.
*
* @param v the offset
*/
public void setOffset(int v) {
setChildOffset(inlines.listIterator(), v);
}

private void setChildOffset(ListIterator childrenIterator, int v) {
while (childrenIterator.hasNext()) {
InlineArea child = (InlineArea) childrenIterator.next();
if (child instanceof InlineParent) {
setChildOffset(((InlineParent) child).getChildAreas().listIterator(), v);
} else if (child instanceof org.apache.fop.area.inline.Viewport) {
// nothing
} else {
child.setOffset(v);
}
}
}

/**
* Set the unit width for the areas to fill the full width.
*

+ 9
- 0
src/java/org/apache/fop/fo/flow/Character.java View File

@@ -84,6 +84,7 @@ public class Character extends FObj {
private int textDecoration;
// private ToBeImplementedProperty textShadow;
private int textTransform;
private int verticalAlign;
private int visibility;
private Property wordSpacing;
// End of property values
@@ -132,6 +133,7 @@ public class Character extends FObj {
textDecoration = pList.get(PR_TEXT_DECORATION).getEnum();
// textShadow = pList.get(PR_TEXT_SHADOW);
textTransform = pList.get(PR_TEXT_TRANSFORM).getEnum();
verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
visibility = pList.get(PR_VISIBILITY).getEnum();
wordSpacing = pList.get(PR_WORD_SPACING);
}
@@ -224,6 +226,13 @@ public class Character extends FObj {
return wordSpacing;
}

/**
* Return the "vertical-align" property.
*/
public int getVerticalAlign() {
return verticalAlign;
}

/**
* @see org.apache.fop.fo.FONode#addLayoutManager(List)
*/

+ 9
- 0
src/java/org/apache/fop/fo/flow/Inline.java View File

@@ -66,6 +66,7 @@ public class Inline extends FObjMixed {
private KeepProperty keepWithPrevious;
private Length lineHeight;
private int textDecoration;
private int verticalAlign;
private int visibility;
private Length width;
private int wrapOption;
@@ -106,6 +107,7 @@ public class Inline extends FObjMixed {
keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
lineHeight = pList.get(PR_LINE_HEIGHT).getLength();
textDecoration = pList.get(PR_TEXT_DECORATION).getEnum();
verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
visibility = pList.get(PR_VISIBILITY).getEnum();
width = pList.get(PR_WIDTH).getLength();
wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
@@ -214,6 +216,13 @@ public class Inline extends FObjMixed {
return textDecoration;
}

/**
* Return the "vertical-align" property.
*/
public int getVerticalAlign() {
return verticalAlign;
}

/**
* @see org.apache.fop.fo.FObjMixed#charIterator
*/

+ 24
- 1
src/java/org/apache/fop/fo/flow/Leader.java View File

@@ -54,6 +54,7 @@ public class Leader extends FObjMixed {
private CommonRelativePosition commonRelativePosition;
private Length alignmentAdjust;
private int alignmentBaseline;
private int verticalAlign;
private Length baselineShift;
private ColorType color;
private int dominantBaseline;
@@ -94,6 +95,7 @@ public class Leader extends FObjMixed {
commonRelativePosition = pList.getRelativePositionProps();
alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength();
alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum();
verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
baselineShift = pList.get(PR_BASELINE_SHIFT).getLength();
color = pList.get(PR_COLOR).getColorType();
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
@@ -136,7 +138,21 @@ public class Leader extends FObjMixed {
protected void startOfNode() throws FOPException {
checkId(id);
}

/**
* Return the Common Margin Properties-Inline.
*/
public CommonMarginInline getCommonMarginInline() {
return commonMarginInline;
}

/**
* Return the Common Border, Padding, and Background Properties.
*/
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return commonBorderPaddingBackground;
}

/**
* Return the Common Font Properties.
*/
@@ -193,6 +209,13 @@ public class Leader extends FObjMixed {
return leaderPatternWidth;
}

/**
* Return the "vertical-align" property.
*/
public int getVerticalAlign() {
return verticalAlign;
}

/**
* @see org.apache.fop.fo.FONode#addLayoutManager(List)
*/

+ 0
- 37
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java View File

@@ -275,43 +275,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
* interface which are declared abstract in AbstractLayoutManager.
* ---------------------------------------------------------*/

public LinkedList getNextKnuthElements(LayoutContext context,
int alignment) {
log.debug("null implementation of getNextKnuthElements() called!");
setFinished(true);
return null;
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
log.debug("null implementation of addALetterSpaceTo() called!");
return element;
}

public void getWordChars(StringBuffer sbChars, Position pos) {
log.debug("null implementation of getWordChars() called!");
}

public void hyphenate(Position pos, HyphContext hc) {
log.debug("null implementation of hyphenate called!");
}

public boolean applyChanges(List oldList) {
log.debug("null implementation of applyChanges() called!");
return false;
}

public LinkedList getChangedKnuthElements(List oldList,
int flaggedPenalty,
int alignment) {
log.debug("null implementation of getChangeKnuthElement() called!");
return null;
}

public int getWordSpaceIPD() {
log.debug("null implementation of getWordSpaceIPD() called!");
return 0;
}

public Area getParentArea(Area childArea) {
return null;
}

+ 1
- 1
src/java/org/apache/fop/layoutmgr/BasicLinkLayoutManager.java View File

@@ -27,7 +27,7 @@ import org.apache.fop.area.PageViewport;
/**
* LayoutManager for the fo:basic-link formatting object
*/
public class BasicLinkLayoutManager extends InlineStackingLayoutManager {
public class BasicLinkLayoutManager extends InlineLayoutManager {
private BasicLink fobj;
/**

+ 3
- 1
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java View File

@@ -57,6 +57,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
private int lead = 12000;
private int lineHeight = 14000;
private int follow = 2000;
private int middleShift = 0;

private int iStartPos = 0;

@@ -71,6 +72,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
lead = fs.getAscender();
follow = -fs.getDescender();
middleShift = -fs.getXHeight() / 2;
lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue();
}

@@ -141,7 +143,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
*/
private LineLayoutManager createLineManager(LayoutManager firstlm) {
LineLayoutManager llm;
llm = new LineLayoutManager(fobj, lineHeight, lead, follow);
llm = new LineLayoutManager(fobj, lineHeight, lead, follow, middleShift);
List inlines = new ArrayList();
inlines.add(firstlm);
while (proxyLMiter.hasNext()) {

+ 10
- 10
src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java View File

@@ -48,6 +48,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
fobj = node;
InlineArea inline = getCharacterInlineArea(node);
setCurrentArea(inline);
setAlignment(fobj.getVerticalAlign());
fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());

SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing());
@@ -71,15 +72,15 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
*/
protected void offsetArea(LayoutContext context) {
int bpd = curArea.getBPD();
switch (alignment) {
switch (verticalAlignment) {
case VerticalAlign.MIDDLE:
curArea.setOffset(context.getBaseline() - bpd / 2 /* - fontLead/2 */);
curArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
break;
case VerticalAlign.TOP:
//curArea.setOffset(0);
curArea.setOffset(fs.getAscender());
break;
case VerticalAlign.BOTTOM:
curArea.setOffset(context.getLineHeight() - bpd);
curArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
break;
case VerticalAlign.BASELINE:
default:
@@ -116,16 +117,15 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
int lead = 0;
int total = 0;
int middle = 0;
switch (alignment) {
switch (verticalAlignment) {
case VerticalAlign.MIDDLE : middle = bpd / 2 ;
lead = bpd / 2 ;
break;
case VerticalAlign.TOP : total = bpd;
break;
case VerticalAlign.TOP : // fall through
case VerticalAlign.BOTTOM : total = bpd;
break;
case VerticalAlign.BASELINE:
default: lead = bpd;
case VerticalAlign.BASELINE: // fall through
default : lead = fs.getAscender();
total = bpd;
break;
}


+ 38
- 11
src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java View File

@@ -22,6 +22,7 @@ import org.apache.fop.fo.FObj;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.area.Area;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.Resolvable;
import org.apache.fop.area.PageViewport;

@@ -40,12 +41,12 @@ import org.apache.commons.logging.LogFactory;
* For use with objects that contain inline areas such as
* leader use-content and title.
*/
public class ContentLayoutManager implements LayoutManager {
public class ContentLayoutManager implements InlineLevelLayoutManager {
private FOUserAgent userAgent;
private Area holder;
private int stackSize;
private LayoutManager parentLM;
private List childLMs = new ArrayList(1);
private InlineLevelLayoutManager childLM = null;

/**
* logging instance
@@ -137,6 +138,19 @@ public class ContentLayoutManager implements LayoutManager {
stackSize = stack.opt;
}

public void addAreas(PositionIterator posIter, LayoutContext context) {
// add the content areas
// the area width has already been adjusted, and it must remain unchanged
// so save its value before calling addAreas, and set it again afterwards
int savedIPD = ((InlineArea)holder).getIPD();
// set to zero the ipd adjustment ratio, to avoid spaces in the pattern
// to be modified
LayoutContext childContext = new LayoutContext(context);
childContext.setIPDAdjust(0.0);
childLM.addAreas(posIter, childContext);
((InlineArea)holder).setIPD(savedIPD);
}

public int getStackingSize() {
return stackSize;
}
@@ -201,9 +215,6 @@ public class ContentLayoutManager implements LayoutManager {
//to be done
}

/** @see org.apache.fop.layoutmgr.LayoutManager */
public void addAreas(PositionIterator posIter, LayoutContext context) { }

/** @see org.apache.fop.layoutmgr.LayoutManager */
public void initialize() {
//to be done
@@ -259,6 +270,8 @@ public class ContentLayoutManager implements LayoutManager {
* @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs
*/
public List getChildLMs() {
List childLMs = new ArrayList(1);
childLMs.add(childLM);
return childLMs;
}

@@ -271,7 +284,7 @@ public class ContentLayoutManager implements LayoutManager {
}
lm.setParent(this);
lm.initialize();
childLMs.add(lm);
childLM = (InlineLevelLayoutManager)lm;
log.trace(this.getClass().getName()
+ ": Adding child LM " + lm.getClass().getName());
}
@@ -292,8 +305,26 @@ public class ContentLayoutManager implements LayoutManager {

public LinkedList getNextKnuthElements(LayoutContext context,
int alignment) {
LinkedList contentList = new LinkedList();
LinkedList returnedList;

while (!childLM.isFinished()) {
// get KnuthElements from childLM
returnedList = childLM.getNextKnuthElements(context, alignment);

if (returnedList != null) {
// move elements to contentList, and accumulate their size
KnuthElement contentElement;
while (returnedList.size() > 0) {
contentElement = (KnuthElement)returnedList.removeFirst();
stackSize += contentElement.getW();
contentList.add(contentElement);
}
}
}

setFinished(true);
return null;
return contentList;
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
@@ -315,9 +346,5 @@ public class ContentLayoutManager implements LayoutManager {
int alignment) {
return null;
}

public int getWordSpaceIPD() {
return 0;
}
}


+ 227
- 5
src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java View File

@@ -18,8 +18,13 @@

package org.apache.fop.layoutmgr;

import java.util.List;
import java.util.ListIterator;
import java.util.LinkedList;

import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Inline;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonMarginInline;
import org.apache.fop.fo.properties.SpaceProperty;
@@ -30,8 +35,9 @@ import org.apache.fop.traits.SpaceVal;
* LayoutManager for objects which stack children in the inline direction,
* such as Inline or Line
*/
public class InlineLayoutManager extends InlineStackingLayoutManager {
private Inline fobj;
public class InlineLayoutManager extends InlineStackingLayoutManager
implements InlineLevelLayoutManager {
private FObj fobj;

private CommonMarginInline inlineProps = null;
private CommonBorderPaddingBackground borderProps = null;
@@ -47,13 +53,31 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
super(node);
fobj = node;
}

/**
* Create an inline layout manager.
* This is used for fo's that create areas that
* contain inline areas.
*
* @param node the formatting object that creates the area
*/
public InlineLayoutManager(Leader node) {
super(node);
fobj = node;
}
/**
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
*/
protected void initProperties() {
inlineProps = fobj.getCommonMarginInline();
borderProps = fobj.getCommonBorderPaddingBackground();
// fobj can be either an Inline or a Leader
if (fobj instanceof Inline) {
inlineProps = ((Inline) fobj).getCommonMarginInline();
borderProps = ((Inline) fobj).getCommonBorderPaddingBackground();
} else {
inlineProps = ((Leader) fobj).getCommonMarginInline();
borderProps = ((Leader) fobj).getCommonBorderPaddingBackground();
}
int iPad = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false);
iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
false);
@@ -94,7 +118,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
return inlineProps.spaceEnd;
}

/**
* Return value indicating whether the next area to be generated could
* start a new line. This should only be called in the "START" condition
@@ -121,5 +144,204 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
}
}

public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
InlineLevelLayoutManager curLM;

// the list returned by child LM
LinkedList returnedList;
KnuthElement returnedElement;

// the list which will be returned to the parent LM
LinkedList returnList = new LinkedList();

SpaceSpecifier leadingSpace = lc.getLeadingSpace();

if (lc.startsNewArea()) {
// First call to this LM in new parent "area", but this may
// not be the first area created by this inline
childLC = new LayoutContext(lc);
if (getSpaceStart() != null) {
lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
}

// Check for "fence"
if (hasLeadingFence(!lc.isFirstArea())) {
// Reset leading space sequence for child areas
leadingSpace = new SpaceSpecifier(false);
}
// Reset state variables
clearPrevIPD(); // Clear stored prev content dimensions
}

while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
// get KnuthElements from curLM
returnedList = curLM.getNextKnuthElements(lc, alignment);
if (returnedList != null) {
// "wrap" the Position stored in each element of returnedList
ListIterator listIter = returnedList.listIterator();
while (listIter.hasNext()) {
returnedElement = (KnuthElement) listIter.next();
returnedElement.setPosition
(new NonLeafPosition(this,
returnedElement.getPosition()));
returnList.add(returnedElement);
}
return returnList;
} else {
// curLM returned null because it finished;
// just iterate once more to see if there is another child
}
}
setFinished(true);
return null;
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
element.setPosition(savedPos.getPosition());

KnuthElement newElement
= ((InlineLevelLayoutManager)
element.getLayoutManager()).addALetterSpaceTo(element);
newElement.setPosition
(new NonLeafPosition(this, newElement.getPosition()));
element.setPosition(savedPos);
return newElement;
}

public void getWordChars(StringBuffer sbChars, Position pos) {
Position newPos = ((NonLeafPosition) pos).getPosition();
((InlineLevelLayoutManager)
newPos.getLM()).getWordChars(sbChars, newPos);
}

public void hyphenate(Position pos, HyphContext hc) {
Position newPos = ((NonLeafPosition) pos).getPosition();
((InlineLevelLayoutManager)
newPos.getLM()).hyphenate(newPos, hc);
}

public boolean applyChanges(List oldList) {
// "unwrap" the Positions stored in the elements
ListIterator oldListIterator = oldList.listIterator();
KnuthElement oldElement;
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();

InlineLevelLayoutManager prevLM = null;
InlineLevelLayoutManager currLM;
int fromIndex = 0;

boolean bSomethingChanged = false;
while(oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
// initialize prevLM
if (prevLM == null) {
prevLM = currLM;
}

if (currLM != prevLM || !oldListIterator.hasNext()) {
if (oldListIterator.hasNext()) {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
|| bSomethingChanged;
prevLM = currLM;
fromIndex = oldListIterator.previousIndex();
} else if (currLM == prevLM) {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
|| bSomethingChanged;
} else {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
|| bSomethingChanged;
bSomethingChanged
= currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
|| bSomethingChanged;
}
}
}

// "wrap" again the Positions stored in the elements
oldListIterator = oldList.listIterator();
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(new NonLeafPosition(this, oldElement.getPosition()));
}
return bSomethingChanged;
}

public LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty, int alignment) {
// "unwrap" the Positions stored in the elements
ListIterator oldListIterator = oldList.listIterator();
KnuthElement oldElement;
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();

KnuthElement returnedElement;
LinkedList returnedList = new LinkedList();
LinkedList returnList = new LinkedList();
InlineLevelLayoutManager prevLM = null;
InlineLevelLayoutManager currLM;
int fromIndex = 0;

while(oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
if (prevLM == null) {
prevLM = currLM;
}

if (currLM != prevLM || !oldListIterator.hasNext()) {
if (oldListIterator.hasNext()) {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex,
oldListIterator.previousIndex()),
flaggedPenalty, alignment));
prevLM = currLM;
fromIndex = oldListIterator.previousIndex();
} else if (currLM == prevLM) {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex, oldList.size()),
flaggedPenalty, alignment));
} else {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex,
oldListIterator.previousIndex()),
flaggedPenalty, alignment));
returnedList.addAll
(currLM.getChangedKnuthElements
(oldList.subList(oldListIterator.previousIndex(),
oldList.size()),
flaggedPenalty, alignment));
}
}
}

// "wrap" the Position stored in each element of returnedList
ListIterator listIter = returnedList.listIterator();
while (listIter.hasNext()) {
returnedElement = (KnuthElement) listIter.next();
returnedElement.setPosition
(new NonLeafPosition(this, returnedElement.getPosition()));
returnList.add(returnedElement);
}
return returnList;
}
}


+ 84
- 0
src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java View File

@@ -0,0 +1,84 @@
/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */
package org.apache.fop.layoutmgr;

import java.util.LinkedList;
import java.util.List;

/**
* The interface for LayoutManagers which generate inline areas
*/
public interface InlineLevelLayoutManager extends LayoutManager {

/**
* Get a sequence of KnuthElements representing the content
* of the node assigned to the LM
*
* @param context the LayoutContext used to store layout information
* @param alignment the desired text alignement
* @return the list of KnuthElements
*/
LinkedList getNextKnuthElements(LayoutContext context, int alignment);

/**
* Tell the LM to modify its data, adding a letter space
* to the word fragment represented by the given element,
* and returning a corrected element
*
* @param element the element which must be given one more letter space
* @return the new element replacing the old one
*/
KnuthElement addALetterSpaceTo(KnuthElement element);

/**
* Get the word chars corresponding to the given position
*
* @param sbChars the StringBuffer used to append word chars
* @param pos the Position referring to the needed word chars
*/
void getWordChars(StringBuffer sbChars, Position pos);

/**
* Tell the LM to hyphenate a word
*
* @param pos the Position referring to the word
* @param hc the HyphContext storing hyphenation information
*/
void hyphenate(Position pos, HyphContext hc);

/**
* Tell the LM to apply the changes due to hyphenation
*
* @param oldList the list of the old elements the changes refer to
* @return true if the LM had to change its data, false otherwise
*/
boolean applyChanges(List oldList);

/**
* Get a sequence of KnuthElements representing the content
* of the node assigned to the LM, after changes have been applied
*
* @param oldList the elements to replace
* @param flaggedPenalty the penalty value for hyphenated lines
* @param alignment the desired text alignment
* @return the updated list of KnuthElements
*/
LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
int alignment);
}

+ 4
- 209
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java View File

@@ -69,7 +69,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
private Area currentArea; // LineArea or InlineParent

private BreakPoss prevBP;
private LayoutContext childLC ;
protected LayoutContext childLC ;

private LayoutManager lastChildLM = null; // Set when return last breakposs
private boolean bAreaCreated = false;
@@ -462,8 +462,9 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
= new StackingIter(positionList.listIterator());

LayoutManager prevLM = null;
LayoutManager childLM ;
while ((childLM = childPosIter.getNextChildLM()) != null) {
InlineLevelLayoutManager childLM ;
while ((childLM = (InlineLevelLayoutManager) childPosIter.getNextChildLM())
!= null) {
getContext().setFlags(LayoutContext.LAST_AREA,
context.isLastArea() && childLM == lastLM);
childLM.addAreas(childPosIter, getContext());
@@ -557,211 +558,5 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
}
}
}

public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
LayoutManager curLM;

// the list returned by child LM
LinkedList returnedList;
KnuthElement returnedElement;

// the list which will be returned to the parent LM
LinkedList returnList = new LinkedList();

SpaceSpecifier leadingSpace = lc.getLeadingSpace();

if (lc.startsNewArea()) {
// First call to this LM in new parent "area", but this may
// not be the first area created by this inline
childLC = new LayoutContext(lc);
if (getSpaceStart() != null) {
lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
}

// Check for "fence"
if (hasLeadingFence(!lc.isFirstArea())) {
// Reset leading space sequence for child areas
leadingSpace = new SpaceSpecifier(false);
}
// Reset state variables
clearPrevIPD(); // Clear stored prev content dimensions
}

while ((curLM = getChildLM()) != null) {
// get KnuthElements from curLM
returnedList = curLM.getNextKnuthElements(lc, alignment);
if (returnedList != null) {
// "wrap" the Position stored in each element of returnedList
ListIterator listIter = returnedList.listIterator();
while (listIter.hasNext()) {
returnedElement = (KnuthElement) listIter.next();
returnedElement.setPosition
(new NonLeafPosition(this,
returnedElement.getPosition()));
returnList.add(returnedElement);
}
return returnList;
} else {
// curLM returned null because it finished;
// just iterate once more to see if there is another child
}
}
setFinished(true);
return null;
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
element.setPosition(savedPos.getPosition());

KnuthElement newElement
= element.getLayoutManager().addALetterSpaceTo(element);
newElement.setPosition
(new NonLeafPosition(this, newElement.getPosition()));
element.setPosition(savedPos);
return newElement;
}

public void getWordChars(StringBuffer sbChars, Position pos) {
Position newPos = ((NonLeafPosition) pos).getPosition();
newPos.getLM().getWordChars(sbChars, newPos);
}

public void hyphenate(Position pos, HyphContext hc) {
Position newPos = ((NonLeafPosition) pos).getPosition();
newPos.getLM().hyphenate(newPos, hc);
}

public boolean applyChanges(List oldList) {
// "unwrap" the Positions stored in the elements
ListIterator oldListIterator = oldList.listIterator();
KnuthElement oldElement;
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();

LayoutManager prevLM = null;
LayoutManager currLM;
int fromIndex = 0;

boolean bSomethingChanged = false;
while(oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
currLM = oldElement.getLayoutManager();
// initialize prevLM
if (prevLM == null) {
prevLM = currLM;
}

if (currLM != prevLM || !oldListIterator.hasNext()) {
if (oldListIterator.hasNext()) {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
|| bSomethingChanged;
prevLM = currLM;
fromIndex = oldListIterator.previousIndex();
} else if (currLM == prevLM) {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
|| bSomethingChanged;
} else {
bSomethingChanged
= prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
|| bSomethingChanged;
bSomethingChanged
= currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
|| bSomethingChanged;
}
}
}

// "wrap" again the Positions stored in the elements
oldListIterator = oldList.listIterator();
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(new NonLeafPosition(this, oldElement.getPosition()));
}
return bSomethingChanged;
}

public LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty, int alignment) {
// "unwrap" the Positions stored in the elements
ListIterator oldListIterator = oldList.listIterator();
KnuthElement oldElement;
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();

KnuthElement returnedElement;
LinkedList returnedList = new LinkedList();
LinkedList returnList = new LinkedList();
LayoutManager prevLM = null;
LayoutManager currLM;
int fromIndex = 0;

while(oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
currLM = oldElement.getLayoutManager();
if (prevLM == null) {
prevLM = currLM;
}

if (currLM != prevLM || !oldListIterator.hasNext()) {
if (oldListIterator.hasNext()) {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex,
oldListIterator.previousIndex()),
flaggedPenalty, alignment));
prevLM = currLM;
fromIndex = oldListIterator.previousIndex();
} else if (currLM == prevLM) {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex, oldList.size()),
flaggedPenalty, alignment));
} else {
returnedList.addAll
(prevLM.getChangedKnuthElements
(oldList.subList(fromIndex,
oldListIterator.previousIndex()),
flaggedPenalty, alignment));
returnedList.addAll
(currLM.getChangedKnuthElements
(oldList.subList(oldListIterator.previousIndex(),
oldList.size()),
flaggedPenalty, alignment));
}
}
}

// "wrap" the Position stored in each element of returnedList
ListIterator listIter = returnedList.listIterator();
while (listIter.hasNext()) {
returnedElement = (KnuthElement) listIter.next();
returnedElement.setPosition
(new NonLeafPosition(this, returnedElement.getPosition()));
returnList.add(returnedElement);
}
return returnList;
}

public int getWordSpaceIPD() {
LayoutManager firstChild = getChildLM();
if (firstChild != null) {
return firstChild.getWordSpaceIPD();
} else {
return 0;
}
}
}


+ 11
- 0
src/java/org/apache/fop/layoutmgr/LayoutContext.java View File

@@ -86,6 +86,7 @@ public class LayoutContext {

private int iLineHeight;
private int iBaseline;
private int iMiddleShift;

public LayoutContext(LayoutContext parentLC) {
this.flags = parentLC.flags;
@@ -98,6 +99,7 @@ public class LayoutContext {
this.ipdAdjust = parentLC.ipdAdjust;
this.iLineHeight = parentLC.iLineHeight;
this.iBaseline = parentLC.iBaseline;
this.iMiddleShift = parentLC.iMiddleShift;
// Copy other fields as necessary. Use clone???
}

@@ -225,6 +227,14 @@ public class LayoutContext {
return iBaseline;
}
public void setMiddleShift(int ms) {
iMiddleShift = ms;
}

public int getMiddleBaseline() {
return iBaseline + iMiddleShift;
}
public String toString() {
return "Layout Context:" +
"\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) +
@@ -235,6 +245,7 @@ public class LayoutContext {
"\nIPD Adjust: \t" + getIPDAdjust() +
"\nLine Height: \t" + getLineHeight() +
"\nBaseline: \t" + getBaseline() +
"\nMiddle Baseline: \t" + getMiddleBaseline() +
"\nResolve Leading Space: \t" + resolveLeadingSpace() +
"\nSuppress Leading Space: \t" + suppressLeadingSpace() +
"\nIs First Area: \t" + isFirstArea() +

+ 0
- 15
src/java/org/apache/fop/layoutmgr/LayoutManager.java View File

@@ -237,19 +237,4 @@ public interface LayoutManager {
* @param newLMs the list of LMs to be added
*/
void addChildLMs(List newLMs);

LinkedList getNextKnuthElements(LayoutContext context, int alignment);

KnuthElement addALetterSpaceTo(KnuthElement element);

void getWordChars(StringBuffer sbChars, Position pos);

void hyphenate(Position pos, HyphContext hc);

boolean applyChanges(List oldList);

LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
int alignment);

int getWordSpaceIPD();
}

+ 102
- 12
src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java View File

@@ -23,7 +23,10 @@ import org.apache.fop.area.inline.FilledArea;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.Inline;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fonts.Font;
import org.apache.fop.traits.MinOptMax;
@@ -38,6 +41,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
private Leader fobj;
Font font = null;
private LinkedList contentList = null;
private ContentLayoutManager clm = null;

/**
* Constructor
*
@@ -48,7 +54,10 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
super(node);
fobj = node;
font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
setAlignment(node.getLeaderAlignment());
// the property leader-alignment does not affect vertical positioning
// (see section 7.21.1 in the XSL Recommendation)
// setAlignment(node.getLeaderAlignment());
setAlignment(fobj.getVerticalAlign());
}

public InlineArea get(LayoutContext context) {
@@ -84,10 +93,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
char dot = '.'; // userAgent.getLeaderDotCharacter();

t.setTextArea("" + dot);
t.setIPD(font.getCharWidth(dot));
t.addTrait(Trait.FONT_NAME, font.getFontName());
t.addTrait(Trait.FONT_SIZE, new Integer(font.getFontSize()));
// set offset of dot within inline parent
t.setOffset(font.getAscender());
int width = font.getCharWidth(dot);
Space spacer = null;
if (fobj.getLeaderPatternWidth().getValue() > width) {
@@ -116,15 +124,15 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
// get breaks then add areas to FilledArea
FilledArea fa = new FilledArea();

ContentLayoutManager clm = new ContentLayoutManager(fa);
clm = new ContentLayoutManager(fa);
clm.setUserAgent(fobj.getUserAgent());
addChildLM(clm);

InlineStackingLayoutManager lm;
lm = new InlineStackingLayoutManager(fobj);
InlineLayoutManager lm;
lm = new InlineLayoutManager(fobj);
clm.addChildLM(lm);

clm.fillArea(lm);
contentList = clm.getNextKnuthElements(new LayoutContext(0), 0);
int width = clm.getStackingSize();
Space spacer = null;
if (fobj.getLeaderPatternWidth().getValue() > width) {
@@ -141,6 +149,90 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
return leaderArea;
}

protected void offsetArea(LayoutContext context) {
int pattern = fobj.getLeaderPattern();
int bpd = curArea.getBPD();

switch (pattern) {
case LeaderPattern.RULE:
switch (verticalAlignment) {
case VerticalAlign.TOP:
curArea.setOffset(0);
break;
case VerticalAlign.MIDDLE:
curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
break;
case VerticalAlign.BOTTOM:
curArea.setOffset(context.getLineHeight() - bpd);
break;
case VerticalAlign.BASELINE: // fall through
default:
curArea.setOffset(context.getBaseline() - bpd);
break;
}
break;
case LeaderPattern.DOTS:
switch (verticalAlignment) {
case VerticalAlign.TOP:
curArea.setOffset(0);
break;
case VerticalAlign.MIDDLE:
curArea.setOffset(context.getMiddleBaseline());
break;
case VerticalAlign.BOTTOM:
curArea.setOffset(context.getLineHeight() - bpd + font.getAscender());
break;
case VerticalAlign.BASELINE: // fall through
default:
curArea.setOffset(context.getBaseline());
break;
}
break;
case LeaderPattern.SPACE:
// nothing to do
break;
case LeaderPattern.USECONTENT:
switch (verticalAlignment) {
case VerticalAlign.TOP:
curArea.setOffset(0);
break;
case VerticalAlign.MIDDLE:
curArea.setOffset(context.getMiddleBaseline());
break;
case VerticalAlign.BOTTOM:
curArea.setOffset(context.getLineHeight() - bpd);
break;
case VerticalAlign.BASELINE: // fall through
default:
curArea.setOffset(context.getBaseline());
break;
}
break;
}
}

public void addAreas(PositionIterator posIter, LayoutContext context) {
if (fobj.getLeaderPattern() != LeaderPattern.USECONTENT) {
// use LeafNodeLayoutManager.addAreas()
super.addAreas(posIter, context);
} else {
addId();

widthAdjustArea(context);

// add content areas
KnuthPossPosIter contentIter = new KnuthPossPosIter(contentList, 0, contentList.size());
clm.addAreas(contentIter, context);
offsetArea(context);

parentLM.addChild(curArea);

while (posIter.hasNext()) {
posIter.next();
}
}
}

public LinkedList getNextKnuthElements(LayoutContext context,
int alignment) {
MinOptMax ipd;
@@ -158,15 +250,13 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
int lead = 0;
int total = 0;
int middle = 0;
switch (alignment) {
switch (verticalAlignment) {
case VerticalAlign.MIDDLE : middle = bpd / 2 ;
lead = bpd / 2 ;
break;
case VerticalAlign.TOP : total = bpd;
break;
case VerticalAlign.TOP : // fall through
case VerticalAlign.BOTTOM : total = bpd;
break;
case VerticalAlign.BASELINE:
case VerticalAlign.BASELINE: // fall through
default: lead = bpd;
break;
}

+ 10
- 64
src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java View File

@@ -33,12 +33,13 @@ import java.util.LinkedList;
* This class can be extended to handle the creation and adding of the
* inline area.
*/
public class LeafNodeLayoutManager extends AbstractLayoutManager {
public class LeafNodeLayoutManager extends AbstractLayoutManager
implements InlineLevelLayoutManager {
/**
* The inline area that this leafnode will add.
*/
protected InlineArea curArea = null;
protected int alignment;
protected int verticalAlignment;
private int lead;
private MinOptMax ipd;

@@ -116,7 +117,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
* @param al the vertical alignment positioning
*/
public void setAlignment(int al) {
alignment = al;
verticalAlignment = al;
}

/**
@@ -146,49 +147,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
return null;
}

/**
* Get the next break position.
* Since this holds an inline area it will return a single
* break position.
* @param context the layout context for this inline area
* @return the break poisition for adding this inline area
*/
public BreakPoss getNextBreakPoss(LayoutContext context) {
curArea = get(context);
if (curArea == null) {
setFinished(true);
return null;
}
BreakPoss bp = new BreakPoss(new LeafPosition(this, 0),
BreakPoss.CAN_BREAK_AFTER
| BreakPoss.CAN_BREAK_BEFORE | BreakPoss.ISFIRST
| BreakPoss.ISLAST);
ipd = getAllocationIPD(context.getRefIPD());
bp.setStackingSize(ipd);
bp.setNonStackingSize(new MinOptMax(curArea.getBPD()));
bp.setTrailingSpace(new SpaceSpecifier(false));

int bpd = curArea.getBPD();
switch (alignment) {
case VerticalAlign.MIDDLE:
bp.setMiddle(bpd / 2 /* - fontLead/2 */);
bp.setLead(bpd / 2 /* + fontLead/2 */);
break;
case VerticalAlign.TOP:
bp.setTotal(bpd);
break;
case VerticalAlign.BOTTOM:
bp.setTotal(bpd);
break;
case VerticalAlign.BASELINE:
default:
bp.setLead(bpd);
break;
}
setFinished(true);
return bp;
}

/**
* Get the allocation ipd of the inline area.
* This method may be overridden to handle percentage values.
@@ -199,19 +157,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
return new MinOptMax(curArea.getIPD());
}

/**
* Reset the position.
* If the reset position is null then this inline area should be
* restarted.
* @param resetPos the position to reset.
*/
public void resetPosition(Position resetPos) {
// only reset if setting null, start again
if (resetPos == null) {
setFinished(false);
}
}

/**
* Add the area for this layout manager.
* This adds the single inline area to the parent.
@@ -244,9 +189,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
*/
protected void offsetArea(LayoutContext context) {
int bpd = curArea.getBPD();
switch (alignment) {
switch (verticalAlignment) {
case VerticalAlign.MIDDLE:
curArea.setOffset(context.getBaseline() - bpd / 2 /* - fontLead/2 */);
curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
break;
case VerticalAlign.TOP:
//curArea.setOffset(0);
@@ -305,7 +250,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
int lead = 0;
int total = 0;
int middle = 0;
switch (alignment) {
switch (verticalAlignment) {
case VerticalAlign.MIDDLE : middle = bpd / 2 ;
lead = bpd / 2 ;
break;
@@ -331,6 +276,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
return returnList;
}

public void getWordChars(StringBuffer sbChars, Position bp) {
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
// return the unchanged box object
return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
@@ -339,8 +287,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
}

public void hyphenate(Position pos, HyphContext hc) {
// use the AbstractLayoutManager.hyphenate() null implementation
super.hyphenate(pos, hc);
}

public boolean applyChanges(List oldList) {

+ 107
- 127
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java View File

@@ -58,7 +58,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
* @param l the default lead, from top to baseline
* @param f the default follow, from baseline to bottom
*/
public LineLayoutManager(Block node, int lh, int l, int f) {
public LineLayoutManager(Block node, int lh, int l, int f, int ms) {
super(node);
fobj = node;
// the child FObj are owned by the parent BlockLM
@@ -67,6 +67,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
lineHeight = lh;
lead = l;
follow = f;
middleShift = ms;
initialize(); // Normally done when started by parent!
}

@@ -126,6 +127,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
private int lineHeight;
private int lead;
private int follow;
// offset of the middle baseline with respect to the main baseline
private int middleShift;

// inline start pos when adding areas
private int iStartPos = 0;
@@ -151,6 +154,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
// suggested modification to the "optimum" number of lines
private int looseness = 0;

// this constant is used to create elements when text-align is center:
// every TextLM descendant of LineLM must use the same value,
// otherwise the line breaking algorithm does not find the right
// break point
public static final int DEFAULT_SPACE_WIDTH = 3336;
private static final int INFINITE_RATIO = 1000;

// this class represent a feasible breaking point
@@ -272,10 +280,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
// which was the first element in the paragraph
// returned by each LM
private class Update {
private LayoutManager inlineLM;
private InlineLevelLayoutManager inlineLM;
private int iFirstIndex;

public Update(LayoutManager lm, int index) {
public Update(InlineLevelLayoutManager lm, int index) {
inlineLM = lm;
iFirstIndex = index;
}
@@ -288,28 +296,19 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
public int ignoreAtEnd = 0;
// minimum space at the end of the last line (in millipoints)
public int lineFillerWidth;
// word space dimension (in millipoints)
private int wordSpaceIPD;

public void startParagraph(int lineWidth) {
// get the word space dimension, which needs to be known
// in order to center text
LayoutManager lm;
if ((lm = getChildLM()) != null) {
wordSpaceIPD = lm.getWordSpaceIPD();
}

// set the minimum amount of empty space at the end of the
// last line
if (bTextAlignment == CENTER) {
lineFillerWidth = 0;
} else {
lineFillerWidth = (int)(lineWidth / 6);
lineFillerWidth = (int)(lineWidth / 12);
}

// add auxiliary elements at the beginning of the paragraph
if (bTextAlignment == CENTER && bTextAlignmentLast != JUSTIFY) {
this.add(new KnuthGlue(0, 3 * wordSpaceIPD, 0,
this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
null, false));
ignoreAtStart ++;
}
@@ -333,7 +332,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
if (this.size() > ignoreAtStart) {
if (bTextAlignment == CENTER
&& bTextAlignmentLast != JUSTIFY) {
this.add(new KnuthGlue(0, 3 * wordSpaceIPD, 0,
this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
null, false));
this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
false, null, false));
@@ -371,7 +370,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
public BreakPoss getNextBreakPoss(LayoutContext context) {
// Get a break from currently active child LM
// Set up constraints for inline level managers
LayoutManager curLM ; // currently active LM
InlineLevelLayoutManager curLM ; // currently active LM
BreakPoss prev = null;
BreakPoss bp = null; // proposed BreakPoss

@@ -407,57 +406,66 @@ public class LineLayoutManager extends InlineStackingLayoutManager {

Paragraph knuthPar = new Paragraph();
knuthPar.startParagraph(availIPD.opt);
while ((curLM = getChildLM()) != null) {
while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
if ((returnedList
= curLM.getNextKnuthElements(inlineLC,
effectiveAlignment))
!= null) {
// if there are two consecutive KnuthBox, the first one
// does not represent a whole word, so it must be given
// one more letter space
// look at the first element
thisElement = (KnuthElement) returnedList.getFirst();
if (returnedList.size() > 1
|| !(thisElement.isPenalty()
&& ((KnuthPenalty) thisElement).getP()
== -KnuthElement.INFINITE)) {
if (thisElement.isBox() && !thisElement.isAuxiliary()
&& bPrevWasKnuthBox) {
prevBox = (KnuthBox) knuthPar.removeLast();
if (!prevBox.isAuxiliary()) {
// if letter spacing is constant,
// only prevBox needs to be replaced;
knuthPar.addLast(prevBox.getLayoutManager()
.addALetterSpaceTo(prevBox));
} else {
// prevBox is the last element
// in the sub-sequence
// <box> <aux penalty> <aux glue> <aux box>
// the letter space is added to <aux glue>,
// while the other elements are not changed
KnuthBox auxBox = prevBox;
KnuthGlue auxGlue
= (KnuthGlue) knuthPar.removeLast();
KnuthPenalty auxPenalty
= (KnuthPenalty) knuthPar.removeLast();
prevBox = (KnuthBox) knuthPar.getLast();
knuthPar.addLast(auxPenalty);
knuthPar.addLast(prevBox.getLayoutManager()
.addALetterSpaceTo(prevBox));
knuthPar.addLast(auxBox);
}
}
if (((KnuthElement) returnedList.getLast()).isBox()) {
bPrevWasKnuthBox = true;
if (thisElement.isBox() && !thisElement.isAuxiliary()
&& bPrevWasKnuthBox) {
prevBox = (KnuthBox) knuthPar.removeLast();
// if there are two consecutive KnuthBoxes the
// first one does not represent a whole word,
// so it must be given one more letter space
if (!prevBox.isAuxiliary()) {
// if letter spacing is constant,
// only prevBox needs to be replaced;
knuthPar.addLast(((InlineLevelLayoutManager)
prevBox.getLayoutManager())
.addALetterSpaceTo(prevBox));
} else {
bPrevWasKnuthBox = false;
// prevBox is the last element
// in the sub-sequence
// <box> <aux penalty> <aux glue> <aux box>
// the letter space is added to <aux glue>,
// while the other elements are not changed
KnuthBox auxBox = prevBox;
KnuthGlue auxGlue
= (KnuthGlue) knuthPar.removeLast();
KnuthPenalty auxPenalty
= (KnuthPenalty) knuthPar.removeLast();
prevBox = (KnuthBox) knuthPar.getLast();
knuthPar.addLast(auxPenalty);
knuthPar.addLast(((InlineLevelLayoutManager)
prevBox.getLayoutManager())
.addALetterSpaceTo(prevBox));
knuthPar.addLast(auxBox);
}
// add the new elements to the paragraph
knuthPar.addAll(returnedList);
}

// look at the last element
KnuthElement lastElement = (KnuthElement) returnedList.getLast();
boolean bForceLinefeed = false;
if (lastElement.isBox()) {
bPrevWasKnuthBox = true;
} else {
// a list with a single penalty item
// whose value is -inf
// represents a preserved linefeed,
// wich forces a line break
bPrevWasKnuthBox = false;
if (lastElement.isPenalty()
&& ((KnuthPenalty) lastElement).getP()
== -KnuthPenalty.INFINITE) {
// a penalty item whose value is -inf
// represents a preserved linefeed,
// wich forces a line break
bForceLinefeed = true;
returnedList.removeLast();
}
}

// add the new elements to the paragraph
knuthPar.addAll(returnedList);
if (bForceLinefeed) {
knuthPar.endParagraph();
knuthPar = new Paragraph();
knuthPar.startParagraph(availIPD.opt);
@@ -467,7 +475,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
// curLM returned null; this can happen
// if it has nothing more to layout,
// so just iterate once more to see
// if there are other chilren
// if there are other children
}
}
knuthPar.endParagraph();
@@ -647,53 +655,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
double ratio = (textAlign == JUSTIFY)
? bestActiveNode.adjustRatio : 0;

// lead to baseline is
// max of: baseline fixed alignment and middle/2
// after baseline is
// max: top height-lead, middle/2 and bottom height-lead
int halfLeading = (lineHeight - lead - follow) / 2;
// height before baseline
int lineLead = lead + halfLeading;
// maximum size of top and bottom alignment
int maxtb = follow + halfLeading;
// max size of middle alignment below baseline
int middlefollow = maxtb;

// index of the first KnuthElement in this line
int firstElementIndex = 0;
if (line > 1) {
firstElementIndex = bestActiveNode.previous.position + 1;
}
ListIterator inlineIterator = par.listIterator(firstElementIndex);
for (int j = 0;
j < (bestActiveNode.position - firstElementIndex + 1);
j++) {
KnuthElement element = (KnuthElement) inlineIterator.next();
if (element.isBox()) {
if (((KnuthBox) element).getLead() > lineLead) {
lineLead = ((KnuthBox) element).getLead();
}
if (((KnuthBox) element).getTotal() > maxtb) {
maxtb = ((KnuthBox) element).getTotal();
}
if (((KnuthBox) element).getMiddle() > middlefollow) {
middlefollow = ((KnuthBox) element).getMiddle();
}
}
}

if (maxtb - lineLead > middlefollow) {
middlefollow = maxtb - lineLead;
}

// add nodes at the beginning of the list, as they are found
// backwards, from the last one to the first one
breakpoints.add(0,
new LineBreakPosition(this,
makeLineBreakPosition(par,
(i > 1 ? bestActiveNode.previous.position + 1: 0),
bestActiveNode.position,
ratio, 0, indent,
lineLead + middlefollow,
lineLead));
0, ratio, indent);

bestActiveNode = bestActiveNode.previous;
}
if (bForced) {
@@ -705,22 +671,29 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
}

private void fallback(Paragraph par, int line) {
// lead to baseline is
// max of: baseline fixed alignment and middle/2
// after baseline is
// max: top height-lead, middle/2 and bottom height-lead
makeLineBreakPosition(par,
lastDeactivatedNode.position,
par.size() - 1,
line, 0, 0);
}

private void makeLineBreakPosition(Paragraph par,
int firstElementIndex, int lastElementIndex,
int insertIndex, double ratio, int indent) {
// line height calculation

int halfLeading = (lineHeight - lead - follow) / 2;
// height before baseline
// height above the main baseline
int lineLead = lead + halfLeading;
// maximum size of top and bottom alignment
int maxtb = follow + halfLeading;
// max size of middle alignment below baseline
// max size of middle alignment above and below the middle baseline
int middlefollow = maxtb;

ListIterator inlineIterator
= par.listIterator(lastDeactivatedNode.position);
for (int j = lastDeactivatedNode.position;
j < (par.size());
= par.listIterator(firstElementIndex);
for (int j = firstElementIndex;
j <= lastElementIndex;
j++) {
KnuthElement element = (KnuthElement) inlineIterator.next();
if (element.isBox()) {
@@ -730,8 +703,13 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
if (((KnuthBox) element).getTotal() > maxtb) {
maxtb = ((KnuthBox) element).getTotal();
}
if (((KnuthBox) element).getMiddle() > middlefollow) {
middlefollow = ((KnuthBox) element).getMiddle();
if (((KnuthBox) element).getMiddle() > lineLead + middleShift) {
lineLead += ((KnuthBox) element).getMiddle()
- lineLead - middleShift;
}
if (((KnuthBox) element).getMiddle() > middlefollow - middleShift) {
middlefollow += ((KnuthBox) element).getMiddle()
- middlefollow + middleShift;
}
}
}
@@ -740,14 +718,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
middlefollow = maxtb - lineLead;
}

breakpoints.add(line,
new LineBreakPosition(this, par.size() - 1,
0, 0, 0,
breakpoints.add(insertIndex,
new LineBreakPosition(this,
lastElementIndex ,
ratio, 0, indent,
lineLead + middlefollow,
lineLead));
}


private void considerLegalBreak(LinkedList par, int lineWidth,
KnuthElement element,
int totalWidth, int totalStretch,
@@ -969,8 +947,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
LinkedList updateList = new LinkedList();
KnuthElement firstElement = null;
KnuthElement nextElement = null;
// current TextLayoutManager
LayoutManager currLM = null;
// current InlineLevelLayoutManager
InlineLevelLayoutManager currLM = null;
// number of KnuthBox elements containing word fragments
int boxCount;
// number of auxiliary KnuthElements between KnuthBoxes
@@ -982,7 +960,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
firstElement = (KnuthElement) currParIterator.next();
//
if (firstElement.getLayoutManager() != currLM) {
currLM = firstElement.getLayoutManager();
currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
if (currLM != null) {
updateList.add(new Update(currLM, currParIterator.previousIndex()));
} else {
@@ -1003,7 +981,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
if (nextElement.isBox() && !nextElement.isAuxiliary()) {
// a non-auxiliary KnuthBox: append word chars
if (currLM != nextElement.getLayoutManager()) {
currLM = nextElement.getLayoutManager();
currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
updateList.add(new Update(currLM, currParIterator.previousIndex()));
}
// append text to recreate the whole word
@@ -1031,7 +1009,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
for (int i = 0; i < (boxCount + auxCount); i++) {
element = (KnuthElement) currParIterator.next();
if (element.isBox() && !element.isAuxiliary()) {
element.getLayoutManager().hyphenate(element.getPosition(), hc);
((InlineLevelLayoutManager)
element.getLayoutManager()).hyphenate(element.getPosition(), hc);
} else {
// nothing to do, element is an auxiliary KnuthElement
}
@@ -1065,7 +1044,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {

// applyChanges() returns true if the LM modifies its data,
// so it must return new KnuthElements to replace the old ones
if (currUpdate.inlineLM
if (((InlineLevelLayoutManager) currUpdate.inlineLM)
.applyChanges(currPar.subList(fromIndex + iAddedElements,
toIndex + iAddedElements))) {
// insert the new KnuthElements
@@ -1415,6 +1394,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
lineArea.setBPD(lbp.lineHeight);
lc.setBaseline(lbp.baseline);
lc.setLineHeight(lbp.lineHeight);
lc.setMiddleShift(middleShift);
setCurrentArea(lineArea);

Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);

+ 2
- 1
src/java/org/apache/fop/layoutmgr/RetrieveMarkerLayoutManager.java View File

@@ -73,7 +73,8 @@ public class RetrieveMarkerLayoutManager extends AbstractLayoutManager {
if (replaceLM == null) {
return null;
}
return replaceLM.getNextKnuthElements(context, alignment);
return ((InlineLevelLayoutManager) replaceLM)
.getNextKnuthElements(context, alignment);
}

public void addAreas(PositionIterator parentIter,

+ 80
- 26
src/java/org/apache/fop/layoutmgr/TextLayoutManager.java View File

@@ -24,6 +24,7 @@ import java.util.LinkedList;
import java.util.ListIterator;

import org.apache.fop.fo.FOText;
import org.apache.fop.fo.flow.Inline;
import org.apache.fop.fonts.Font;
import org.apache.fop.traits.SpaceVal;
import org.apache.fop.area.Trait;
@@ -36,7 +37,8 @@ import org.apache.fop.traits.MinOptMax;
* LayoutManager for text (a sequence of characters) which generates one
* or more inline areas.
*/
public class TextLayoutManager extends AbstractLayoutManager {
public class TextLayoutManager extends AbstractLayoutManager
implements InlineLevelLayoutManager {

/**
* Store information about each potential text area.
@@ -117,6 +119,12 @@ public class TextLayoutManager extends AbstractLayoutManager {
private short iTempStart = 0;
private LinkedList changeList = null;

private int textHeight;
private int lead = 0;
private int total = 0;
private int middle = 0;
private int verticalAlignment = VerticalAlign.BASELINE;

/**
* Create a Text layout manager.
*
@@ -155,6 +163,26 @@ public class TextLayoutManager extends AbstractLayoutManager {
// in the SpaceVal.makeWordSpacing() method
letterSpaceIPD = ls.getSpace();
wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace());

// set text height
textHeight = fs.getAscender()
- fs.getDescender();

// if the text node is son of an inline, set vertical align
if (foText.getParent() instanceof Inline) {
setAlignment(((Inline) foText.getParent()).getVerticalAlign());
}
switch (verticalAlignment) {
case VerticalAlign.MIDDLE : middle = textHeight / 2 ;
break;
case VerticalAlign.TOP : // fall through
case VerticalAlign.BOTTOM : total = textHeight;
break;
case VerticalAlign.BASELINE: // fall through
default : lead = fs.getAscender();
total = textHeight;
break;
}
}

/**
@@ -458,8 +486,8 @@ public class TextLayoutManager extends AbstractLayoutManager {
* used for calculating the bpd of the line area containing
* this text.
*/
//bp.setDescender(foText.textInfo.fs.getDescender());
//bp.setAscender(foText.textInfo.fs.getAscender());
//bp.setDescender(fs.getDescender());
//bp.setAscender(fs.getAscender());
if (iNextStart == textArray.length) {
flags |= BreakPoss.ISLAST;
setFinished(true);
@@ -574,7 +602,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;

TextArea t = createTextArea(str, realWidth.opt + iTotalAdjust,
context.getBaseline());
context);

// iWordSpaceDim is computed in relation to wordSpaceIPD.opt
// but the renderer needs to know the adjustment in relation
@@ -606,12 +634,26 @@ public class TextLayoutManager extends AbstractLayoutManager {
* @param base the baseline position
* @return the new word area
*/
protected TextArea createTextArea(String str, int width, int base) {
protected TextArea createTextArea(String str, int width, LayoutContext context) {
TextArea textArea = new TextArea();
textArea.setIPD(width);
textArea.setBPD(fs.getAscender() - fs.getDescender());
textArea.setOffset(fs.getAscender());
textArea.setOffset(base);
int bpd = textArea.getBPD();
switch (verticalAlignment) {
case VerticalAlign.MIDDLE:
textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
break;
case VerticalAlign.TOP:
textArea.setOffset(fs.getAscender());
break;
case VerticalAlign.BOTTOM:
textArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
break;
case VerticalAlign.BASELINE:
default:
textArea.setOffset(context.getBaseline());
break;
}

textArea.setTextArea(str);
textArea.addTrait(Trait.FONT_NAME, fs.getFontName());
@@ -620,13 +662,29 @@ public class TextLayoutManager extends AbstractLayoutManager {
return textArea;
}

/**
* Set the alignment of the inline area.
* @param al the vertical alignment positioning
*/
public void setAlignment(int al) {
verticalAlignment = al;
}

public LinkedList getNextKnuthElements(LayoutContext context,
int alignment) {
LinkedList returnList = new LinkedList();

while (iNextStart < textArray.length) {
if (textArray[iNextStart] == SPACE) {
if (textArray[iNextStart] == SPACE
|| textArray[iNextStart] == NBSPACE) {
// normal, breaking space
// or non-breaking space
if (textArray[iNextStart] == NBSPACE) {
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, vecAreaInfo.size() - 1),
false));
}
switch (alignment) {
case CENTER :
vecAreaInfo.add
@@ -634,14 +692,14 @@ public class TextLayoutManager extends AbstractLayoutManager {
(short) 1, (short) 0,
wordSpaceIPD, false));
returnList.add
(new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
returnList.add
(new KnuthPenalty(0, 0, false,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthGlue(wordSpaceIPD.opt,
- 6 * wordSpaceIPD.opt, 0,
- 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthBox(0, 0, 0, 0,
@@ -650,7 +708,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
iNextStart ++;
break;
@@ -715,8 +773,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
iNextStart ++;
} else if (textArray[iNextStart] == NEWLINE) {
// linefeed; this can happen when linefeed-treatment="preserve"
// the linefeed character is the first one in textArray,
// so we can just return a list with a penalty item
// add a penalty item to the list and return
returnList.add
(new KnuthPenalty(0, -KnuthElement.INFINITE,
false, null, false));
@@ -747,14 +804,15 @@ public class TextLayoutManager extends AbstractLayoutManager {
// constant letter space; simply return a box
// whose width includes letter spaces
returnList.add
(new KnuthBox(wordIPD.opt, 0, 0, 0,
(new KnuthBox(wordIPD.opt, lead, total, middle,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
iNextStart = iTempStart;
} else {
// adjustable letter space;
// some other KnuthElements are needed
returnList.add
(new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt, 0, 0, 0,
(new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
lead, total, middle,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -765,7 +823,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
(iTempStart - iThisStart - 1) * (letterSpaceIPD.opt - letterSpaceIPD.min),
new LeafPosition(this, -1), true));
returnList.add
(new KnuthBox(0, 0, 0, 0,
(new KnuthBox(0, lead, total, middle,
new LeafPosition(this, -1), true));
iNextStart = iTempStart;
}
@@ -779,17 +837,13 @@ public class TextLayoutManager extends AbstractLayoutManager {
}
}

public int getWordSpaceIPD() {
return wordSpaceIPD.opt;
}

public KnuthElement addALetterSpaceTo(KnuthElement element) {
LeafPosition pos = (LeafPosition) element.getPosition();
AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
ai.iLScount ++;
ai.ipdArea.add(letterSpaceIPD);
if (letterSpaceIPD.min == letterSpaceIPD.max) {
return new KnuthBox(ai.ipdArea.opt, 0, 0, 0, pos, false);
return new KnuthBox(ai.ipdArea.opt, lead, total, middle, pos, false);
} else {
return new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
@@ -908,13 +962,13 @@ public class TextLayoutManager extends AbstractLayoutManager {
// ai refers either to a word or a word fragment
if (letterSpaceIPD.min == letterSpaceIPD.max) {
returnList.add
(new KnuthBox(ai.ipdArea.opt, 0, 0, 0,
(new KnuthBox(ai.ipdArea.opt, lead, total, middle,
new LeafPosition(this, iReturnedIndex), false));
} else {
returnList.add
(new KnuthBox(ai.ipdArea.opt
- ai.iLScount * letterSpaceIPD.opt,
0, 0, 0,
lead, total, middle,
new LeafPosition(this, iReturnedIndex), false));
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -939,14 +993,14 @@ public class TextLayoutManager extends AbstractLayoutManager {
switch (alignment) {
case CENTER :
returnList.add
(new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, iReturnedIndex), false));
returnList.add
(new KnuthPenalty(0, 0, false,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthGlue(wordSpaceIPD.opt,
- 6 * wordSpaceIPD.opt, 0,
- 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthBox(0, 0, 0, 0,
@@ -955,7 +1009,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
iReturnedIndex ++;
break;

Loading…
Cancel
Save