001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: LeaderLayoutManager.java 462812 2006-10-11 14:19:28Z spepping $ */
019:
020: package org.apache.fop.layoutmgr.inline;
021:
022: import org.apache.fop.area.Trait;
023: import org.apache.fop.area.inline.FilledArea;
024: import org.apache.fop.area.inline.InlineArea;
025: import org.apache.fop.area.inline.Space;
026: import org.apache.fop.area.inline.TextArea;
027: import org.apache.fop.fo.flow.Leader;
028: import org.apache.fop.fonts.Font;
029: import org.apache.fop.layoutmgr.InlineKnuthSequence;
030: import org.apache.fop.layoutmgr.KnuthElement;
031: import org.apache.fop.layoutmgr.KnuthGlue;
032: import org.apache.fop.layoutmgr.KnuthPenalty;
033: import org.apache.fop.layoutmgr.KnuthPossPosIter;
034: import org.apache.fop.layoutmgr.KnuthSequence;
035: import org.apache.fop.layoutmgr.LayoutContext;
036: import org.apache.fop.layoutmgr.LeafPosition;
037: import org.apache.fop.layoutmgr.Position;
038: import org.apache.fop.layoutmgr.PositionIterator;
039: import org.apache.fop.layoutmgr.TraitSetter;
040: import org.apache.fop.traits.MinOptMax;
041:
042: import java.util.List;
043: import java.util.LinkedList;
044: import org.apache.fop.fo.FObj;
045:
046: /**
047: * LayoutManager for the fo:leader formatting object
048: */
049: public class LeaderLayoutManager extends LeafNodeLayoutManager {
050: private Leader fobj;
051: private Font font = null;
052:
053: private LinkedList contentList = null;
054: private ContentLayoutManager clm = null;
055:
056: private int contentAreaIPD = 0;
057:
058: /**
059: * Constructor
060: *
061: * @param node the formatting object that creates this area
062: */
063: public LeaderLayoutManager(Leader node) {
064: super (node);
065: fobj = node;
066: }
067:
068: /** @see org.apache.fop.layoutmgr.LayoutManager#initialize */
069: public void initialize() {
070: font = fobj.getCommonFont().getFontState(
071: fobj.getFOEventHandler().getFontInfo(), this );
072: // the property leader-alignment does not affect vertical positioning
073: // (see section 7.21.1 in the XSL Recommendation)
074: // setAlignment(node.getLeaderAlignment());
075: setCommonBorderPaddingBackground(fobj
076: .getCommonBorderPaddingBackground());
077: }
078:
079: /**
080: * Return the inline area for this leader.
081: * @param context the layout context
082: * @return the inline area
083: */
084: public InlineArea get(LayoutContext context) {
085: return getLeaderInlineArea(context);
086: }
087:
088: /**
089: * Return the allocated IPD for this area.
090: * @param refIPD the IPD of the reference area
091: * @return the allocated IPD
092: */
093: protected MinOptMax getAllocationIPD(int refIPD) {
094: return getLeaderAllocIPD(refIPD);
095: }
096:
097: private MinOptMax getLeaderAllocIPD(int ipd) {
098: // length of the leader
099: int borderPaddingWidth = 0;
100: if (commonBorderPaddingBackground != null) {
101: borderPaddingWidth = commonBorderPaddingBackground
102: .getIPPaddingAndBorder(false, this );
103: }
104: setContentAreaIPD(ipd - borderPaddingWidth);
105: int opt = fobj.getLeaderLength().getOptimum(this ).getLength()
106: .getValue(this )
107: - borderPaddingWidth;
108: int min = fobj.getLeaderLength().getMinimum(this ).getLength()
109: .getValue(this )
110: - borderPaddingWidth;
111: int max = fobj.getLeaderLength().getMaximum(this ).getLength()
112: .getValue(this )
113: - borderPaddingWidth;
114: return new MinOptMax(min, opt, max);
115: }
116:
117: private InlineArea getLeaderInlineArea(LayoutContext context) {
118: InlineArea leaderArea = null;
119:
120: if (fobj.getLeaderPattern() == EN_RULE) {
121: if (fobj.getRuleStyle() != EN_NONE) {
122: org.apache.fop.area.inline.Leader leader = new org.apache.fop.area.inline.Leader();
123: leader.setRuleStyle(fobj.getRuleStyle());
124: leader.setRuleThickness(fobj.getRuleThickness()
125: .getValue(this ));
126: leader.setBPD(fobj.getRuleThickness().getValue(this ));
127: leaderArea = leader;
128: } else {
129: leaderArea = new Space();
130: leaderArea.setBPD(1);
131: }
132: leaderArea.addTrait(Trait.COLOR, fobj.getColor());
133: } else if (fobj.getLeaderPattern() == EN_SPACE) {
134: leaderArea = new Space();
135: leaderArea.setBPD(1);
136: } else if (fobj.getLeaderPattern() == EN_DOTS) {
137: TextArea t = new TextArea();
138: char dot = '.'; // userAgent.getLeaderDotCharacter();
139:
140: int width = font.getCharWidth(dot);
141: t.addWord("" + dot, 0);
142: t.setIPD(width);
143: t.setBPD(width);
144: t.setBaselineOffset(width);
145: TraitSetter.addFontTraits(t, font);
146: t.addTrait(Trait.COLOR, fobj.getColor());
147: Space spacer = null;
148: if (fobj.getLeaderPatternWidth().getValue(this ) > width) {
149: spacer = new Space();
150: spacer.setIPD(fobj.getLeaderPatternWidth().getValue(
151: this )
152: - width);
153: width = fobj.getLeaderPatternWidth().getValue(this );
154: }
155: FilledArea fa = new FilledArea();
156: fa.setUnitWidth(width);
157: fa.addChildArea(t);
158: if (spacer != null) {
159: fa.addChildArea(spacer);
160: }
161: fa.setBPD(t.getBPD());
162:
163: leaderArea = fa;
164: } else if (fobj.getLeaderPattern() == EN_USECONTENT) {
165: if (fobj.getChildNodes() == null) {
166: fobj.getLogger().error(
167: "Leader use-content with no content");
168: return null;
169: }
170:
171: // child FOs are assigned to the InlineStackingLM
172: fobjIter = null;
173:
174: // get breaks then add areas to FilledArea
175: FilledArea fa = new FilledArea();
176:
177: clm = new ContentLayoutManager(fa, this );
178: addChildLM(clm);
179:
180: InlineLayoutManager lm;
181: lm = new InlineLayoutManager(fobj);
182: clm.addChildLM(lm);
183: lm.initialize();
184:
185: LayoutContext childContext = new LayoutContext(0);
186: childContext.setAlignmentContext(context
187: .getAlignmentContext());
188: contentList = clm.getNextKnuthElements(childContext, 0);
189: int width = clm.getStackingSize();
190: Space spacer = null;
191: if (fobj.getLeaderPatternWidth().getValue(this ) > width) {
192: spacer = new Space();
193: spacer.setIPD(fobj.getLeaderPatternWidth().getValue(
194: this )
195: - width);
196: width = fobj.getLeaderPatternWidth().getValue(this );
197: }
198: fa.setUnitWidth(width);
199: if (spacer != null) {
200: fa.addChildArea(spacer);
201: }
202: leaderArea = fa;
203: }
204: TraitSetter.setProducerID(leaderArea, fobj.getId());
205: return leaderArea;
206: }
207:
208: /** @see LeafNodeLayoutManager#addAreas(PositionIterator, LayoutContext) */
209: public void addAreas(PositionIterator posIter, LayoutContext context) {
210: if (fobj.getLeaderPattern() != EN_USECONTENT) {
211: // use LeafNodeLayoutManager.addAreas()
212: super .addAreas(posIter, context);
213: } else {
214: addId();
215:
216: widthAdjustArea(curArea, context);
217:
218: if (commonBorderPaddingBackground != null) {
219: // Add border and padding to area
220: TraitSetter.setBorderPaddingTraits(curArea,
221: commonBorderPaddingBackground, false, false,
222: this );
223: TraitSetter.addBackground(curArea,
224: commonBorderPaddingBackground, this );
225: }
226:
227: // add content areas
228: KnuthPossPosIter contentIter = new KnuthPossPosIter(
229: contentList, 0, contentList.size());
230: clm.addAreas(contentIter, context);
231:
232: parentLM.addChildArea(curArea);
233:
234: while (posIter.hasNext()) {
235: posIter.next();
236: }
237: }
238: }
239:
240: /** @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(LayoutContext, int) */
241: public LinkedList getNextKnuthElements(LayoutContext context,
242: int alignment) {
243: MinOptMax ipd;
244: curArea = get(context);
245: KnuthSequence seq = new InlineKnuthSequence();
246:
247: if (curArea == null) {
248: setFinished(true);
249: return null;
250: }
251:
252: alignmentContext = new AlignmentContext(curArea.getBPD(), fobj
253: .getAlignmentAdjust(), fobj.getAlignmentBaseline(),
254: fobj.getBaselineShift(), fobj.getDominantBaseline(),
255: context.getAlignmentContext());
256:
257: ipd = getAllocationIPD(context.getRefIPD());
258: if (fobj.getLeaderPattern() == EN_USECONTENT
259: && curArea instanceof FilledArea) {
260: // If we have user supplied content make it fit if we can
261: int unitWidth = ((FilledArea) curArea).getUnitWidth();
262: if (ipd.opt < unitWidth && ipd.max >= unitWidth) {
263: ipd.opt = unitWidth;
264: }
265: }
266:
267: // create the AreaInfo object to store the computed values
268: areaInfo = new AreaInfo((short) 0, ipd, false, context
269: .getAlignmentContext());
270: curArea.setAdjustingInfo(ipd.max - ipd.opt, ipd.opt - ipd.min,
271: 0);
272:
273: addKnuthElementsForBorderPaddingStart(seq);
274:
275: // node is a fo:Leader
276: seq.add(new KnuthInlineBox(0, alignmentContext,
277: new LeafPosition(this , -1), true));
278: seq.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
279: new LeafPosition(this , -1), true));
280: if (alignment == EN_JUSTIFY || alignment == 0) {
281: seq.add(new KnuthGlue(areaInfo.ipdArea.opt,
282: areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
283: areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
284: new LeafPosition(this , 0), false));
285: } else {
286: seq.add(new KnuthGlue(areaInfo.ipdArea.opt, 0, 0,
287: new LeafPosition(this , 0), false));
288: }
289: seq.add(new KnuthInlineBox(0, alignmentContext,
290: new LeafPosition(this , -1), true));
291:
292: addKnuthElementsForBorderPaddingEnd(seq);
293:
294: LinkedList returnList = new LinkedList();
295: returnList.add(seq);
296: setFinished(true);
297: return returnList;
298: }
299:
300: /** @see InlineLevelLayoutManager#hyphenate(Position, HyphContext) */
301: public void hyphenate(Position pos, HyphContext hc) {
302: // use the AbstractLayoutManager.hyphenate() null implementation
303: super .hyphenate(pos, hc);
304: }
305:
306: /** @see InlineLevelLayoutManager#applyChanges(List) */
307: public boolean applyChanges(List oldList) {
308: setFinished(false);
309: return false;
310: }
311:
312: /** @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(List, int) */
313: public LinkedList getChangedKnuthElements(List oldList,
314: int alignment) {
315: if (isFinished()) {
316: return null;
317: }
318:
319: LinkedList returnList = new LinkedList();
320:
321: addKnuthElementsForBorderPaddingStart(returnList);
322:
323: returnList.add(new KnuthInlineBox(0, areaInfo.alignmentContext,
324: new LeafPosition(this , -1), true));
325: returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
326: false, new LeafPosition(this , -1), true));
327: if (alignment == EN_JUSTIFY || alignment == 0) {
328: returnList.add(new KnuthGlue(areaInfo.ipdArea.opt,
329: areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
330: areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
331: new LeafPosition(this , 0), false));
332: } else {
333: returnList.add(new KnuthGlue(areaInfo.ipdArea.opt, 0, 0,
334: new LeafPosition(this , 0), false));
335: }
336: returnList.add(new KnuthInlineBox(0, areaInfo.alignmentContext,
337: new LeafPosition(this , -1), true));
338:
339: addKnuthElementsForBorderPaddingEnd(returnList);
340:
341: setFinished(true);
342: return returnList;
343: }
344:
345: /** @see LeafNodeLayoutManager#addId */
346: protected void addId() {
347: getPSLM().addIDToPage(fobj.getId());
348: }
349:
350: /**
351: * @see org.apache.fop.datatypes.PercentBaseContext#getBaseLength(int, FObj)
352: */
353: public int getBaseLength(int lengthBase, FObj fobj) {
354: return getParent().getBaseLength(lengthBase,
355: getParent().getFObj());
356: }
357:
358: /**
359: * Returns the IPD of the content area
360: * @return the IPD of the content area
361: */
362: public int getContentAreaIPD() {
363: return contentAreaIPD;
364: }
365:
366: private void setContentAreaIPD(int contentAreaIPD) {
367: this.contentAreaIPD = contentAreaIPD;
368: }
369:
370: }
|