blob: a6d695b063779cbb8471c072ff7b9dd54a49a46c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.inline;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.fop.area.Trait;
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.fo.FObj;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.layoutmgr.InlineKnuthSequence;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.KnuthPossPosIter;
import org.apache.fop.layoutmgr.KnuthSequence;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LeafPosition;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.MinOptMax;
/**
* LayoutManager for the fo:leader formatting object
*/
public class LeaderLayoutManager extends LeafNodeLayoutManager {
private Leader fobj;
private Font font;
private List contentList;
private ContentLayoutManager clm;
private int contentAreaIPD;
/**
* Constructor
*
* @param node the formatting object that creates this area
*/
public LeaderLayoutManager(Leader node) {
super(node);
fobj = node;
}
/** {@inheritDoc} */
public void initialize() {
FontInfo fi = fobj.getFOEventHandler().getFontInfo();
FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi);
font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this));
// the property leader-alignment does not affect vertical positioning
// (see section 7.21.1 in the XSL Recommendation)
// setAlignment(node.getLeaderAlignment());
setCommonBorderPaddingBackground(fobj.getCommonBorderPaddingBackground());
}
/**
* Return the inline area for this leader.
* @param context the layout context
* @return the inline area
*/
public InlineArea get(LayoutContext context) {
return getLeaderInlineArea(context);
}
/**
* Return the allocated IPD for this area.
* @param refIPD the IPD of the reference area
* @return the allocated IPD
*/
protected MinOptMax getAllocationIPD(int refIPD) {
return getLeaderAllocIPD(refIPD);
}
private MinOptMax getLeaderAllocIPD(int ipd) {
// length of the leader
int borderPaddingWidth = 0;
if (commonBorderPaddingBackground != null) {
borderPaddingWidth = commonBorderPaddingBackground.getIPPaddingAndBorder(false, this);
}
setContentAreaIPD(ipd - borderPaddingWidth);
int opt = fobj.getLeaderLength().getOptimum(this).getLength().getValue(this)
- borderPaddingWidth;
int min = fobj.getLeaderLength().getMinimum(this).getLength().getValue(this)
- borderPaddingWidth;
int max = fobj.getLeaderLength().getMaximum(this).getLength().getValue(this)
- borderPaddingWidth;
return MinOptMax.getInstance(min, opt, max);
}
private InlineArea getLeaderInlineArea(LayoutContext context) {
InlineArea leaderArea = null;
int level = fobj.getBidiLevel();
if (fobj.getLeaderPattern() == EN_RULE) {
if (fobj.getRuleStyle() != EN_NONE) {
org.apache.fop.area.inline.Leader leader
= new org.apache.fop.area.inline.Leader();
leader.setRuleStyle(fobj.getRuleStyle());
leader.setRuleThickness(fobj.getRuleThickness().getValue(this));
leaderArea = leader;
} else {
leaderArea = new Space();
if (level >= 0) {
leaderArea.setBidiLevel(level);
}
}
leaderArea.setBPD(fobj.getRuleThickness().getValue(this));
leaderArea.addTrait(Trait.COLOR, fobj.getColor());
if (level >= 0) {
leaderArea.setBidiLevel(level);
}
} else if (fobj.getLeaderPattern() == EN_SPACE) {
leaderArea = new Space();
leaderArea.setBPD(fobj.getRuleThickness().getValue(this));
if (level >= 0) {
leaderArea.setBidiLevel(level);
}
} else if (fobj.getLeaderPattern() == EN_DOTS) {
TextArea t = new TextArea();
char dot = '.'; // userAgent.getLeaderDotCharacter();
int width = font.getCharWidth(dot);
int[] levels = (level < 0) ? null : new int[] {level};
t.addWord("" + dot, width, null, levels, null, 0);
t.setIPD(width);
t.setBPD(width);
t.setBaselineOffset(width);
TraitSetter.addFontTraits(t, font);
t.addTrait(Trait.COLOR, fobj.getColor());
Space spacer = null;
int widthLeaderPattern = fobj.getLeaderPatternWidth().getValue(this);
if (widthLeaderPattern > width) {
spacer = new Space();
spacer.setIPD(widthLeaderPattern - width);
if (level >= 0) {
spacer.setBidiLevel(level);
}
width = widthLeaderPattern;
}
FilledArea fa = new FilledArea();
fa.setUnitWidth(width);
fa.addChildArea(t);
if (spacer != null) {
fa.addChildArea(spacer);
}
fa.setBPD(t.getBPD());
leaderArea = fa;
} else if (fobj.getLeaderPattern() == EN_USECONTENT) {
if (fobj.getChildNodes() == null) {
InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get(
getFObj().getUserAgent().getEventBroadcaster());
eventProducer.leaderWithoutContent(this, getFObj().getLocator());
return null;
}
// child FOs are assigned to the InlineStackingLM
fobjIter = null;
// get breaks then add areas to FilledArea
FilledArea fa = new FilledArea();
clm = new ContentLayoutManager(fa, this);
addChildLM(clm);
InlineLayoutManager lm;
lm = new InlineLayoutManager(fobj);
clm.addChildLM(lm);
lm.initialize();
LayoutContext childContext = LayoutContext.newInstance();
childContext.setAlignmentContext(context.getAlignmentContext());
contentList = clm.getNextKnuthElements(childContext, 0);
int width = clm.getStackingSize();
if (width != 0) {
Space spacer = null;
if (fobj.getLeaderPatternWidth().getValue(this) > width) {
spacer = new Space();
spacer.setIPD(fobj.getLeaderPatternWidth().getValue(this) - width);
if (level >= 0) {
spacer.setBidiLevel(level);
}
width = fobj.getLeaderPatternWidth().getValue(this);
}
fa.setUnitWidth(width);
if (spacer != null) {
fa.addChildArea(spacer);
}
leaderArea = fa;
} else {
//Content collapsed to nothing, so use a space
leaderArea = new Space();
leaderArea.setBPD(fobj.getRuleThickness().getValue(this));
leaderArea.setBidiLevel(fobj.getBidiLevelRecursive());
}
}
assert leaderArea != null;
leaderArea.setChangeBarList(getChangeBarList());
TraitSetter.setProducerID(leaderArea, fobj.getId());
return leaderArea;
}
/** {@inheritDoc} */
public void addAreas(PositionIterator posIter, LayoutContext context) {
if (fobj.getLeaderPattern() != EN_USECONTENT) {
// use LeafNodeLayoutManager.addAreas()
super.addAreas(posIter, context);
} else {
addId();
widthAdjustArea(curArea, context);
if (commonBorderPaddingBackground != null) {
// Add border and padding to area
TraitSetter.setBorderPaddingTraits(curArea,
commonBorderPaddingBackground,
false, false, this);
TraitSetter.addBackground(curArea, commonBorderPaddingBackground, this);
}
// add content areas
KnuthPossPosIter contentIter = new KnuthPossPosIter(contentList, 0, contentList.size());
clm.addAreas(contentIter, context);
parentLayoutManager.addChildArea(curArea);
while (posIter.hasNext()) {
posIter.next();
}
}
}
/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
MinOptMax ipd;
curArea = get(context);
KnuthSequence seq = new InlineKnuthSequence();
if (curArea == null) {
setFinished(true);
return null;
}
alignmentContext = new AlignmentContext(curArea.getBPD()
, fobj.getAlignmentAdjust()
, fobj.getAlignmentBaseline()
, fobj.getBaselineShift()
, fobj.getDominantBaseline()
, context.getAlignmentContext());
ipd = getAllocationIPD(context.getRefIPD());
if (fobj.getLeaderPattern() == EN_USECONTENT && curArea instanceof FilledArea) {
// If we have user supplied content make it fit if we can
int unitWidth = ((FilledArea) curArea).getUnitWidth();
if (ipd.getOpt() < unitWidth && unitWidth <= ipd.getMax()) {
ipd = MinOptMax.getInstance(ipd.getMin(), unitWidth, ipd.getMax());
}
}
// create the AreaInfo object to store the computed values
areaInfo = new AreaInfo((short) 0, ipd, false, context.getAlignmentContext());
curArea.setAdjustingInfo(ipd.getStretch(), ipd.getShrink(), 0);
addKnuthElementsForBorderPaddingStart(seq);
// node is a fo:Leader
seq.add(new KnuthInlineBox(0, alignmentContext, new LeafPosition(this, -1), true));
seq.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
if (alignment == EN_JUSTIFY || alignment == 0) {
seq.add(new KnuthGlue(areaInfo.ipdArea, new LeafPosition(this, 0), false));
} else {
seq.add(new KnuthGlue(areaInfo.ipdArea.getOpt(), 0, 0,
new LeafPosition(this, 0), false));
}
seq.add(new KnuthInlineBox(0, alignmentContext, new LeafPosition(this, -1), true));
addKnuthElementsForBorderPaddingEnd(seq);
setFinished(true);
return Collections.singletonList(seq);
}
/** {@inheritDoc} */
public void hyphenate(Position pos, HyphContext hc) {
// use the AbstractLayoutManager.hyphenate() null implementation
super.hyphenate(pos, hc);
}
/** {@inheritDoc} */
public boolean applyChanges(List oldList) {
setFinished(false);
return false;
}
/** {@inheritDoc} */
public List getChangedKnuthElements(List oldList, int alignment) {
if (isFinished()) {
return null;
}
List returnList = new LinkedList();
addKnuthElementsForBorderPaddingStart(returnList);
returnList.add(new KnuthInlineBox(0, areaInfo.alignmentContext,
new LeafPosition(this, -1), true));
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
if (alignment == EN_JUSTIFY || alignment == 0) {
returnList.add(new KnuthGlue(areaInfo.ipdArea, new LeafPosition(this, 0), false));
} else {
returnList.add(new KnuthGlue(areaInfo.ipdArea.getOpt(), 0, 0,
new LeafPosition(this, 0), false));
}
returnList.add(new KnuthInlineBox(0, areaInfo.alignmentContext,
new LeafPosition(this, -1), true));
addKnuthElementsForBorderPaddingEnd(returnList);
setFinished(true);
return returnList;
}
/** {@inheritDoc} */
public int getBaseLength(int lengthBase, FObj fobj) {
return getParent().getBaseLength(lengthBase, getParent().getFObj());
}
/**
* Returns the IPD of the content area
* @return the IPD of the content area
*/
public int getContentAreaIPD() {
return contentAreaIPD;
}
private void setContentAreaIPD(int contentAreaIPD) {
this.contentAreaIPD = contentAreaIPD;
}
/** {@inheritDoc} */
public void reset() {
childLMs.clear();
super.reset();
}
}