package org.apache.fop.area.inline;
import java.util.List;
+import java.util.ListIterator;
import java.util.ArrayList;
/**
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.
*
private int textDecoration;
// private ToBeImplementedProperty textShadow;
private int textTransform;
+ private int verticalAlign;
private int visibility;
private Property wordSpacing;
// End of property values
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);
}
return wordSpacing;
}
+ /**
+ * Return the "vertical-align" property.
+ */
+ public int getVerticalAlign() {
+ return verticalAlign;
+ }
+
/**
* @see org.apache.fop.fo.FONode#addLayoutManager(List)
*/
private KeepProperty keepWithPrevious;
private Length lineHeight;
private int textDecoration;
+ private int verticalAlign;
private int visibility;
private Length width;
private int wrapOption;
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();
return textDecoration;
}
+ /**
+ * Return the "vertical-align" property.
+ */
+ public int getVerticalAlign() {
+ return verticalAlign;
+ }
+
/**
* @see org.apache.fop.fo.FObjMixed#charIterator
*/
private CommonRelativePosition commonRelativePosition;
private Length alignmentAdjust;
private int alignmentBaseline;
+ private int verticalAlign;
private Length baselineShift;
private ColorType color;
private int dominantBaseline;
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();
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.
*/
return leaderPatternWidth;
}
+ /**
+ * Return the "vertical-align" property.
+ */
+ public int getVerticalAlign() {
+ return verticalAlign;
+ }
+
/**
* @see org.apache.fop.fo.FONode#addLayoutManager(List)
*/
* 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;
}
/**
* LayoutManager for the fo:basic-link formatting object
*/
-public class BasicLinkLayoutManager extends InlineStackingLayoutManager {
+public class BasicLinkLayoutManager extends InlineLayoutManager {
private BasicLink fobj;
/**
private int lead = 12000;
private int lineHeight = 14000;
private int follow = 2000;
+ private int middleShift = 0;
private int iStartPos = 0;
lead = fs.getAscender();
follow = -fs.getDescender();
+ middleShift = -fs.getXHeight() / 2;
lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue();
}
*/
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()) {
fobj = node;
InlineArea inline = getCharacterInlineArea(node);
setCurrentArea(inline);
+ setAlignment(fobj.getVerticalAlign());
fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing());
*/
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:
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;
}
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;
* 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
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;
}
//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
* @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs
*/
public List getChildLMs() {
+ List childLMs = new ArrayList(1);
+ childLMs.add(childLM);
return childLMs;
}
}
lm.setParent(this);
lm.initialize();
- childLMs.add(lm);
+ childLM = (InlineLevelLayoutManager)lm;
log.trace(this.getClass().getName()
+ ": Adding child LM " + lm.getClass().getName());
}
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) {
int alignment) {
return null;
}
-
- public int getWordSpaceIPD() {
- return 0;
- }
}
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;
* 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;
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);
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
}
}
+ 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;
+ }
}
--- /dev/null
+/*
+ * 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);
+}
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;
= 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());
}
}
}
-
- 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;
- }
- }
}
private int iLineHeight;
private int iBaseline;
+ private int iMiddleShift;
public LayoutContext(LayoutContext parentLC) {
this.flags = parentLC.flags;
this.ipdAdjust = parentLC.ipdAdjust;
this.iLineHeight = parentLC.iLineHeight;
this.iBaseline = parentLC.iBaseline;
+ this.iMiddleShift = parentLC.iMiddleShift;
// Copy other fields as necessary. Use clone???
}
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()) +
"\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() +
* @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();
}
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;
private Leader fobj;
Font font = null;
+ private LinkedList contentList = null;
+ private ContentLayoutManager clm = null;
+
/**
* Constructor
*
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) {
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) {
// 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) {
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;
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;
}
* 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;
* @param al the vertical alignment positioning
*/
public void setAlignment(int al) {
- alignment = al;
+ verticalAlignment = al;
}
/**
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.
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.
*/
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);
int lead = 0;
int total = 0;
int middle = 0;
- switch (alignment) {
+ switch (verticalAlignment) {
case VerticalAlign.MIDDLE : middle = bpd / 2 ;
lead = bpd / 2 ;
break;
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,
}
public void hyphenate(Position pos, HyphContext hc) {
- // use the AbstractLayoutManager.hyphenate() null implementation
- super.hyphenate(pos, hc);
}
public boolean applyChanges(List oldList) {
* @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
lineHeight = lh;
lead = l;
follow = f;
+ middleShift = ms;
initialize(); // Normally done when started by parent!
}
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;
// 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
// 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;
}
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 ++;
}
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));
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
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);
// 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();
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) {
}
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()) {
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;
}
}
}
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,
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
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 {
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
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
}
// 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
lineArea.setBPD(lbp.lineHeight);
lc.setBaseline(lbp.baseline);
lc.setLineHeight(lbp.lineHeight);
+ lc.setMiddleShift(middleShift);
setCurrentArea(lineArea);
Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
if (replaceLM == null) {
return null;
}
- return replaceLM.getNextKnuthElements(context, alignment);
+ return ((InlineLevelLayoutManager) replaceLM)
+ .getNextKnuthElements(context, alignment);
}
public void addAreas(PositionIterator parentIter,
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;
* 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.
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.
*
// 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;
+ }
}
/**
* 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);
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
* @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());
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
(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,
(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;
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));
// 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,
(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;
}
}
}
- 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),
// 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,
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,
(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;