/*
 * 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: LeaderLayoutManager.java 1610839 2014-07-15 20:25:58Z vhennebert $ */

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
/** * 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
Params:
  • node – the formatting object that creates this area
/** * Constructor * * @param node the formatting object that creates this area */
public LeaderLayoutManager(Leader node) { super(node); fobj = node; }
{@inheritDoc}
/** {@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.
Params:
  • context – the layout context
Returns:the inline area
/** * 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.
Params:
  • refIPD – the IPD of the reference area
Returns:the allocated IPD
/** * 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()); } } TraitSetter.setProducerID(leaderArea, fobj.getId()); return leaderArea; }
{@inheritDoc}
/** {@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}
/** {@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}
/** {@inheritDoc} */
public void hyphenate(Position pos, HyphContext hc) { // use the AbstractLayoutManager.hyphenate() null implementation super.hyphenate(pos, hc); }
{@inheritDoc}
/** {@inheritDoc} */
public boolean applyChanges(List oldList) { setFinished(false); return false; }
{@inheritDoc}
/** {@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}
/** {@inheritDoc} */
public int getBaseLength(int lengthBase, FObj fobj) { return getParent().getBaseLength(lengthBase, getParent().getFObj()); }
Returns the IPD of the content area
Returns:the IPD of the content area
/** * 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}
/** {@inheritDoc} */
public void reset() { childLMs.clear(); super.reset(); } }