package org.apache.fop.layoutmgr.inline;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.flow.BasicLink;
import org.apache.fop.fo.flow.Inline;
import org.apache.fop.fo.flow.InlineLevel;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fo.pagination.Title;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonFont;
import org.apache.fop.fo.properties.CommonMarginInline;
import org.apache.fop.fo.properties.SpaceProperty;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.layoutmgr.BlockKnuthSequence;
import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.InlineKnuthSequence;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthSequence;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.SpaceSpecifier;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
import org.apache.fop.util.ListUtil;
public class InlineLayoutManager extends InlineStackingLayoutManager {
private static Log log = LogFactory.getLog(InlineLayoutManager.class);
private CommonMarginInline inlineProps;
private CommonBorderPaddingBackground borderProps;
private boolean areaCreated;
private LayoutManager lastChildLM;
private Font font;
protected Length alignmentAdjust;
protected int alignmentBaseline = EN_BASELINE;
protected Length baselineShift;
protected int dominantBaseline;
protected SpaceProperty lineHeight;
private AlignmentContext alignmentContext;
public InlineLayoutManager(InlineLevel node) {
super(node);
}
@Override
public void initialize() {
InlineLevel fobj = (InlineLevel) this.fobj;
int padding = 0;
FontInfo fi = fobj.getFOEventHandler().getFontInfo();
CommonFont commonFont = fobj.getCommonFont();
FontTriplet[] fontkeys = commonFont.getFontState(fi);
font = fi.getFontInstance(fontkeys[0], commonFont.fontSize.getValue(this));
lineHeight = fobj.getLineHeight();
borderProps = fobj.getCommonBorderPaddingBackground();
inlineProps = fobj.getCommonMarginInline();
if (fobj instanceof Inline) {
alignmentAdjust = ((Inline)fobj).getAlignmentAdjust();
alignmentBaseline = ((Inline)fobj).getAlignmentBaseline();
baselineShift = ((Inline)fobj).getBaselineShift();
dominantBaseline = ((Inline)fobj).getDominantBaseline();
} else if (fobj instanceof Leader) {
alignmentAdjust = ((Leader)fobj).getAlignmentAdjust();
alignmentBaseline = ((Leader)fobj).getAlignmentBaseline();
baselineShift = ((Leader)fobj).getBaselineShift();
dominantBaseline = ((Leader)fobj).getDominantBaseline();
} else if (fobj instanceof BasicLink) {
alignmentAdjust = ((BasicLink)fobj).getAlignmentAdjust();
alignmentBaseline = ((BasicLink)fobj).getAlignmentBaseline();
baselineShift = ((BasicLink)fobj).getBaselineShift();
dominantBaseline = ((BasicLink)fobj).getDominantBaseline();
}
if (borderProps != null) {
padding = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false, this);
padding += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
false);
padding += borderProps.getPadding(CommonBorderPaddingBackground.AFTER, false, this);
padding += borderProps.getBorderWidth(CommonBorderPaddingBackground.AFTER, false);
}
extraBPD = MinOptMax.getInstance(padding);
}
@Override
protected MinOptMax (boolean isNotFirst, boolean isNotLast) {
int borderAndPadding = 0;
if (borderProps != null) {
borderAndPadding
= borderProps.getPadding(CommonBorderPaddingBackground.START, isNotFirst, this);
borderAndPadding
+= borderProps.getBorderWidth(CommonBorderPaddingBackground.START, isNotFirst);
borderAndPadding
+= borderProps.getPadding(CommonBorderPaddingBackground.END, isNotLast, this);
borderAndPadding
+= borderProps.getBorderWidth(CommonBorderPaddingBackground.END, isNotLast);
}
return MinOptMax.getInstance(borderAndPadding);
}
@Override
protected boolean hasLeadingFence(boolean isNotFirst) {
return borderProps != null
&& (borderProps.getPadding(CommonBorderPaddingBackground.START, isNotFirst, this) > 0
|| borderProps.getBorderWidth(CommonBorderPaddingBackground.START, isNotFirst) > 0
);
}
@Override
protected boolean hasTrailingFence(boolean isNotLast) {
return borderProps != null
&& (borderProps.getPadding(CommonBorderPaddingBackground.END, isNotLast, this) > 0
|| borderProps.getBorderWidth(CommonBorderPaddingBackground.END, isNotLast) > 0
);
}
@Override
protected SpaceProperty getSpaceStart() {
return inlineProps != null ? inlineProps.spaceStart : null;
}
@Override
protected SpaceProperty getSpaceEnd() {
return inlineProps != null ? inlineProps.spaceEnd : null;
}
protected InlineArea createArea(boolean isInline) {
InlineArea area;
if (isInline) {
area = createInlineParent();
area.setBlockProgressionOffset(0);
} else {
area = new InlineBlockParent();
}
if (fobj instanceof Inline || fobj instanceof BasicLink) {
TraitSetter.setProducerID(area, fobj.getId());
TraitSetter.setLayer(area, fobj.getLayer());
}
return area;
}
protected InlineParent createInlineParent() {
return new InlineParent();
}
@Override
protected void setTraits(boolean isNotFirst, boolean isNotLast) {
if (borderProps != null) {
TraitSetter.setBorderPaddingTraits(getCurrentArea(),
borderProps, isNotFirst, isNotLast, this);
TraitSetter.addBackground(getCurrentArea(), borderProps, this);
}
}
public boolean mustKeepTogether() {
return mustKeepTogether(this.getParent());
}
private boolean mustKeepTogether(LayoutManager lm) {
if (lm instanceof BlockLevelLayoutManager) {
return ((BlockLevelLayoutManager) lm).mustKeepTogether();
} else if (lm instanceof InlineLayoutManager) {
return ((InlineLayoutManager) lm).mustKeepTogether();
} else {
return mustKeepTogether(lm.getParent());
}
}
@Override
public List getNextKnuthElements(
LayoutContext context, int alignment) {
LayoutManager curLM;
List<KnuthSequence> returnedList;
List<KnuthSequence> returnList = new LinkedList<KnuthSequence>();
KnuthSequence lastSequence = null;
if (fobj instanceof Title) {
alignmentContext = new AlignmentContext(font,
lineHeight.getOptimum(this).getLength().getValue(this),
context.getWritingMode());
} else {
alignmentContext = new AlignmentContext(font
, lineHeight.getOptimum(this).getLength().getValue(this)
, alignmentAdjust
, alignmentBaseline
, baselineShift
, dominantBaseline
, context.getAlignmentContext());
}
childLC = LayoutContext.copyOf(context);
childLC.setAlignmentContext(alignmentContext);
if (context.startsNewArea()) {
if (getSpaceStart() != null) {
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
}
}
StringBuffer trace = new StringBuffer("InlineLM:");
boolean borderAdded = false;
if (borderProps != null) {
childLC.setLineStartBorderAndPaddingWidth(context.getLineStartBorderAndPaddingWidth()
+ borderProps.getPaddingStart(true, this)
+ borderProps.getBorderStartWidth(true)
);
childLC.setLineEndBorderAndPaddingWidth(context.getLineEndBorderAndPaddingWidth()
+ borderProps.getPaddingEnd(true, this)
+ borderProps.getBorderEndWidth(true)
);
}
while ((curLM = getChildLM()) != null) {
if (!(curLM instanceof InlineLevelLayoutManager)) {
if (borderProps != null) {
childLC.setRefIPD(childLC.getRefIPD()
- borderProps.getPaddingStart(lastChildLM != null, this)
- borderProps.getBorderStartWidth(lastChildLM != null)
- borderProps.getPaddingEnd(hasNextChildLM(), this)
- borderProps.getBorderEndWidth(hasNextChildLM()));
}
}
returnedList = curLM.getNextKnuthElements(childLC, alignment);
if (returnList.isEmpty() && childLC.isKeepWithPreviousPending()) {
childLC.clearKeepWithPreviousPending();
}
if (returnedList == null
|| returnedList.isEmpty()) {
continue;
}
if (curLM instanceof InlineLevelLayoutManager) {
context.clearKeepWithNextPending();
for (KnuthSequence sequence : returnedList) {
sequence.wrapPositions(this);
}
int insertionStartIndex = 0;
if (lastSequence != null
&& lastSequence.appendSequenceOrClose(returnedList.get(0))) {
insertionStartIndex = 1;
}
if (!borderAdded && !returnedList.isEmpty()) {
addKnuthElementsForBorderPaddingStart(returnedList.get(0));
borderAdded = true;
}
for (Iterator<KnuthSequence> iter = returnedList.listIterator(insertionStartIndex);
iter.hasNext();) {
returnList.add(iter.next());
}
} else {
BlockKnuthSequence sequence = new BlockKnuthSequence(returnedList);
sequence.wrapPositions(this);
boolean appended = false;
if (lastSequence != null) {
if (lastSequence.canAppendSequence(sequence)) {
BreakElement bk = new BreakElement(new Position(this), 0, context);
boolean keepTogether = (mustKeepTogether()
|| context.isKeepWithNextPending()
|| childLC.isKeepWithPreviousPending());
appended = lastSequence.appendSequenceOrClose(sequence, keepTogether, bk);
} else {
lastSequence.endSequence();
}
}
if (!appended) {
if (!borderAdded) {
addKnuthElementsForBorderPaddingStart(sequence);
borderAdded = true;
}
returnList.add(sequence);
}
context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
childLC.clearKeepsPending();
}
lastSequence = ListUtil.getLast(returnList);
lastChildLM = curLM;
childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE, false);
}
if (lastSequence != null) {
addKnuthElementsForBorderPaddingEnd(lastSequence);
}
setFinished(true);
log.trace(trace);
if (returnList.isEmpty()) {
if (fobj.hasId() || fobj.hasMarkers()) {
InlineKnuthSequence emptySeq = new InlineKnuthSequence();
emptySeq.add(new KnuthInlineBox(
0,
alignmentContext,
notifyPos(getAuxiliaryPosition()),
true));
returnList.add(emptySeq);
}
}
return returnList.isEmpty() ? null : returnList;
}
@Override
public void addAreas(PositionIterator parentIter,
LayoutContext context) {
addId();
setChildContext(LayoutContext.copyOf(context));
List<Position> positionList = new LinkedList<Position>();
Position pos;
LayoutManager lastLM = null;
Position lastPos = null;
while (parentIter.hasNext()) {
pos = parentIter.next();
if (pos != null && pos.getPosition() != null) {
if (isFirst(pos)) {
areaCreated = false;
}
positionList.add(pos.getPosition());
lastLM = pos.getPosition().getLM();
lastPos = pos;
}
}
if (hasLeadingFence(areaCreated)) {
getContext().setLeadingSpace(new SpaceSpecifier(false));
getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
} else {
getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, false);
}
if (getSpaceStart() != null) {
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
}
registerMarkers(
true,
!areaCreated,
lastPos == null || isLast(lastPos));
InlineArea parent = createArea(lastLM == null
|| lastLM instanceof InlineLevelLayoutManager);
parent.setBPD(alignmentContext.getHeight());
if (parent instanceof InlineParent) {
parent.setBlockProgressionOffset(alignmentContext.getOffset());
} else if (parent instanceof InlineBlockParent) {
if (borderProps != null) {
parent.setBlockProgressionOffset(borderProps.getPaddingBefore(false, this)
+ borderProps.getBorderBeforeWidth(false));
}
}
setCurrentArea(parent);
PositionIterator childPosIter
= new PositionIterator(positionList.listIterator());
LayoutManager prevLM = null;
LayoutManager childLM;
while ((childLM = childPosIter.getNextChildLM()) != null) {
getContext().setFlags(LayoutContext.LAST_AREA,
context.isLastArea() && childLM == lastLM);
childLM.addAreas(childPosIter, getContext());
getContext().setLeadingSpace(getContext().getTrailingSpace());
getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
prevLM = childLM;
}
boolean isLast = (getContext().isLastArea() && prevLM == lastChildLM);
if (hasTrailingFence(isLast)) {
addSpace(getCurrentArea(), getContext().getTrailingSpace().resolve(false),
getContext().getSpaceAdjust());
context.setTrailingSpace(new SpaceSpecifier(false));
} else {
context.setTrailingSpace(getContext().getTrailingSpace());
}
if (context.getTrailingSpace() != null && getSpaceEnd() != null) {
context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd(), this));
}
setTraits(areaCreated, lastPos == null || !isLast(lastPos));
parentLayoutManager.addChildArea(getCurrentArea());
registerMarkers(
false,
!areaCreated,
lastPos == null || isLast(lastPos));
context.setFlags(LayoutContext.LAST_AREA, isLast);
areaCreated = true;
checkEndOfLayout(lastPos);
}
@Override
public void addChildArea(Area childArea) {
Area parent = getCurrentArea();
if (getContext().resolveLeadingSpace()) {
addSpace(parent, getContext().getLeadingSpace().resolve(false),
getContext().getSpaceAdjust());
}
parent.addChildArea(childArea);
}
@Override
public List getChangedKnuthElements(List oldList, int alignment, int depth) {
List returnedList = new LinkedList();
addKnuthElementsForBorderPaddingStart(returnedList);
returnedList.addAll(super.getChangedKnuthElements(oldList, alignment, depth));
addKnuthElementsForBorderPaddingEnd(returnedList);
return returnedList;
}
protected void addKnuthElementsForBorderPaddingStart(List returnList) {
if (returnList instanceof BlockKnuthSequence) {
return;
}
CommonBorderPaddingBackground borderAndPadding
= ((InlineLevel)fobj).getCommonBorderPaddingBackground();
if (borderAndPadding != null) {
int ipStart = borderAndPadding.getBorderStartWidth(false)
+ borderAndPadding.getPaddingStart(false, this);
if (ipStart > 0) {
returnList.add(0, new KnuthBox(ipStart, getAuxiliaryPosition(), true));
}
}
}
protected void addKnuthElementsForBorderPaddingEnd(List returnList) {
if (returnList instanceof BlockKnuthSequence) {
return;
}
CommonBorderPaddingBackground borderAndPadding
= ((InlineLevel)fobj).getCommonBorderPaddingBackground();
if (borderAndPadding != null) {
int ipEnd = borderAndPadding.getBorderEndWidth(false)
+ borderAndPadding.getPaddingEnd(false, this);
if (ipEnd > 0) {
returnList.add(new KnuthBox(ipEnd, getAuxiliaryPosition(), true));
}
}
}
protected Position getAuxiliaryPosition() {
return new NonLeafPosition(this, null);
}
}