/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 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.
package org.apache.fop.fo.flow;
-// Java
-import java.util.List;
-
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.apache.fop.layoutmgr.BasicLinkLayoutManager;
/**
* The fo:basic-link formatting object.
}
/**
- * Return the "internal-destination" property.
+ * @return the "internal-destination" property.
*/
public String getInternalDestination() {
return internalDestination;
}
/**
- * Return the "external-destination" property.
+ * @return the "external-destination" property.
*/
public String getExternalDestination() {
return externalDestination;
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 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.
package org.apache.fop.fo.flow;
-// Java
-import java.util.ArrayList;
-import java.util.List;
-
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.properties.CommonFont;
import org.apache.fop.fo.properties.CommonRelativePosition;
import org.apache.fop.fo.properties.SpaceProperty;
-import org.apache.fop.layoutmgr.BidiLayoutManager;
-import org.apache.fop.layoutmgr.InlineLayoutManager;
-import org.apache.fop.layoutmgr.LayoutManager;
/**
* fo:bidi-override element.
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 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.
package org.apache.fop.fo.flow;
-// Java
-import java.util.ArrayList;
-import java.util.List;
-
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.properties.CommonRelativePosition;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.LengthRangeProperty;
-import org.apache.fop.layoutmgr.ICLayoutManager;
-import org.apache.fop.layoutmgr.LayoutManager;
/**
* Class modelling the fo:inline-container object.
*/
public class InlineContainer extends FObj {
+
// The value of properties relevant for fo:inline-container.
private CommonBorderPaddingBackground commonBorderPaddingBackground;
private CommonMarginInline commonMarginInline;
}
/**
- * Return the "id" property.
+ * @return the "id" property.
*/
public String getId() {
return id;
/*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 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.
package org.apache.fop.fo.flow;
-import java.util.List;
-
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.ColorType;
import org.apache.fop.datatypes.Length;
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.layoutmgr.InlineLayoutManager;
/**
* Class modelling the commonalities of several inline-level
* formatting objects.
*/
public abstract class InlineLevel extends FObjMixed {
+
+ // The value of properties relevant for inline-level FOs.
protected CommonBorderPaddingBackground commonBorderPaddingBackground;
protected CommonAccessibility commonAccessibility;
protected CommonMarginInline commonMarginInline;
}
/**
- * Return the Common Margin Properties-Inline.
+ * @return the Common Margin Properties-Inline.
*/
public CommonMarginInline getCommonMarginInline() {
return commonMarginInline;
}
/**
- * Return the Common Border, Padding, and Background Properties.
+ * @return the Common Border, Padding, and Background Properties.
*/
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return commonBorderPaddingBackground;
}
/**
- * Return the Common Font Properties.
+ * @return the Common Font Properties.
*/
public CommonFont getCommonFont() {
return commonFont;
}
/**
- * Return the "color" property.
+ * @return the "color" property.
*/
public ColorType getColor() {
return color;
}
/**
- * Return the "id" property.
+ * @return the "id" property.
*/
public String getId() {
return id;
}
/**
- * Return the Common Border, Padding, and Background Properties.
+ * @return the Common Border, Padding, and Background Properties.
*/
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return commonBorderPaddingBackground;
}
/**
- * Return the "line-height" property.
+ * @return the "line-height" property.
*/
public Length getLineHeight() {
return lineHeight;
}
/**
- * Return the "inline-progression-dimension" property.
+ * @return the "inline-progression-dimension" property.
*/
public LengthRangeProperty getInlineProgressionDimension() {
return inlineProgressionDimension;
}
/**
- * Return the "block-progression-dimension" property.
+ * @return the "block-progression-dimension" property.
*/
public LengthRangeProperty getBlockProgressionDimension() {
return blockProgressionDimension;
}
/**
- * Return the "height" property.
+ * @return the "height" property.
*/
public Length getHeight() {
return height;
}
/**
- * Return the "width" property.
+ * @return the "width" property.
*/
public Length getWidth() {
return width;
}
/**
- * Return the "content-height" property.
+ * @return the "content-height" property.
*/
public Length getContentHeight() {
return contentHeight;
}
/**
- * Return the "content-width" property.
+ * @return the "content-width" property.
*/
public Length getContentWidth() {
return contentWidth;
}
/**
- * Return the "scaling" property.
+ * @return the "scaling" property.
*/
public int getScaling() {
return scaling;
}
/**
- * Return the "vertical-align" property.
+ * @return the "vertical-align" property.
*/
public int getVerticalAlign() {
return verticalAlign;
}
/**
- * Return the "overflow" property.
+ * @return the "overflow" property.
*/
public int getOverflow() {
return overflow;
return FO_INSTREAM_FOREIGN_OBJECT;
}
- /**
- * @see org.apache.fop.fo.FObj#getLayoutDimension(org.apache.fop.datatypes.PercentBase.DimensionType)
- */
+ /** @see org.apache.fop.fo.FObj */
public Number getLayoutDimension(PercentBase.LayoutDimension key) {
if (key == PercentBase.IMAGE_INTRINSIC_WIDTH) {
return new Integer(getIntrinsicWidth());
}
}
+ /** @see org.apache.fop.fo.FONode#addChildNode(org.apache.fop.fo.FONode) */
+ protected void addChildNode(FONode child) throws FOPException {
+ super.addChildNode(child);
+ }
}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 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.
package org.apache.fop.fo.flow;
-// Java
-import java.util.List;
-
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.LengthRangeProperty;
import org.apache.fop.fo.properties.SpaceProperty;
-import org.apache.fop.layoutmgr.LeaderLayoutManager;
/**
* Class modelling fo:leader object.
// use inline layout manager to create inline areas
// add the inline parent multiple times until leader full
break;
+ default:
+ throw new RuntimeException("Invalid leader pattern: " + leaderPattern);
}
// letterSpacing = pList.get(PR_LETTER_SPACING);
// textShadow = pList.get(PR_TEXT_SHADOW);
/**
- * Return the "id" property.
+ * @return the "id" property.
*/
public String getId() {
return id;
}
/**
- * Return the "rule-style" property.
+ * @return the "rule-style" property.
*/
public int getRuleStyle() {
return ruleStyle;
}
/**
- * Return the "rule-thickness" property.
+ * @return the "rule-thickness" property.
*/
public Length getRuleThickness() {
return ruleThickness;
}
/**
- * Return the "leader-alignment" property.
+ * @return the "leader-alignment" property.
*/
public int getLeaderAlignment() {
return leaderAlignment;
}
/**
- * Return the "leader-length" property.
+ * @return the "leader-length" property.
*/
public LengthRangeProperty getLeaderLength() {
return leaderLength;
}
/**
- * Return the "leader-pattern" property.
+ * @return the "leader-pattern" property.
*/
public int getLeaderPattern() {
return leaderPattern;
}
/**
- * Return the "leader-pattern-width" property.
+ * @return the "leader-pattern-width" property.
*/
public Length getLeaderPatternWidth() {
return leaderPatternWidth;
}
/**
- * Return the "vertical-align" property.
+ * @return the "vertical-align" property.
*/
public int getVerticalAlign() {
return verticalAlign;
+++ /dev/null
-/*
- * Copyright 1999-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 org.apache.fop.fo.flow.BasicLink;
-import org.apache.fop.area.inline.InlineParent;
-import org.apache.fop.area.Trait;
-import org.apache.fop.area.LinkResolver;
-import org.apache.fop.area.PageViewport;
-
-/**
- * LayoutManager for the fo:basic-link formatting object
- */
-public class BasicLinkLayoutManager extends InlineLayoutManager {
- private BasicLink fobj;
-
- /**
- * Create an fo:basic-link layout manager.
- *
- * @param node the formatting object that creates the area
- */
- public BasicLinkLayoutManager(BasicLink node) {
- super(node);
- fobj = node;
- }
-
- protected InlineParent createArea() {
- InlineParent area = super.createArea();
- setupBasicLinkArea(parentLM, area);
- return area;
- }
-
- private void setupBasicLinkArea(LayoutManager parentLM,
- InlineParent area) {
- if (fobj.getExternalDestination() != null) {
- area.addTrait(Trait.EXTERNAL_LINK, fobj.getExternalDestination());
- } else {
- String idref = fobj.getInternalDestination();
- PageViewport page = getPSLM().getFirstPVWithID(idref);
- if (page != null) {
- area.addTrait(Trait.INTERNAL_LINK, page.getKey());
- } else {
- LinkResolver res = new LinkResolver(idref, area);
- getPSLM().addUnresolvedArea(idref, res);
- }
- }
- }
-}
-
+++ /dev/null
-/*
- * Copyright 1999-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.ArrayList;
-import java.util.List;
-
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.fo.flow.BidiOverride;
-
-
-/**
- * If this bidi has a different writing mode direction
- * ltr or rtl than its parent writing mode then this
- * reverses the inline areas (at the character level).
- */
-public class BidiLayoutManager extends LeafNodeLayoutManager {
-
- private List children;
-
- public BidiLayoutManager(BidiOverride node, InlineLayoutManager cLM) {
- super(node);
- children = new ArrayList();
-/*
- for (int count = cLM.size() - 1; count >= 0; count--) {
- InlineArea ia = cLM.get(count);
- if (ia instanceof Word) {
- // reverse word
- Word word = (Word) ia;
- StringBuffer sb = new StringBuffer(word.getWord());
- word.setWord(sb.reverse().toString());
- }
- children.add(ia);
- }
-*/
- }
-
- public int size() {
- return children.size();
- }
-
- public InlineArea get(int index) {
- return (InlineArea) children.get(index);
- }
-
-}
import java.util.List;
import org.apache.fop.fonts.Font;
+import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.LineArea;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager;
import org.apache.fop.traits.MinOptMax;
/**
// this class represent a feasible breaking point
- protected class KnuthNode {
+ public class KnuthNode {
/** index of the breakpoint represented by this node */
public int position;
+++ /dev/null
-/*
- * Copyright 1999-2005 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 org.apache.fop.fo.flow.Character;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.Trait;
-import org.apache.fop.traits.MinOptMax;
-import org.apache.fop.traits.SpaceVal;
-
-import java.util.List;
-import java.util.LinkedList;
-
-/**
- * LayoutManager for the fo:character formatting object
- */
-public class CharacterLayoutManager extends LeafNodeLayoutManager {
- private Character fobj;
- private MinOptMax letterSpaceIPD;
- private int hyphIPD;
- private Font fs;
-
- /**
- * Constructor
- *
- * @param node the fo:character formatting object
- * @todo better null checking of node
- */
- public CharacterLayoutManager(Character node) {
- super(node);
- fobj = node;
- InlineArea inline = getCharacterInlineArea(node);
- setCurrentArea(inline);
- setAlignment(fobj.getVerticalAlign());
- fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
-
- SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing());
- letterSpaceIPD = ls.getSpace();
- hyphIPD = fs.getCharWidth(fobj.getCommonHyphenation().hyphenationCharacter);
- }
-
- private InlineArea getCharacterInlineArea(Character node) {
- org.apache.fop.area.inline.Character ch =
- new org.apache.fop.area.inline.Character(node.getCharacter());
- TraitSetter.addTextDecoration(ch, fobj.getTextDecoration());
- return ch;
- }
-
- /**
- * Offset this area.
- * Offset the inline area in the bpd direction when adding the
- * inline area.
- * This is used for vertical alignment.
- * Subclasses should override this if necessary.
- * @param area the inline area to be updated
- * @param context the layout context used for adding the area
- */
- protected void offsetArea(InlineArea area, LayoutContext context) {
- int bpd = area.getBPD();
- switch (verticalAlignment) {
- case EN_MIDDLE:
- area.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
- break;
- case EN_TOP:
- area.setOffset(fs.getAscender());
- break;
- case EN_BOTTOM:
- area.setOffset(context.getLineHeight() - bpd + fs.getAscender());
- break;
- case EN_BASELINE:
- default:
- area.setOffset(context.getBaseline());
- break;
- }
- }
-
- public LinkedList getNextKnuthElements(LayoutContext context,
- int alignment) {
- MinOptMax ipd;
- curArea = get(context);
- LinkedList returnList = new LinkedList();
-
- if (curArea == null) {
- setFinished(true);
- return null;
- }
-
- ipd = new MinOptMax(fs.getCharWidth(((org.apache.fop.area.inline.Character) curArea).getChar().charAt(0)));
-
- curArea.setIPD(ipd.opt);
- curArea.setBPD(fs.getAscender() - fs.getDescender());
-
- // offset is set in the offsetArea() method
- //curArea.setOffset(textInfo.fs.getAscender());
- //curArea.setOffset(context.getBaseline());
-
- curArea.addTrait(Trait.FONT_NAME, fs.getFontName());
- curArea.addTrait(Trait.FONT_SIZE, new Integer(fs.getFontSize()));
- curArea.addTrait(Trait.COLOR, fobj.getColor());
-
- int bpd = curArea.getBPD();
- int lead = 0;
- int total = 0;
- int middle = 0;
- switch (verticalAlignment) {
- case EN_MIDDLE : middle = bpd / 2 ;
- break;
- case EN_TOP : // fall through
- case EN_BOTTOM : total = bpd;
- break;
- case EN_BASELINE: // fall through
- default : lead = fs.getAscender();
- total = bpd;
- break;
- }
-
- // create the AreaInfo object to store the computed values
- areaInfo = new AreaInfo((short) 0, ipd, false,
- lead, total, middle);
-
- // node is a fo:Character
- if (letterSpaceIPD.min == letterSpaceIPD.max) {
- // constant letter space, only return a box
- returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false));
- } else {
- // adjustable letter space, return a sequence of elements;
- // at the moment the character is supposed to have no letter spaces,
- // but returning this sequence allows us to change only one element
- // if addALetterSpaceTo() is called
- returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false));
- returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add(new KnuthGlue(0, 0, 0,
- new LeafPosition(this, -1), true));
- returnList.add(new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), true));
- }
-
- setFinished(true);
- return returnList;
- }
-
- public void getWordChars(StringBuffer sbChars, Position bp) {
- sbChars.append
- (((org.apache.fop.area.inline.Character) curArea).getChar());
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- if (hc.getNextHyphPoint() == 1) {
- // the character ends a syllable
- areaInfo.bHyphenated = true;
- bSomethingChanged = true;
- } else {
- // hc.getNextHyphPoint() returned -1 (no more hyphenation points)
- // or a number > 1;
- // the character does not end a syllable
- }
- hc.updateOffset(1);
- }
-
- public boolean applyChanges(List oldList) {
- setFinished(false);
- if (bSomethingChanged) {
- // there is nothing to do,
- // possible changes have already been applied
- // in the hyphenate() method
- return true;
- } else {
- return false;
- }
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- int flaggedPenalty,
- int alignment) {
- if (isFinished()) {
- return null;
- }
-
- LinkedList returnList = new LinkedList();
-
- if (letterSpaceIPD.min == letterSpaceIPD.max
- || areaInfo.iLScount == 0) {
- // constant letter space, or no letter space
- returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false));
- if (areaInfo.bHyphenated) {
- returnList.add
- (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
- new LeafPosition(this, -1), false));
- }
- } else {
- // adjustable letter space
- returnList.add
- (new KnuthInlineBox(areaInfo.ipdArea.opt
- - areaInfo.iLScount * letterSpaceIPD.opt,
- areaInfo.lead, areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false));
- returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(areaInfo.iLScount * letterSpaceIPD.opt,
- areaInfo.iLScount * letterSpaceIPD.max - letterSpaceIPD.opt,
- areaInfo.iLScount * letterSpaceIPD.opt - letterSpaceIPD.min,
- new LeafPosition(this, -1), true));
- returnList.add(new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), true));
- if (areaInfo.bHyphenated) {
- returnList.add
- (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
- new LeafPosition(this, -1), false));
- }
- }
-
- setFinished(true);
- return returnList;
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
-
+++ /dev/null
-/*
- * Copyright 1999-2005 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 org.apache.fop.apps.FOUserAgent;
-import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.pagination.Title;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.LineArea;
-import org.apache.fop.area.inline.InlineArea;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.ArrayList;
-import org.apache.fop.traits.MinOptMax;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Content Layout Manager.
- * For use with objects that contain inline areas such as
- * leader use-content and title.
- */
-public class ContentLayoutManager implements InlineLevelLayoutManager {
- private FOUserAgent userAgent;
- private Area holder;
- private int stackSize;
- private LayoutManager parentLM;
- private InlineLevelLayoutManager childLM = null;
-
- /**
- * logging instance
- */
- protected static Log log = LogFactory.getLog(LayoutManager.class);
-
- /**
- * Constructs a new ContentLayoutManager
- *
- * @param area The parent area
- */
- public ContentLayoutManager(Area area, LayoutManager parentLM) {
- holder = area;
- this.parentLM = parentLM;
- }
-
- /**
- * Constructor using a fo:title formatting object and its
- * PageSequenceLayoutManager parent.
- */
- public ContentLayoutManager(Title foTitle, PageSequenceLayoutManager pslm) {
- // get breaks then add areas to title
- this.parentLM = pslm;
- holder = new LineArea();
-
- setUserAgent(foTitle.getUserAgent());
-
- // use special layout manager to add the inline areas
- // to the Title.
- InlineLayoutManager lm;
- lm = new InlineLayoutManager(foTitle);
- addChildLM(lm);
- fillArea(lm);
- }
-
- public void fillArea(LayoutManager curLM) {
-
- int ipd = 1000000;
-
- LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA);
- childLC.setLeadingSpace(new SpaceSpecifier(false));
- childLC.setTrailingSpace(new SpaceSpecifier(false));
- // set stackLimit for lines
- childLC.setStackLimit(new MinOptMax(ipd));
- childLC.setRefIPD(ipd);
-
- int lineHeight = 14000;
- int lead = 12000;
- int follow = 2000;
-
- 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;
-
- stackSize = 0;
-
- LinkedList contentList =
- getNextKnuthElements(childLC, Constants.EN_START);
- ListIterator contentIter = contentList.listIterator();
- while (contentIter.hasNext()) {
- KnuthElement element = (KnuthElement) contentIter.next();
- if (element.isBox()) {
- KnuthInlineBox box = (KnuthInlineBox) element;
- if (box.getLead() > lineLead) {
- lineLead = box.getLead();
- }
- if (box.getTotal() > maxtb) {
- maxtb = box.getTotal();
- }
- // Is this needed? cf. LineLM.makeLineBreakPosition
- // if (box.getMiddle() > lineLead) {
- // lineLead = box.getMiddle();
- // }
- if (box.getMiddle() > middlefollow) {
- middlefollow = box.getMiddle();
- }
- }
- }
-
- if (maxtb - lineLead > middlefollow) {
- middlefollow = maxtb - lineLead;
- }
-
- LayoutContext lc = new LayoutContext(0);
- lc.setBaseline(lineLead);
- lc.setLineHeight(lineHeight);
-
- lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
- lc.setLeadingSpace(new SpaceSpecifier(false));
- lc.setTrailingSpace(new SpaceSpecifier(false));
- KnuthPossPosIter contentPosIter =
- new KnuthPossPosIter(contentList, 0, contentList.size());
- curLM.addAreas(contentPosIter, lc);
- }
-
- 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;
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public Area getParentArea(Area childArea) {
- return holder;
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
- **/
- public void addChildArea(Area childArea) {
- holder.addChildArea(childArea);
- }
-
- /**
- * Set the user agent.
- *
- * @param ua the user agent
- */
- public void setUserAgent(FOUserAgent ua) {
- userAgent = ua;
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#getUserAgent()
- */
- public FOUserAgent getUserAgent() {
- return userAgent;
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void setParent(LayoutManager lm) {
- parentLM = lm;
- }
-
- public LayoutManager getParent() {
- return this.parentLM;
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public boolean isFinished() {
- return false;
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void setFinished(boolean isFinished) {
- //to be done
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void resetPosition(Position position) {
- //to be done
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#createNextChildLMs
- */
- public boolean createNextChildLMs(int pos) {
- return false;
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs
- */
- public List getChildLMs() {
- List childLMs = new ArrayList(1);
- childLMs.add(childLM);
- return childLMs;
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#addChildLM
- */
- public void addChildLM(LayoutManager lm) {
- if (lm == null) {
- return;
- }
- lm.setParent(this);
- childLM = (InlineLevelLayoutManager)lm;
- log.trace(this.getClass().getName()
- + ": Adding child LM " + lm.getClass().getName());
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#addChildLMs
- */
- public void addChildLMs(List newLMs) {
- if (newLMs == null || newLMs.size() == 0) {
- return;
- }
- ListIterator iter = newLMs.listIterator();
- while (iter.hasNext()) {
- LayoutManager lm = (LayoutManager) iter.next();
- addChildLM(lm);
- }
- }
-
- 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 contentList;
- }
-
- public List addALetterSpaceTo(List oldList) {
- return oldList;
- }
-
- public void getWordChars(StringBuffer sbChars, Position pos) {
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- }
-
- public boolean applyChanges(List oldList) {
- return false;
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- /*int flaggedPenalty,*/
- int alignment) {
- return null;
- }
-
- public PageSequenceLayoutManager getPSLM() {
- return parentLM.getPSLM();
- }
-}
-
+++ /dev/null
-/*
- * Copyright 1999-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;
-
-// Java
-import java.awt.geom.Rectangle2D;
-
-// FOP
-import org.apache.fop.area.inline.Image;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.Viewport;
-import org.apache.fop.datatypes.Length;
-import org.apache.fop.fo.flow.ExternalGraphic;
-
-/**
- * LayoutManager for the fo:external-graphic formatting object
- */
-public class ExternalGraphicLayoutManager extends LeafNodeLayoutManager {
-
- private ExternalGraphic fobj;
-
- private int breakAfter;
- private int breakBefore;
- private int align;
- private int startIndent;
- private int endIndent;
- private int spaceBefore;
- private int spaceAfter;
- private int viewWidth = -1;
- private int viewHeight = -1;
- private boolean clip = false;
- private Rectangle2D placement = null;
-
- /**
- * Constructor
- *
- * @param node the fo:external-graphic formatting object that creates the area
- */
- public ExternalGraphicLayoutManager(ExternalGraphic node) {
- super(node);
- fobj = node;
- setup();
- InlineArea area = getExternalGraphicInlineArea();
- setCurrentArea(area);
- setAlignment(fobj.getVerticalAlign());
- setLead(viewHeight);
- }
-
- /**
- * Setup this image.
- * This gets the sizes for the image and the dimensions and clipping.
- * @todo see if can simplify property handling logic
- */
- private void setup() {
- // assume lr-tb for now and just use the .optimum value of the range
- Length ipd = fobj.getInlineProgressionDimension().getOptimum().getLength();
- if (ipd.getEnum() != EN_AUTO) {
- viewWidth = ipd.getValue();
- } else {
- ipd = fobj.getWidth();
- if (ipd.getEnum() != EN_AUTO) {
- viewWidth = ipd.getValue();
- }
- }
- Length bpd = fobj.getBlockProgressionDimension().getOptimum().getLength();
- if (bpd.getEnum() != EN_AUTO) {
- viewHeight = bpd.getValue();
- } else {
- bpd = fobj.getHeight();
- if (bpd.getEnum() != EN_AUTO) {
- viewHeight = bpd.getValue();
- }
- }
-
- int cwidth = -1;
- int cheight = -1;
- Length ch = fobj.getContentHeight();
- if (ch.getEnum() != EN_AUTO) {
- if (ch.getEnum() == EN_SCALE_TO_FIT) {
- if (viewHeight != -1) {
- cheight = viewHeight;
- }
- } else {
- cheight = ch.getValue();
- }
- }
- Length cw = fobj.getContentWidth();
- if (cw.getEnum() != EN_AUTO) {
- if (cw.getEnum() == EN_SCALE_TO_FIT) {
- if (viewWidth != -1) {
- cwidth = viewWidth;
- }
- } else {
- cwidth = cw.getValue();
- }
- }
-
- int scaling = fobj.getScaling();
- if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
- if (cwidth == -1 && cheight == -1) {
- cwidth = fobj.getIntrinsicWidth();
- cheight = fobj.getIntrinsicHeight();
- } else if (cwidth == -1) {
- cwidth = (int)(fobj.getIntrinsicWidth() * (double)cheight
- / fobj.getIntrinsicHeight());
- } else if (cheight == -1) {
- cheight = (int)(fobj.getIntrinsicHeight() * (double)cwidth
- / fobj.getIntrinsicWidth());
- } else {
- // adjust the larger
- double rat1 = cwidth / fobj.getIntrinsicWidth();
- double rat2 = cheight / fobj.getIntrinsicHeight();
- if (rat1 < rat2) {
- // reduce cheight
- cheight = (int)(rat1 * fobj.getIntrinsicHeight());
- } else if (rat1 > rat2) {
- cwidth = (int)(rat2 * fobj.getIntrinsicWidth());
- }
- }
- }
-
- if (viewWidth == -1) {
- viewWidth = cwidth;
- }
- if (viewHeight == -1) {
- viewHeight = cheight;
- }
-
- if (cwidth > viewWidth || cheight > viewHeight) {
- int overflow = fobj.getOverflow();
- if (overflow == EN_HIDDEN) {
- clip = true;
- } else if (overflow == EN_ERROR_IF_OVERFLOW) {
- fobj.getLogger().error("Image: " + fobj.getURL()
- + " overflows the viewport, clipping to viewport");
- clip = true;
- }
- }
-
- int xoffset = 0;
- int yoffset = 0;
- switch(fobj.getDisplayAlign()) {
- case EN_BEFORE:
- break;
- case EN_AFTER:
- yoffset = viewHeight - cheight;
- break;
- case EN_CENTER:
- yoffset = (viewHeight - cheight) / 2;
- break;
- case EN_AUTO:
- default:
- break;
- }
-
- switch(fobj.getTextAlign()) {
- case EN_CENTER:
- xoffset = (viewWidth - cwidth) / 2;
- break;
- case EN_END:
- xoffset = viewWidth - cwidth;
- break;
- case EN_START:
- break;
- case EN_JUSTIFY:
- default:
- break;
- }
- placement = new Rectangle2D.Float(xoffset, yoffset, cwidth, cheight);
- }
-
- /**
- * Get the inline area for this external grpahic.
- * This creates the image area and puts it inside a viewport.
- *
- * @return the viewport containing the image area
- */
- public InlineArea getExternalGraphicInlineArea() {
- Image imArea = new Image(fobj.getSrc());
- Viewport vp = new Viewport(imArea);
- vp.setIPD(viewWidth);
- vp.setBPD(viewHeight);
- vp.setClip(clip);
- vp.setContentPosition(placement);
- vp.setOffset(0);
-
- // Common Border, Padding, and Background Properties
- TraitSetter.addBorders(vp, fobj.getCommonBorderPaddingBackground());
- TraitSetter.addBackground(vp, fobj.getCommonBorderPaddingBackground());
-
- return vp;
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
-
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.pagination.Flow;
+import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockParent;
+++ /dev/null
-/*
- * Copyright 1999-2005 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;
-import java.util.ListIterator;
-
-import org.apache.fop.fo.flow.Footnote;
-
-public class FootnoteLayoutManager extends AbstractLayoutManager
- implements InlineLevelLayoutManager {
-
- private Footnote footnote;
- private InlineStackingLayoutManager citationLM;
- private FootnoteBodyLayoutManager bodyLM;
-
- /**
- * Create a new footnote layout manager.
- * @param node footnote to create the layout manager for
- */
- public FootnoteLayoutManager(Footnote node) {
- super(node);
- footnote = node;
-
- // create an InlineStackingLM handling the fo:inline child of fo:footnote
- citationLM = new InlineStackingLayoutManager(footnote.getFootnoteCitation());
-
- // create a FootnoteBodyLM handling the fo:footnote-body child of fo:footnote
- bodyLM = new FootnoteBodyLayoutManager(footnote.getFootnoteBody());
- }
-
- public LinkedList getNextKnuthElements(LayoutContext context,
- int alignment) {
- // this is the only method that must be implemented:
- // all other methods will never be called, as the returned elements
- // contain Positions created by the citationLM, so its methods will
- // be called instead
-
- // set the citationLM parent to be this LM's parent
- citationLM.setParent(getParent());
- bodyLM.setParent(this);
-
- // get Knuth elements representing the footnote citation
- LinkedList returnedList = new LinkedList();
- while (!citationLM.isFinished()) {
- LinkedList partialList = citationLM.getNextKnuthElements(context, alignment);
- if (partialList != null) {
- returnedList.addAll(partialList);
- }
- }
- if (returnedList.size() == 0) {
- //Inline part of the footnote is empty. Need to send back an auxiliary
- //zero-width, zero-height inline box so the footnote gets painted.
- returnedList.add(new KnuthInlineBox(0, 0, 0, 0, null, true));
- }
- setFinished(true);
-
- addAnchor(returnedList);
-
- return returnedList;
- }
-
- private void addAnchor(LinkedList citationList) {
- // find the last box in the sequence, and add a reference
- // to the FootnoteBodyLM
- ListIterator citationIterator = citationList.listIterator(citationList.size());
- KnuthInlineBox lastBox = null;
- while (citationIterator.hasPrevious() && lastBox == null) {
- KnuthElement element = (KnuthElement) citationIterator.previous();
- if (element instanceof KnuthInlineBox) {
- lastBox = (KnuthInlineBox) element;
- }
- }
- if (lastBox != null) {
- lastBox.setFootnoteBodyLM(bodyLM);
- }
- }
-
- public List addALetterSpaceTo(List oldList) {
- log.warn("null implementation of addALetterSpaceTo() called!");
- return oldList;
- }
-
- public void getWordChars(StringBuffer sbChars, Position pos) {
- log.warn("null implementation of getWordChars() called!");
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- log.warn("null implementation of hyphenate called!");
- }
-
- public boolean applyChanges(List oldList) {
- log.warn("null implementation of applyChanges() called!");
- return false;
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- int alignment) {
- log.warn("null implementation of getChangeKnuthElement() called!");
- return null;
- }
-}
+++ /dev/null
-/*
- * Copyright 1999-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;
-
-/**
- * This class is used to pass information to the getNextBreakPoss()
- * method concerning hyphenation. A reference to an instance of the
- * class is contained in the LayoutContext object passed to each
- * LayoutManager. It contains information concerning the hyphenation
- * points in a word and the how many of those have previously been
- * processed by a Layout Manager to generate size information.
- */
-public class HyphContext {
- private int[] hyphPoints;
- private int currentOffset = 0;
- private int currentIndex = 0;
-
- public HyphContext(int[] hyphPoints) {
- this.hyphPoints = hyphPoints;
- }
-
- public int getNextHyphPoint() {
- for (; currentIndex < hyphPoints.length; currentIndex++) {
- if (hyphPoints[currentIndex] > currentOffset) {
- return (hyphPoints[currentIndex] - currentOffset);
- }
- }
- return -1; // AT END!
- }
-
- public boolean hasMoreHyphPoints() {
- for (; currentIndex < hyphPoints.length; currentIndex++) {
- if (hyphPoints[currentIndex] > currentOffset) {
- return true;
- }
- }
- return false;
- }
-
- public void updateOffset(int iCharsProcessed) {
- currentOffset += iCharsProcessed;
- }
-}
+++ /dev/null
-/*
- * Copyright 1999-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;
-
-// Java
-import java.util.List;
-
-// FOP
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.fo.flow.InlineContainer;
-/**
- * This creates a single inline container area after
- * laying out the child block areas. All footnotes, floats
- * and id areas are maintained for later retrieval.
- */
-public class ICLayoutManager extends LeafNodeLayoutManager {
- private InlineContainer fobj;
- private List childrenLM;
-
- public ICLayoutManager(InlineContainer node, List childLM) {
- super(node);
- fobj = node;
- childrenLM = childLM;
- }
-
- public InlineArea get(int index) {
- return null;
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
+++ /dev/null
-/*
- * Copyright 1999-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.ListIterator;
-import java.util.LinkedList;
-
-import org.apache.fop.fo.flow.InlineLevel;
-import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.fo.properties.CommonMarginInline;
-import org.apache.fop.fo.properties.SpaceProperty;
-import org.apache.fop.traits.MinOptMax;
-import org.apache.fop.traits.SpaceVal;
-
-/**
- * LayoutManager for objects which stack children in the inline direction,
- * such as Inline or Line
- */
-public class InlineLayoutManager extends InlineStackingLayoutManager
- implements InlineLevelLayoutManager {
- private InlineLevel fobj;
-
- private CommonMarginInline inlineProps = null;
- private CommonBorderPaddingBackground borderProps = null;
-
- /**
- * 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
- */
- // The node should be FObjMixed
- public InlineLayoutManager(InlineLevel node) {
- super(node);
- fobj = node;
- initialize();
- }
-
- private void initialize() {
- inlineProps = fobj.getCommonMarginInline();
- borderProps = fobj.getCommonBorderPaddingBackground();
-
- int iPad = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false);
- iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
- false);
- iPad += borderProps.getPadding(CommonBorderPaddingBackground.AFTER, false);
- iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.AFTER, false);
- extraBPD = new MinOptMax(iPad);
- }
-
- protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
- int iBP = borderProps.getPadding(CommonBorderPaddingBackground.START,
- bNotFirst);
- iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.START,
- bNotFirst);
- iBP += borderProps.getPadding(CommonBorderPaddingBackground.END, bNotLast);
- iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, bNotLast);
- return new MinOptMax(iBP);
- }
-
-
- protected boolean hasLeadingFence(boolean bNotFirst) {
- int iBP = borderProps.getPadding(CommonBorderPaddingBackground.START,
- bNotFirst);
- iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.START,
- bNotFirst);
- return (iBP > 0);
- }
-
- protected boolean hasTrailingFence(boolean bNotLast) {
- int iBP = borderProps.getPadding(CommonBorderPaddingBackground.END, bNotLast);
- iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, bNotLast);
- return (iBP > 0);
- }
-
- protected SpaceProperty getSpaceStart() {
- return inlineProps.spaceStart;
- }
- protected SpaceProperty getSpaceEnd() {
- return inlineProps.spaceEnd;
- }
-
- protected void setTraits(boolean bNotFirst, boolean bNotLast) {
-
- // Add border and padding to current area and set flags (FIRST, LAST ...)
- TraitSetter.setBorderPaddingTraits(getCurrentArea(),
- borderProps, bNotFirst, bNotLast);
-
- if (borderProps != null) {
- TraitSetter.addBorders(getCurrentArea(), borderProps);
- TraitSetter.addBackground(getCurrentArea(), borderProps);
- }
- }
-
- 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-2005 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.List;
-
-/**
- * The interface for LayoutManagers which generate inline areas
- */
-public interface InlineLevelLayoutManager extends LayoutManager {
-
- /**
- * Tell the LM to modify its data, adding a letter space
- * to the word fragment represented by the given elements,
- * and returning the corrected elements
- *
- * @param oldList the elements which must be given one more letter space
- * @return the new elements replacing the old ones
- */
- List addALetterSpaceTo(List oldList);
-
- /**
- * 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);
-
-}
+++ /dev/null
-/*
- * Copyright 1999-2005 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.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.HashMap;
-
-import org.apache.fop.fo.FObj;
-import org.apache.fop.fo.properties.SpaceProperty;
-import org.apache.fop.traits.SpaceVal;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.InlineParent;
-import org.apache.fop.area.inline.Space;
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * Class modelling the commonalities of layoutmanagers for objects
- * which stack children in the inline direction, such as Inline or
- * Line. It should not be instantiated directly.
- */
-public class InlineStackingLayoutManager extends AbstractLayoutManager
- implements InlineLevelLayoutManager {
-
-
- private static class StackingIter extends PositionIterator {
-
- StackingIter(Iterator parentIter) {
- super(parentIter);
- }
-
- protected LayoutManager getLM(Object nextObj) {
- return ((Position) nextObj).getLM();
- }
-
- protected Position getPos(Object nextObj) {
- return ((Position) nextObj);
- }
- }
-
-
- /**
- * Size of any start or end borders and padding.
- */
- private MinOptMax allocIPD = new MinOptMax(0);
-
- /**
- * Size of border and padding in BPD (ie, before and after).
- */
- protected MinOptMax extraBPD;
-
- private Area currentArea; // LineArea or InlineParent
-
- //private BreakPoss prevBP;
- protected LayoutContext childLC;
-
- private LayoutManager lastChildLM = null; // Set when return last breakposs
- private boolean bAreaCreated = false;
-
- //private LayoutManager currentLM = null;
-
- /** Used to store previous content IPD for each child LM. */
- private HashMap hmPrevIPD = new HashMap();
-
- /**
- * Create an inline stacking layout manager.
- * This is used for fo's that create areas that
- * contain inline areas.
- *
- * @param node the formatting object that creates the area
- */
- protected InlineStackingLayoutManager(FObj node) {
- super(node);
- extraBPD = new MinOptMax(0);
- }
-
- /**
- * Set the iterator.
- *
- * @param iter the iterator for this LM
- */
- public void setLMiter(ListIterator iter) {
- childLMiter = iter;
- }
-
- protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
- return new MinOptMax(0);
- }
-
-
- protected boolean hasLeadingFence(boolean bNotFirst) {
- return false;
- }
-
- protected boolean hasTrailingFence(boolean bNotLast) {
- return false;
- }
-
- protected SpaceProperty getSpaceStart() {
- return null;
- }
-
- protected SpaceProperty getSpaceEnd() {
- return null;
- }
-
- /**
- * Reset position for returning next BreakPossibility.
- * @param prevPos a Position returned by this layout manager
- * representing a potential break decision.
- */
- public void resetPosition(Position prevPos) {
- if (prevPos != null) {
- // ASSERT (prevPos.getLM() == this)
- if (prevPos.getLM() != this) {
- //getLogger().error(
- // "InlineStackingLayoutManager.resetPosition: " +
- // "LM mismatch!!!");
- }
- // Back up the child LM Position
- Position childPos = prevPos.getPosition();
- reset(childPos);
- /*
- if (prevBP != null
- && prevBP.getLayoutManager() != childPos.getLM()) {
- childLC = null;
- }
- prevBP = new BreakPoss(childPos);
- */
- } else {
- // Backup to start of first child layout manager
- //prevBP = null;
- // super.resetPosition(prevPos);
- reset(prevPos);
- // If any areas created, we are restarting!
- bAreaCreated = false;
- }
- // Do we need to reset some context like pending or prevContent?
- // What about prevBP?
- }
-
- protected MinOptMax getPrevIPD(LayoutManager lm) {
- return (MinOptMax) hmPrevIPD.get(lm);
- }
-
- /**
- * Clear the previous IPD calculation.
- */
- protected void clearPrevIPD() {
- hmPrevIPD.clear();
- }
-
- protected InlineParent createArea() {
- return new InlineParent();
- }
-
- /**
- * Generate and add areas to parent area.
- * Set size of each area. This should only create and return one
- * inline area for any inline parent area.
- *
- * @param parentIter Iterator over Position information returned
- * by this LayoutManager.
- * @param dSpaceAdjust Factor controlling how much extra space to add
- * in order to justify the line.
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext context) {
- InlineParent parent = createArea();
- parent.setBPD(context.getLineHeight());
- parent.setOffset(0);
- setCurrentArea(parent);
-
- setChildContext(new LayoutContext(context)); // Store current value
-
- // If has fence, make a new leadingSS
- /* How to know if first area created by this LM? Keep a count and
- * reset it if getNextBreakPoss() is called again.
- */
- if (hasLeadingFence(bAreaCreated)) {
- 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()));
- }
-
- // "unwrap" the NonLeafPositions stored in parentIter
- // and put them in a new list;
- // also set lastLM to be the LayoutManager which created
- // the last Position: if the LAST_AREA flag is set in context,
- // it must be also set in the LayoutContext given to lastLM,
- // but unset in the LayoutContext given to the other LMs
- LinkedList positionList = new LinkedList();
- NonLeafPosition pos;
- LayoutManager lastLM = null; // last child LM in this iterator
- while (parentIter.hasNext()) {
- pos = (NonLeafPosition) parentIter.next();
- lastLM = pos.getPosition().getLM();
- positionList.add(pos.getPosition());
- }
-
- StackingIter childPosIter
- = new StackingIter(positionList.listIterator());
-
- LayoutManager prevLM = null;
- InlineLevelLayoutManager childLM;
- while ((childLM = (InlineLevelLayoutManager) 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;
- }
-
- /* If has trailing fence,
- * resolve trailing space specs from descendants.
- * Otherwise, propagate any trailing space specs to parent LM via
- * the context object.
- * If the last child LM called return ISLAST in the context object
- * and it is the last child LM for this LM, then this must be
- * the last area for the current LM also.
- */
- boolean bIsLast =
- (getContext().isLastArea() && prevLM == lastChildLM);
- if (hasTrailingFence(bIsLast)) {
- addSpace(getCurrentArea(),
- getContext().getTrailingSpace().resolve(false),
- getContext().getSpaceAdjust());
- context.setTrailingSpace(new SpaceSpecifier(false));
- } else {
- // Propagate trailing space-spec sequence to parent LM in context
- context.setTrailingSpace(getContext().getTrailingSpace());
- }
- // Add own trailing space to parent context (or set on area?)
- if (context.getTrailingSpace() != null && getSpaceEnd() != null) {
- context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd()));
- }
- setTraits(bAreaCreated, !bIsLast);
-
- parentLM.addChildArea(getCurrentArea());
- context.setFlags(LayoutContext.LAST_AREA, bIsLast);
- bAreaCreated = true;
- }
-
- protected Area getCurrentArea() {
- return currentArea;
- }
-
- protected void setCurrentArea(Area area) {
- currentArea = area;
- }
-
- protected void setTraits(boolean bNotFirst, boolean bNotLast) {
-
- }
-
- public void addChildArea(Area childArea) {
- // Make sure childArea is inline area
- if (childArea instanceof InlineArea) {
- Area parent = getCurrentArea();
- if (getContext().resolveLeadingSpace()) {
- addSpace(parent,
- getContext().getLeadingSpace().resolve(false),
- getContext().getSpaceAdjust());
- }
- parent.addChildArea(childArea);
- }
- }
-
- protected void setChildContext(LayoutContext lc) {
- childLC = lc;
- }
-
- // Current child layout context
- protected LayoutContext getContext() {
- return childLC;
- }
-
- protected void addSpace(Area parentArea, MinOptMax spaceRange,
- double dSpaceAdjust) {
- if (spaceRange != null) {
- int iAdjust = spaceRange.opt;
- if (dSpaceAdjust > 0.0) {
- // Stretch by factor
- iAdjust += (int) ((double) (spaceRange.max
- - spaceRange.opt) * dSpaceAdjust);
- } else if (dSpaceAdjust < 0.0) {
- // Shrink by factor
- iAdjust += (int) ((double) (spaceRange.opt
- - spaceRange.min) * dSpaceAdjust);
- }
- if (iAdjust != 0) {
- //getLogger().debug("Add leading space: " + iAdjust);
- Space ls = new Space();
- ls.setIPD(iAdjust);
- parentArea.addChildArea(ls);
- }
- }
- }
-
- 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);
- 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);
- }
- setFinished(curLM.isFinished() && (getChildLM() == null));
- 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 List addALetterSpaceTo(List oldList) {
- // old list contains only a box, or the sequence: box penalty glue box
-
- ListIterator oldListIterator = oldList.listIterator();
- KnuthElement element = null;
- // "unwrap" the Position stored in each element of oldList
- while (oldListIterator.hasNext()) {
- element = (KnuthElement) oldListIterator.next();
- element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
- }
-
- oldList = ((InlineLevelLayoutManager)
- element.getLayoutManager()).addALetterSpaceTo(oldList);
-
- // "wrap" againg the Position stored in each element of oldList
- oldListIterator = oldList.listIterator();
- while (oldListIterator.hasNext()) {
- element = (KnuthElement) oldListIterator.next();
- element.setPosition(new NonLeafPosition(this, element.getPosition()));
- }
-
- return oldList;
- }
-
- 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 1999-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;
-
-// Java
-import java.awt.geom.Rectangle2D;
-
-// FOP
-import org.apache.fop.datatypes.Length;
-import org.apache.fop.fo.XMLObj;
-import org.apache.fop.fo.flow.InstreamForeignObject;
-import org.apache.fop.area.inline.ForeignObject;
-import org.apache.fop.area.inline.Viewport;
-
-/**
- * LayoutManager for the fo:instream-foreign-object formatting object
- */
-public class InstreamForeignObjectLM extends LeafNodeLayoutManager {
-
- private InstreamForeignObject fobj;
-
- /**
- * Constructor
- * @param node the formatting object that creates this area
- */
- public InstreamForeignObjectLM(InstreamForeignObject node) {
- super(node);
- fobj = node;
- Viewport areaCurrent = getInlineArea();
- setCurrentArea(areaCurrent);
- setAlignment(node.getVerticalAlign());
- setLead(areaCurrent.getBPD());
- }
-
- /**
- * Get the inline area created by this element.
- *
- * @return the viewport inline area
- */
- private Viewport getInlineArea() {
- XMLObj child = (XMLObj) fobj.childNodes.get(0);
-
- // viewport size is determined by block-progression-dimension
- // and inline-progression-dimension
-
- // if replaced then use height then ignore block-progression-dimension
- //int h = this.propertyList.get("height").getLength().mvalue();
-
- // use specified line-height then ignore dimension in height direction
- boolean hasLH = false; //propertyList.get("line-height").getSpecifiedValue() != null;
-
- Length len;
-
- int bpd = -1;
- int ipd = -1;
- boolean bpdauto = false;
- if (hasLH) {
- bpd = fobj.getLineHeight().getValue();
- } else {
- // this property does not apply when the line-height applies
- // isn't the block-progression-dimension always in the same
- // direction as the line height?
- len = fobj.getBlockProgressionDimension().getOptimum().getLength();
- if (len.getEnum() != EN_AUTO) {
- bpd = len.getValue();
- } else {
- len = fobj.getHeight();
- if (len.getEnum() != EN_AUTO) {
- bpd = len.getValue();
- }
- }
- }
-
- len = fobj.getInlineProgressionDimension().getOptimum().getLength();
- if (len.getEnum() != EN_AUTO) {
- ipd = len.getValue();
- } else {
- len = fobj.getWidth();
- if (len.getEnum() != EN_AUTO) {
- ipd = len.getValue();
- }
- }
-
- // if auto then use the intrinsic size of the content scaled
- // to the content-height and content-width
- int cwidth = -1;
- int cheight = -1;
- len = fobj.getContentWidth();
- if (len.getEnum() != EN_AUTO) {
- if (len.getEnum() == EN_SCALE_TO_FIT) {
- if (ipd != -1) {
- cwidth = ipd;
- }
- } else {
- cwidth = len.getValue();
- }
- }
- len = fobj.getContentHeight();
- if (len.getEnum() != EN_AUTO) {
- if (len.getEnum() == EN_SCALE_TO_FIT) {
- if (bpd != -1) {
- cwidth = bpd;
- }
- } else {
- cheight = len.getValue();
- }
- }
-
- int scaling = fobj.getScaling();
- if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
- if (cwidth == -1 && cheight == -1) {
- cwidth = fobj.getIntrinsicWidth();
- cheight = fobj.getIntrinsicHeight();
- } else if (cwidth == -1) {
- cwidth = (int)(fobj.getIntrinsicWidth() * (double)cheight
- / fobj.getIntrinsicHeight());
- } else if (cheight == -1) {
- cheight = (int)(fobj.getIntrinsicHeight() * (double)cwidth
- / fobj.getIntrinsicWidth());
- } else {
- // adjust the larger
- double rat1 = cwidth / fobj.getIntrinsicWidth();
- double rat2 = cheight / fobj.getIntrinsicHeight();
- if (rat1 < rat2) {
- // reduce cheight
- cheight = (int)(rat1 * fobj.getIntrinsicHeight());
- } else if (rat1 > rat2) {
- cwidth = (int)(rat2 * fobj.getIntrinsicWidth());
- }
- }
- }
-
- if (ipd == -1) {
- ipd = cwidth;
- }
- if (bpd == -1) {
- bpd = cheight;
- }
-
- boolean clip = false;
- if (cwidth > ipd || cheight > bpd) {
- int overflow = fobj.getOverflow();
- if (overflow == EN_HIDDEN) {
- clip = true;
- } else if (overflow == EN_ERROR_IF_OVERFLOW) {
- fobj.getLogger().error("Instream foreign object overflows the viewport: clipping");
- clip = true;
- }
- }
-
- int xoffset = fobj.computeXOffset(ipd, cwidth);
- int yoffset = fobj.computeYOffset(bpd, cheight);
-
- Rectangle2D placement = new Rectangle2D.Float(xoffset, yoffset, cwidth, cheight);
-
- org.w3c.dom.Document doc = child.getDOMDocument();
- String ns = child.getDocumentNamespace();
-
- //fobj.childNodes = null; This is bad for i-f-o in static-content!!!!!
- ForeignObject foreign = new ForeignObject(doc, ns);
-
- Viewport vp = new Viewport(foreign);
- vp.setIPD(ipd);
- vp.setBPD(bpd);
- vp.setContentPosition(placement);
- vp.setClip(clip);
- vp.setOffset(0);
-
- // Common Border, Padding, and Background Properties
- TraitSetter.addBorders(vp, fobj.getCommonBorderPaddingBackground());
- TraitSetter.addBackground(vp, fobj.getCommonBorderPaddingBackground());
-
- return vp;
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#addId()
- */
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
-
+++ /dev/null
-/*
- * Copyright 2004-2005 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;
-
-public class KnuthInlineBox extends KnuthBox {
-
- private int lead;
- private int total;
- private int middle;
- private FootnoteBodyLayoutManager footnoteBodyLM = null;
-
- /**
- * Create a new KnuthBox.
- *
- * @param w the width of this box
- * @param l the height of this box above the main baseline
- * @param t the total height of this box
- * @param m the height of this box above and below the middle baseline
- * @param pos the Position stored in this box
- * @param bAux is this box auxiliary?
- */
- public KnuthInlineBox(int w, int l, int t, int m, Position pos, boolean bAux) {
- super(w, pos, bAux);
- lead = l;
- total = t;
- middle = m;
- }
-
- /**
- * @return the height of this box above the main baseline.
- */
- public int getLead() {
- return lead;
- }
-
- /**
- * @return the total height of this box.
- */
- public int getTotal() {
- return total;
- }
-
- /**
- * @return the height of this box above and below the middle baseline.
- */
- public int getMiddle() {
- return middle;
- }
-
- /**
- * @param fblm the FootnoteBodyLM this box must hold a reference to
- */
- public void setFootnoteBodyLM(FootnoteBodyLayoutManager fblm) {
- footnoteBodyLM = fblm;
- }
-
- /**
- * @return the FootnoteBodyLM this box holds a reference to
- */
- public FootnoteBodyLayoutManager getFootnoteBodyLM() {
- return footnoteBodyLM;
- }
-
- /**
- * @return true if this box holds a reference to a FootnoteBodyLM
- */
- public boolean isAnchor() {
- return (footnoteBodyLM != null);
- }
-
-
- /** @see java.lang.Object#toString() */
- public String toString() {
- StringBuffer sb = new StringBuffer(super.toString());
- sb.append(" lead=").append(lead);
- sb.append(" total=").append(total);
- sb.append(" middle=").append(middle);
- return sb.toString();
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright 2004-2005 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.List;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * A knuth paragraph
- *
- * The set is sorted into lines indexed into activeLines.
- * The nodes in each line is linked together in a single linked list by the
- * KnuthNode.next field. The activeLines array contains a link to the head of
- * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
- * <p>
- * The set of active nodes can be traversed by
- * <pre>
- * for (int line = startLine; line < endLine; line++) {
- * for (KnuthNode node = getNode(line); node != null; node = node.next) {
- * // Do something with 'node'
- * }
- * }
- * </pre>
- */
-public class KnuthParagraph {
- // parameters of Knuth's algorithm:
- // penalty value for flagged penalties
- private int flaggedPenalty = 50;
- // demerit for consecutive lines ending at flagged penalties
- private int repeatedFlaggedDemerit = 50;
- // demerit for consecutive lines belonging to incompatible fitness classes
- private int incompatibleFitnessDemerit = 50;
- // suggested modification to the "optimum" number of lines
- private int looseness = 0;
-
- /**
- * The threshold for considering breaks to be acceptable.
- */
- private double threshold;
-
- /**
- * The paragraph of KnuthElements.
- */
- private List par;
-
- /**
- * The width of a line.
- */
- private int lineWidth = 0;
- private boolean force = false;
-
- private KnuthNode lastTooLong;
- private KnuthNode lastTooShort;
- private KnuthNode lastDeactivated;
-
- /**
- * The set of active nodes.
- */
- private KnuthNode[] activeLines;
-
- /**
- * The number of active nodes.
- */
- private int activeNodeCount;
-
- /**
- * The lowest available line in the set of active nodes.
- */
- private int startLine = 0;
-
- /**
- * The highest + 1 available line in the set of active nodes.
- */
- private int endLine = 0;
-
- /**
- * The total width of all elements handled so far.
- */
- private int totalWidth;
-
- /**
- * The total stretch of all elements handled so far.
- */
- private int totalStretch = 0;
-
- /**
- * The total shrink of all elements handled so far.
- */
- private int totalShrink = 0;
-
- private BestRecords best;
- private KnuthNode[] positions;
-
- private static final int INFINITE_RATIO = 1000;
-
- protected static Log log = LogFactory.getLog(KnuthParagraph.class);
-
- public KnuthParagraph(List par) {
- this.best = new BestRecords();
- this.par = par;
- }
-
-
- // this class represent a feasible breaking point
- private class KnuthNode {
- // index of the breakpoint represented by this node
- public int position;
-
- // number of the line ending at this breakpoint
- public int line;
-
- // fitness class of the line ending at his breakpoint
- public int fitness;
-
- // accumulated width of the KnuthElements
- public int totalWidth;
-
- public int totalStretch;
-
- public int totalShrink;
-
- // adjustment ratio if the line ends at this breakpoint
- public double adjustRatio;
-
- // difference between target and actual line width
- public int difference;
-
- // minimum total demerits up to this breakpoint
- public double totalDemerits;
-
- // best node for the preceding breakpoint
- public KnuthNode previous;
-
- // Next possible node in the same line
- public KnuthNode next;
-
-
- public KnuthNode(int position, int line, int fitness,
- int totalWidth, int totalStretch, int totalShrink,
- double adjustRatio, int difference,
- double totalDemerits, KnuthNode previous) {
- this.position = position;
- this.line = line;
- this.fitness = fitness;
- this.totalWidth = totalWidth;
- this.totalStretch = totalStretch;
- this.totalShrink = totalShrink;
- this.adjustRatio = adjustRatio;
- this.difference = difference;
- this.totalDemerits = totalDemerits;
- this.previous = previous;
- }
-
- public String toString() {
- return "<KnuthNode at " + position + " " +
- totalWidth + "+" + totalStretch + "-" + totalShrink +
- " line:" + line +
- " prev:" + (previous != null ? previous.position : -1) +
- " dem:" + totalDemerits +
- ">";
- }
- }
-
- // this class stores information about how the nodes
- // which could start a line
- // ending at the current element
- private class BestRecords {
- private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
-
- private double bestDemerits[] = new double[4];
- private KnuthNode bestNode[] = new KnuthNode[4];
- private double bestAdjust[] = new double[4];
- private int bestDifference[] = new int[4];
- private int bestIndex = -1;
-
- public BestRecords() {
- reset();
- }
-
- public void addRecord(double demerits, KnuthNode node, double adjust,
- int difference, int fitness) {
- if (demerits > bestDemerits[fitness]) {
- log.error("New demerits value greter than the old one");
- }
- bestDemerits[fitness] = demerits;
- bestNode[fitness] = node;
- bestAdjust[fitness] = adjust;
- bestDifference[fitness] = difference;
- if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
- bestIndex = fitness;
- }
- }
-
- public boolean hasRecords() {
- return (bestIndex != -1);
- }
-
- public boolean notInfiniteDemerits(int fitness) {
- return (bestDemerits[fitness] != INFINITE_DEMERITS);
- }
-
- public double getDemerits(int fitness) {
- return bestDemerits[fitness];
- }
-
- public KnuthNode getNode(int fitness) {
- return bestNode[fitness];
- }
-
- public double getAdjust(int fitness) {
- return bestAdjust[fitness];
- }
-
- public int getDifference(int fitness) {
- return bestDifference[fitness];
- }
-
- public double getMinDemerits() {
- if (bestIndex != -1) {
- return getDemerits(bestIndex);
- } else {
- // anyway, this should never happen
- return INFINITE_DEMERITS;
- }
- }
-
- public void reset() {
- bestDemerits[0] = INFINITE_DEMERITS;
- bestDemerits[1] = INFINITE_DEMERITS;
- bestDemerits[2] = INFINITE_DEMERITS;
- bestDemerits[3] = INFINITE_DEMERITS;
- bestIndex = -1;
- }
- }
-
- public int findBreakPoints(int lineWidth, double threshold, boolean force) {
- this.lineWidth = lineWidth;
- this.totalWidth = 0;
- this.totalStretch = 0;
- this.totalShrink = 0;
- this.threshold = threshold;
- this.force = force;
-
- activeLines = new KnuthNode[20];
- addNode(0, new KnuthNode(0, 0, 1, 0, 0, 0, 0, 0, 0, null));
-
- boolean bForced = false;
-
- // previous element in the paragraph is a KnuthBox
- boolean previousIsBox = false;
-
- if (log.isTraceEnabled()) {
- log.trace("Looping over " + par.size() + " box objects");
- }
-
- KnuthNode lastForced = getNode(0);
-
- // main loop
- for (int i = 0; i < par.size(); i++) {
- KnuthElement element = getElement(i);
- if (element.isBox()) {
- // a KnuthBox object is not a legal line break
- totalWidth += element.getW();
- previousIsBox = true;
- } else if (element.isGlue()) {
- // a KnuthGlue object is a legal line break
- // only if the previous object is a KnuthBox
- if (previousIsBox) {
- considerLegalBreak(element, i);
- }
- totalWidth += element.getW();
- totalStretch += element.getY();
- totalShrink += element.getZ();
- previousIsBox = false;
- } else {
- // a KnuthPenalty is a legal line break
- // only if its penalty is not infinite
- if (element.getP() < KnuthElement.INFINITE) {
- considerLegalBreak(element, i);
- }
- previousIsBox = false;
- }
- if (activeNodeCount == 0) {
- if (!force) {
- log.debug("Could not find a set of breaking points " + threshold);
- return 0;
- }
- /*
- if (lastForced != null && lastForced.position == lastDeactivated.position) {
- lastForced = lastTooShort != null ? lastTooShort : lastTooLong;
- } else {
- lastForced = lastDeactivated;
- }
- */
- if (lastTooShort == null || lastForced.position == lastTooShort.position) {
- lastForced = lastTooLong;
- } else {
- lastForced = lastTooShort;
- }
-
- log.debug("Restarting at node " + lastForced);
- lastForced.totalDemerits = 0;
- addNode(lastForced.line, lastForced);
- i = lastForced.position;
- startLine = lastForced.line;
- endLine = startLine + 1;
- totalWidth = lastForced.totalWidth;
- totalStretch = lastForced.totalStretch;
- totalShrink = lastForced.totalShrink;
- lastTooShort = lastTooLong = null;
- }
- }
- if (log.isTraceEnabled()) {
- log.trace("Main loop completed " + activeNodeCount);
- log.trace("Active nodes=" + toString(""));
- }
-
- // there is at least one set of breaking points
- // choose the active node with fewest total demerits
- KnuthNode bestActiveNode = findBestNode();
- int line = bestActiveNode.line;
-/*
- if (looseness != 0) {
- // choose the appropriate active node
- int s = 0;
- double bestDemerits = 0;
- for (int i = 0; i < activeList.size(); i++) {
- KnuthNode node = getNode(i);
- int delta = node.line - line;
- if (looseness <= delta && delta < s
- || s < delta && delta <= looseness) {
- s = delta;
- bestActiveNode = node;
- bestDemerits = node.totalDemerits;
- } else if (delta == s
- && node.totalDemerits < bestDemerits) {
- bestActiveNode = node;
- bestDemerits = node.totalDemerits;
- }
- }
- line = bestActiveNode.line;
- }
-*/
- // Reverse the list of nodes from bestActiveNode.
- positions = new KnuthNode[line];
- // use the chosen node to determine the optimum breakpoints
- for (int i = line - 1; i >= 0; i--) {
- positions[i] = bestActiveNode;
- bestActiveNode = bestActiveNode.previous;
- }
- activeLines = null;
- return positions.length;
- }
-
- private void considerLegalBreak(KnuthElement element, int elementIdx) {
-
- if (log.isTraceEnabled()) {
- log.trace("Feasible breakpoint at " + par.indexOf(element) + " " + totalWidth + "+" + totalStretch + "-" + totalShrink);
- log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
- }
-
- lastDeactivated = null;
- lastTooLong = null;
- for (int line = startLine; line < endLine; line++) {
- for (KnuthNode node = getNode(line); node != null; node = node.next) {
- if (node.position == elementIdx) {
- continue;
- }
- int difference = computeDifference(node, element);
- double r = computeAdjustmentRatio(node, difference);
- if (log.isTraceEnabled()) {
- log.trace("\tr=" + r);
- log.trace("\tline=" + line);
- }
-
- // The line would be too long.
- if (r < -1 || element.isForcedBreak()) {
- // Deactivate node.
- if (log.isTraceEnabled()) {
- log.trace("Removing " + node);
- }
- removeNode(line, node);
- lastDeactivated = compareNodes(lastDeactivated, node);
- }
-
- // The line is within the available shrink and the threshold.
- if (r >= -1 && r <= threshold) {
- int fitnessClass = computeFitness(r);
- double demerits = computeDemerits(node, element, fitnessClass, r);
-
- if (log.isTraceEnabled()) {
- log.trace("\tDemerits=" + demerits);
- log.trace("\tFitness class=" + fitnessClass);
- }
-
- if (demerits < best.getDemerits(fitnessClass)) {
- // updates best demerits data
- best.addRecord(demerits, node, r, difference, fitnessClass);
- }
- }
-
- // The line is way too short, but we are in forcing mode, so a node is
- // calculated and stored in lastValidNode.
- if (force && (r <= -1 || r > threshold)) {
- int fitnessClass = computeFitness(r);
- double demerits = computeDemerits(node, element, fitnessClass, r);
- if (r <= -1) {
- if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
- lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass,
- totalWidth, totalStretch, totalShrink,
- r, difference, demerits, node);
- if (log.isTraceEnabled()) {
- log.trace("Picking tooLong " + lastTooLong);
- }
- }
- } else {
- if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
- lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass,
- totalWidth, totalStretch, totalShrink,
- r, difference, demerits, node);
- if (log.isTraceEnabled()) {
- log.trace("Picking tooShort " + lastTooShort);
- }
- }
- }
- }
- }
- addBreaks(line, elementIdx);
- }
- }
-
-
- private void addBreaks(int line, int elementIdx) {
- if (!best.hasRecords()) {
- return;
- }
-
- int newWidth = totalWidth;
- int newStretch = totalStretch;
- int newShrink = totalShrink;
-
- for (int i = elementIdx; i < par.size(); i++) {
- KnuthElement tempElement = getElement(i);
- if (tempElement.isBox()) {
- break;
- } else if (tempElement.isGlue()) {
- newWidth += tempElement.getW();
- newStretch += tempElement.getY();
- newShrink += tempElement.getZ();
- } else if (tempElement.isForcedBreak() && i != elementIdx) {
- break;
- }
- }
-
- // add nodes to the active nodes list
- double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
- for (int i = 0; i <= 3; i++) {
- if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) {
- // the nodes in activeList must be ordered
- // by line number and position;
- if (log.isTraceEnabled()) {
- log.trace("\tInsert new break in list of " + activeNodeCount);
- }
- KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i,
- newWidth, newStretch, newShrink,
- best.getAdjust(i),
- best.getDifference(i),
- best.getDemerits(i),
- best.getNode(i));
- addNode(line + 1, newNode);
- }
- }
- best.reset();
- }
-
- /**
- * Return the difference between the line width and the width of the break that
- * ends in 'element'.
- * @param activeNode
- * @param element
- * @return The difference in width. Positive numbers mean extra space in the line,
- * negative number that the line overflows.
- */
- private int computeDifference(KnuthNode activeNode, KnuthElement element) {
- // compute the adjustment ratio
- int actualWidth = totalWidth - activeNode.totalWidth;
- if (element.isPenalty()) {
- actualWidth += element.getW();
- }
- return lineWidth - actualWidth;
- }
-
- /**
- * Return the adjust ration needed to make up for the difference. A ration of
- * <ul>
- * <li>0 means that the break has the exact right width</li>
- * <li>>= -1 && < 0 means that the break is to wider than the line,
- * but within the minimim values of the glues.</li>
- * <li>>0 && < 1 means that the break is smaller than the line width,
- * but within the maximum values of the glues.</li>
- * <li>> 1 means that the break is too small to make up for the glues.</li>
- * </ul>
- * @param activeNode
- * @param difference
- * @return The ration.
- */
- private double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
- // compute the adjustment ratio
- if (difference > 0) {
- int maxAdjustment = totalStretch - activeNode.totalStretch;
- if (maxAdjustment > 0) {
- return (double) difference / maxAdjustment;
- } else {
- return INFINITE_RATIO;
- }
- } else if (difference < 0) {
- int maxAdjustment = totalShrink - activeNode.totalShrink;
- if (maxAdjustment > 0) {
- return (double) difference / maxAdjustment;
- } else {
- return -INFINITE_RATIO;
- }
- } else {
- return 0;
- }
- }
-
- /**
- * Figure out the fitness class of this line (tight, loose,
- * very tight or very loose).
- * @param r
- * @return
- */
- private int computeFitness(double r) {
- int newFitnessClass;
- if (r < -0.5) {
- return 0;
- } else if (r <= 0.5) {
- return 1;
- } else if (r <= 1) {
- return 2;
- } else {
- return 3;
- }
- }
-
- /**
- * Find and return the KnuthNode in the active set of nodes with the
- * lowest demerit.
- */
- private KnuthNode findBestNode() {
- // choose the active node with fewest total demerits
- KnuthNode bestActiveNode = null;
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- bestActiveNode = compareNodes(bestActiveNode, node);
- }
- }
- if (log.isTraceEnabled()) {
- log.trace("Best demerits " + bestActiveNode.totalDemerits + " for paragraph size " + par.size());
- }
- return bestActiveNode;
- }
-
- /**
- * Compare two KnuthNodes and return the node with the least demerit.
- * @param node1 The first knuth node.
- * @param node2 The other knuth node.
- * @return
- */
- private KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
- if (node1 == null || node2.position > node1.position) {
- return node2;
- }
- if (node2.position == node1.position) {
- if (node2.totalDemerits < node1.totalDemerits) {
- return node2;
- }
- }
- return node1;
- }
-
- private double computeDemerits(KnuthNode activeNode, KnuthElement element,
- int fitnessClass, double r) {
- double demerits = 0;
- // compute demerits
- double f = Math.abs(r);
- f = 1 + 100 * f * f * f;
- if (element.isPenalty() && element.getP() >= 0) {
- f += element.getP();
- demerits = f * f;
- } else if (element.isPenalty() && !element.isForcedBreak()) {
- double penalty = element.getP();
- demerits = f * f - penalty * penalty;
- } else {
- demerits = f * f;
- }
-
- if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
- && getElement(activeNode.position).isPenalty()
- && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
- // add demerit for consecutive breaks at flagged penalties
- demerits += repeatedFlaggedDemerit;
- }
- if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
- // add demerit for consecutive breaks
- // with very different fitness classes
- demerits += incompatibleFitnessDemerit;
- }
- demerits += activeNode.totalDemerits;
- return demerits;
- }
-
- /**
- * Return the element at index idx in the paragraph.
- * @param idx index of the element.
- * @return
- */
- private KnuthElement getElement(int idx) {
- return (KnuthElement) par.get(idx);
- }
-
- /**
- * Add a KnuthNode at the end of line 'line'.
- * If this is the first node in the line, adjust endLine accordingly.
- * @param line
- * @param node
- */
- private void addNode(int line, KnuthNode node) {
- int headIdx = line * 2;
- if (headIdx >= activeLines.length) {
- KnuthNode[] oldList = activeLines;
- activeLines = new KnuthNode[headIdx + headIdx];
- System.arraycopy(oldList, 0, activeLines, 0, oldList.length);
- }
- node.next = null;
- if (activeLines[headIdx + 1] != null) {
- activeLines[headIdx + 1].next = node;
- } else {
- activeLines[headIdx] = node;
- endLine = line+1;
- }
- activeLines[headIdx + 1] = node;
- activeNodeCount++;
- }
-
- /**
- * Remove the first node in line 'line'. If the line then becomes empty, adjust the
- * startLine accordingly.
- * @param line
- * @param node
- */
- private void removeNode(int line, KnuthNode node) {
- KnuthNode n = getNode(line);
- if (n != node) {
- log.error("Should be first");
- } else {
- activeLines[line*2] = node.next;
- if (node.next == null) {
- activeLines[line*2+1] = null;
- }
- while (startLine < endLine && getNode(startLine) == null) {
- startLine++;
- }
- }
- activeNodeCount--;
- }
-
- private KnuthNode getNode(int line) {
- return activeLines[line * 2];
- }
-
- /**
- * Return true if the position 'idx' is a legal breakpoint.
- * @param idx
- * @return
- */
- private boolean isLegalBreakpoint(int idx) {
- KnuthElement elm = getElement(idx);
- if (elm.isPenalty() && elm.getP() != KnuthElement.INFINITE) {
- return true;
- } else if (idx > 0 && elm.isGlue() && getElement(idx-1).isBox()) {
- return true;
- } else {
- return false;
- }
- }
-
- public int getDifference(int line) {
- return positions[line].difference;
- }
-
- public double getAdjustRatio(int line) {
- return positions[line].adjustRatio;
- }
-
- public int getStart(int line) {
- KnuthNode previous = positions[line].previous;
- return line == 0 ? 0 : previous.position + 1;
- }
-
- public int getEnd(int line) {
- return positions[line].position;
- }
-
- /**
- * Return a string representation of a MinOptMax in the form of a
- * "width+stretch-shrink". Useful only for debugging.
- * @param mom
- * @return
- */
- private static String width(MinOptMax mom) {
- return mom.opt + "+" + (mom.max - mom.opt) + "-" + (mom.opt - mom.min);
-
- }
-
- public String toString(String prepend) {
- StringBuffer sb = new StringBuffer();
- sb.append("[\n");
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- sb.append(prepend + "\t" + node + ",\n");
- }
- }
- sb.append(prepend + "]");
- return sb.toString();
- }
-}
\ No newline at end of file
package org.apache.fop.layoutmgr;
import org.apache.fop.fo.Constants;
+import org.apache.fop.layoutmgr.inline.HyphContext;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.fo.pagination.Title;
import org.apache.fop.area.AreaTreeHandler;
+import org.apache.fop.layoutmgr.inline.BasicLinkLayoutManager;
+import org.apache.fop.layoutmgr.inline.BidiLayoutManager;
+import org.apache.fop.layoutmgr.inline.CharacterLayoutManager;
+import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager;
+import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager;
+import org.apache.fop.layoutmgr.inline.ICLayoutManager;
+import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
+import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
+import org.apache.fop.layoutmgr.inline.InstreamForeignObjectLM;
+import org.apache.fop.layoutmgr.inline.LeaderLayoutManager;
+import org.apache.fop.layoutmgr.inline.PageNumberCitationLayoutManager;
+import org.apache.fop.layoutmgr.inline.PageNumberLayoutManager;
+import org.apache.fop.layoutmgr.inline.TextLayoutManager;
+import org.apache.fop.layoutmgr.inline.WrapperLayoutManager;
import org.apache.fop.layoutmgr.list.ListBlockLayoutManager;
import org.apache.fop.layoutmgr.list.ListItemLayoutManager;
import org.apache.fop.layoutmgr.table.TableLayoutManager;
+++ /dev/null
-/*
- * Copyright 1999-2005 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 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.datatypes.PercentBase;
-import org.apache.fop.fo.flow.Leader;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.traits.MinOptMax;
-
-import java.util.List;
-import java.util.LinkedList;
-
-/**
- * LayoutManager for the fo:leader formatting object
- */
-public class LeaderLayoutManager extends LeafNodeLayoutManager {
- private Leader fobj;
- Font font = null;
-
- private LinkedList contentList = null;
- private ContentLayoutManager clm = null;
-
- /**
- * Constructor
- *
- * @param node the formatting object that creates this area
- * @todo better null checking of font object
- */
- public LeaderLayoutManager(Leader node) {
- super(node);
- fobj = node;
- font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
- // 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) {
- return getLeaderInlineArea();
- }
-
- protected MinOptMax getAllocationIPD(int refIPD) {
- return getLeaderAllocIPD(refIPD);
- }
-
- private MinOptMax getLeaderAllocIPD(int ipd) {
- // length of the leader
- fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd);
- int opt = fobj.getLeaderLength().getOptimum().getLength().getValue();
- int min = fobj.getLeaderLength().getMinimum().getLength().getValue();
- int max = fobj.getLeaderLength().getMaximum().getLength().getValue();
- return new MinOptMax(min, opt, max);
- }
-
- private InlineArea getLeaderInlineArea() {
- InlineArea leaderArea = null;
-
- if (fobj.getLeaderPattern() == EN_RULE) {
- org.apache.fop.area.inline.Leader leader =
- new org.apache.fop.area.inline.Leader();
- leader.setRuleStyle(fobj.getRuleStyle());
- leader.setRuleThickness(fobj.getRuleThickness().getValue());
- leaderArea = leader;
- } else if (fobj.getLeaderPattern() == EN_SPACE) {
- leaderArea = new Space();
- } else if (fobj.getLeaderPattern() == EN_DOTS) {
- TextArea t = new TextArea();
- 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()));
- int width = font.getCharWidth(dot);
- Space spacer = null;
- if (fobj.getLeaderPatternWidth().getValue() > width) {
- spacer = new Space();
- spacer.setIPD(fobj.getLeaderPatternWidth().getValue() - width);
- width = fobj.getLeaderPatternWidth().getValue();
- }
- FilledArea fa = new FilledArea();
- fa.setUnitWidth(width);
- fa.addChildArea(t);
- if (spacer != null) {
- fa.addChildArea(spacer);
- }
- fa.setBPD(font.getAscender());
-
- leaderArea = fa;
- } else if (fobj.getLeaderPattern() == EN_USECONTENT) {
- if (fobj.getChildNodes() == null) {
- fobj.getLogger().error("Leader use-content with no content");
- 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);
- clm.setUserAgent(fobj.getUserAgent());
- addChildLM(clm);
-
- InlineLayoutManager lm;
- lm = new InlineLayoutManager(fobj);
- clm.addChildLM(lm);
-
- contentList = clm.getNextKnuthElements(new LayoutContext(0), 0);
- int width = clm.getStackingSize();
- Space spacer = null;
- if (fobj.getLeaderPatternWidth().getValue() > width) {
- spacer = new Space();
- spacer.setIPD(fobj.getLeaderPatternWidth().getValue() - width);
- width = fobj.getLeaderPatternWidth().getValue();
- }
- fa.setUnitWidth(width);
- if (spacer != null) {
- fa.addChildArea(spacer);
- }
- leaderArea = fa;
- }
- return leaderArea;
- }
-
- protected void offsetArea(InlineArea area, LayoutContext context) {
- int pattern = fobj.getLeaderPattern();
- int bpd = area.getBPD();
-
- switch (pattern) {
- case EN_RULE:
- switch (verticalAlignment) {
- case EN_TOP:
- area.setOffset(0);
- break;
- case EN_MIDDLE:
- area.setOffset(context.getMiddleBaseline() - bpd / 2);
- break;
- case EN_BOTTOM:
- area.setOffset(context.getLineHeight() - bpd);
- break;
- case EN_BASELINE: // fall through
- default:
- area.setOffset(context.getBaseline() - bpd);
- break;
- }
- break;
- case EN_DOTS:
- switch (verticalAlignment) {
- case EN_TOP:
- area.setOffset(0);
- break;
- case EN_MIDDLE:
- area.setOffset(context.getMiddleBaseline());
- break;
- case EN_BOTTOM:
- area.setOffset(context.getLineHeight() - bpd + font.getAscender());
- break;
- case EN_BASELINE: // fall through
- default:
- area.setOffset(context.getBaseline());
- break;
- }
- break;
- case EN_SPACE:
- // nothing to do
- break;
- case EN_USECONTENT:
- switch (verticalAlignment) {
- case EN_TOP:
- area.setOffset(0);
- break;
- case EN_MIDDLE:
- area.setOffset(context.getMiddleBaseline());
- break;
- case EN_BOTTOM:
- area.setOffset(context.getLineHeight() - bpd);
- break;
- case EN_BASELINE: // fall through
- default:
- area.setOffset(context.getBaseline());
- break;
- }
- break;
- }
- }
-
- public void addAreas(PositionIterator posIter, LayoutContext context) {
- if (fobj.getLeaderPattern() != EN_USECONTENT) {
- // use LeafNodeLayoutManager.addAreas()
- super.addAreas(posIter, context);
- } else {
- addId();
-
- widthAdjustArea(curArea, context);
-
- // add content areas
- KnuthPossPosIter contentIter = new KnuthPossPosIter(contentList, 0, contentList.size());
- clm.addAreas(contentIter, context);
- offsetArea(curArea, context);
-
- parentLM.addChildArea(curArea);
-
- while (posIter.hasNext()) {
- posIter.next();
- }
- }
- }
-
- public LinkedList getNextKnuthElements(LayoutContext context,
- int alignment) {
- MinOptMax ipd;
- curArea = get(context);
- LinkedList returnList = new LinkedList();
-
- if (curArea == null) {
- setFinished(true);
- return null;
- }
-
- ipd = getAllocationIPD(context.getRefIPD());
-
- int bpd = curArea.getBPD();
- int lead = 0;
- int total = 0;
- int middle = 0;
- switch (verticalAlignment) {
- case EN_MIDDLE : middle = bpd / 2 ;
- break;
- case EN_TOP : // fall through
- case EN_BOTTOM : total = bpd;
- break;
- case EN_BASELINE: // fall through
- default: lead = bpd;
- break;
- }
-
- // create the AreaInfo object to store the computed values
- areaInfo = new AreaInfo((short) 0, ipd, false,
- lead, total, middle);
-
- // node is a fo:Leader
- returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
- areaInfo.middle,
- new LeafPosition(this, -1), true));
- returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(areaInfo.ipdArea.opt,
- areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
- areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
- new LeafPosition(this, 0), false));
- returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
- areaInfo.middle,
- new LeafPosition(this, -1), true));
-
- setFinished(true);
- return returnList;
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- // use the AbstractLayoutManager.hyphenate() null implementation
- super.hyphenate(pos, hc);
- }
-
- public boolean applyChanges(List oldList) {
- setFinished(false);
- return false;
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- int flaggedPenalty,
- int alignment) {
- if (isFinished()) {
- return null;
- }
-
- LinkedList returnList = new LinkedList();
-
- returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
- areaInfo.middle,
- new LeafPosition(this, -1), true));
- returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(areaInfo.ipdArea.opt,
- areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
- areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
- new LeafPosition(this, 0), false));
- returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
- areaInfo.middle,
- new LeafPosition(this, -1), true));
-
- setFinished(true);
- return returnList;
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
+++ /dev/null
-/*
- * Copyright 1999-2005 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 org.apache.fop.area.Area;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.fo.FObj;
-import org.apache.fop.traits.MinOptMax;
-
-import java.util.List;
-import java.util.LinkedList;
-
-/**
- * Base LayoutManager for leaf-node FObj, ie: ones which have no children.
- * These are all inline objects. Most of them cannot be split (Text is
- * an exception to this rule.)
- * This class can be extended to handle the creation and adding of the
- * inline area.
- */
-public abstract class LeafNodeLayoutManager extends AbstractLayoutManager
- implements InlineLevelLayoutManager {
- /**
- * The inline area that this leafnode will add.
- */
- protected InlineArea curArea = null;
- protected int verticalAlignment;
- private int lead;
- private MinOptMax ipd;
-
- protected boolean bSomethingChanged = false;
- protected AreaInfo areaInfo = null;
-
- /**
- * Store information about the inline area
- */
- protected class AreaInfo {
- protected short iLScount;
- protected MinOptMax ipdArea;
- protected boolean bHyphenated;
- protected int lead;
- protected int total;
- protected int middle;
-
- public AreaInfo(short iLS, MinOptMax ipd, boolean bHyph,
- int l, int t, int m) {
- iLScount = iLS;
- ipdArea = ipd;
- bHyphenated = bHyph;
- lead = l;
- total = t;
- middle = m;
- }
- }
-
-
- /**
- * Create a Leaf node layout mananger.
- * @param node the FObj to attach to this LM.
- */
- public LeafNodeLayoutManager(FObj node) {
- super(node);
- }
-
- /**
- * Create a Leaf node layout mananger.
- */
- public LeafNodeLayoutManager() {
- }
-
- /**
- * get the inline area.
- * @param context the context used to create the area
- * @return the current inline area for this layout manager
- */
- public InlineArea get(LayoutContext context) {
- return curArea;
- }
-
- /**
- * Check if this inline area is resolved due to changes in
- * page or ipd.
- * Currently not used.
- * @return true if the area is resolved when adding
- */
- public boolean resolved() {
- return false;
- }
-
- /**
- * Set the current inline area.
- * @param ia the inline area to set for this layout manager
- */
- public void setCurrentArea(InlineArea ia) {
- curArea = ia;
- }
-
- /**
- * Set the alignment of the inline area.
- * @param al the vertical alignment positioning
- */
- public void setAlignment(int al) {
- verticalAlignment = al;
- }
-
- /**
- * Set the lead for this inline area.
- * The lead is the distance from the top of the object
- * to the baseline.
- * @param l the lead value
- */
- public void setLead(int l) {
- lead = l;
- }
-
- /** @return the lead value (distance from the top of the object to the baseline) */
- public int getLead() {
- return this.lead;
- }
-
- /**
- * This is a leaf-node, so this method is never called.
- * @param childArea the childArea to add
- */
- public void addChildArea(Area childArea) {
- }
-
- /**
- * This is a leaf-node, so this method is never called.
- * @param childArea the childArea to get the parent for
- * @return the parent area
- */
- public Area getParentArea(Area childArea) {
- return null;
- }
-
- /**
- * Get the allocation ipd of the inline area.
- * This method may be overridden to handle percentage values.
- * @param refIPD the ipd of the parent reference area
- * @return the min/opt/max ipd of the inline area
- */
- protected MinOptMax getAllocationIPD(int refIPD) {
- return new MinOptMax(curArea.getIPD());
- }
-
- /**
- * Add the area for this layout manager.
- * This adds the single inline area to the parent.
- * @param posIter the position iterator
- * @param context the layout context for adding the area
- */
- public void addAreas(PositionIterator posIter, LayoutContext context) {
- addId();
-
- InlineArea area = getEffectiveArea();
- if (area.getAllocIPD() > 0 || area.getAllocBPD() > 0) {
- offsetArea(area, context);
- widthAdjustArea(area, context);
- parentLM.addChildArea(area);
- }
-
- while (posIter.hasNext()) {
- posIter.next();
- }
- }
-
- /**
- * @return the effective area to be added to the area tree. Normally, this is simply "curArea"
- * but in the case of page-number(-citation) curArea is cloned, updated and returned.
- */
- protected InlineArea getEffectiveArea() {
- return curArea;
- }
-
- protected void addId() {
- // Do nothing here, overriden in subclasses that have an 'id' property.
- }
-
- /**
- * Offset this area.
- * Offset the inline area in the bpd direction when adding the
- * inline area.
- * This is used for vertical alignment.
- * Subclasses should override this if necessary.
- * @param area the inline area to be updated
- * @param context the layout context used for adding the area
- */
- protected void offsetArea(InlineArea area, LayoutContext context) {
- int bpd = area.getBPD();
- switch (verticalAlignment) {
- case EN_MIDDLE:
- area.setOffset(context.getMiddleBaseline() - bpd / 2);
- break;
- case EN_TOP:
- area.setOffset(context.getTopBaseline());
- break;
- case EN_BOTTOM:
- area.setOffset(context.getBottomBaseline() - bpd);
- break;
- case EN_BASELINE:
- default:
- area.setOffset(context.getBaseline() - bpd);
- break;
- }
- }
-
- /**
- * Adjust the width of the area when adding.
- * This uses the min/opt/max values to adjust the with
- * of the inline area by a percentage.
- * @param area the inline area to be updated
- * @param context the layout context for adding this area
- */
- protected void widthAdjustArea(InlineArea area, LayoutContext context) {
- double dAdjust = context.getIPDAdjust();
- int width = areaInfo.ipdArea.opt;
- if (dAdjust < 0) {
- width = (int) (width + dAdjust * (areaInfo.ipdArea.opt
- - areaInfo.ipdArea.min));
- } else if (dAdjust > 0) {
- width = (int) (width + dAdjust * (areaInfo.ipdArea.max
- - areaInfo.ipdArea.opt));
- }
- area.setIPD(width);
- }
-
- public LinkedList getNextKnuthElements(LayoutContext context,
- int alignment) {
- MinOptMax ipd;
- curArea = get(context);
-
- if (curArea == null) {
- setFinished(true);
- return null;
- }
- ipd = getAllocationIPD(context.getRefIPD());
-
- int bpd = curArea.getBPD();
- int lead = 0;
- int total = 0;
- int middle = 0;
- switch (verticalAlignment) {
- case EN_MIDDLE : middle = bpd / 2 ;
- lead = bpd / 2 ;
- break;
- case EN_TOP : total = bpd;
- break;
- case EN_BOTTOM : total = bpd;
- break;
- case EN_BASELINE:
- default:
- //lead = bpd;
- lead = getLead();
- total = bpd;
- break;
- }
-
- // create the AreaInfo object to store the computed values
- areaInfo = new AreaInfo((short) 0, ipd, false,
- lead, total, middle);
-
- // node is a fo:ExternalGraphic, fo:InstreamForeignObject,
- // fo:PageNumber or fo:PageNumberCitation
- LinkedList returnList = new LinkedList();
- returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false));
- setFinished(true);
- return returnList;
- }
-
- public List addALetterSpaceTo(List oldList) {
- // return the unchanged elements
- return oldList;
- }
-
- public void getWordChars(StringBuffer sbChars, Position pos) {
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- }
-
- public boolean applyChanges(List oldList) {
- setFinished(false);
- return false;
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- /*int flaggedPenalty,*/
- int alignment) {
- if (isFinished()) {
- return null;
- }
-
- LinkedList returnList = new LinkedList();
-
- // fobj is a fo:ExternalGraphic, fo:InstreamForeignObject,
- // fo:PageNumber or fo:PageNumberCitation
- returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), true));
-
- setFinished(true);
- return returnList;
- }
-}
-
+++ /dev/null
-/*
- * Copyright 1999-2005 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: LineLayoutManager.java,v 1.17 2004/04/02 10:38:29 cbowditch Exp $ */
-
-package org.apache.fop.layoutmgr;
-
-import org.apache.fop.datatypes.Length;
-import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.flow.Block;
-import org.apache.fop.fo.properties.CommonHyphenation;
-import org.apache.fop.hyphenation.Hyphenation;
-import org.apache.fop.hyphenation.Hyphenator;
-import org.apache.fop.area.LineArea;
-
-import java.util.ListIterator;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.LinkedList;
-
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * LayoutManager for lines. It builds one or more lines containing
- * inline areas generated by its sub layout managers.
- * A break is found for each line which may contain one of more
- * breaks from the child layout managers.
- * Once a break is found then it is return for the parent layout
- * manager to handle.
- * When the areas are being added to the page this manager
- * creates a line area to contain the inline areas added by the
- * child layout managers.
- */
-public class LineLayoutManager extends InlineStackingLayoutManager
- implements BlockLevelLayoutManager {
-
- private Block fobj;
-
- private void initialize() {
- bTextAlignment = fobj.getTextAlign();
- bTextAlignmentLast = fobj.getTextAlignLast();
- textIndent = fobj.getTextIndent();
- hyphProps = fobj.getCommonHyphenation();
-
- //
- if (bTextAlignment != EN_JUSTIFY && bTextAlignmentLast == EN_JUSTIFY) {
- effectiveAlignment = 0;
- } else {
- effectiveAlignment = bTextAlignment;
- }
- }
-
- /**
- * Private class to store information about inline breaks.
- * Each value holds the start and end indexes into a List of
- * inline break positions.
- */
- private static class LineBreakPosition extends LeafPosition {
- // int iPos;
- int iParIndex; // index of the Paragraph this Position refers to
- int availableShrink;
- int availableStretch;
- int difference;
- double dAdjust; // Percentage to adjust (stretch or shrink)
- double ipdAdjust; // Percentage to adjust (stretch or shrink)
- int startIndent;
- int lineHeight;
- int lineWidth;
- int baseline;
- int topShift;
- int bottomShift;
-
- LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
- int shrink, int stretch, int diff,
- double ipdA, double adjust, int ind,
- int lh, int lw, int bl, int ts, int bs) {
- super(lm, iBreakIndex);
- availableShrink = shrink;
- availableStretch = stretch;
- difference = diff;
- iParIndex = index;
- ipdAdjust = ipdA;
- dAdjust = adjust;
- startIndent = ind;
- lineHeight = lh;
- lineWidth = lw;
- baseline = bl;
- topShift = ts;
- bottomShift = bs;
- }
-
- }
-
-
- /** Break positions returned by inline content. */
- private List vecInlineBreaks = new java.util.ArrayList();
-
- private int bTextAlignment = EN_JUSTIFY;
- private int bTextAlignmentLast;
- private int effectiveAlignment;
- private Length textIndent;
- private int iIndents = 0;
- private CommonHyphenation hyphProps;
- //private LayoutProps layoutProps;
-
- private int lineHeight;
- private int lead;
- private int follow;
- // offset of the middle baseline with respect to the main baseline
- private int middleShift;
-
- private List knuthParagraphs = null;
- private int iReturnedLBP = 0;
- private int iStartElement = 0;
- private int iEndElement = 0;
-
- // parameters of Knuth's algorithm:
- // penalty value for flagged penalties
- private int flaggedPenalty = 50;
-
- private LineLayoutPossibilities lineLayouts;
- private List lineLayoutsList;
- private int iLineWidth = 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;
-
-
- // this class is used to remember
- // which was the first element in the paragraph
- // returned by each LM
- private class Update {
- private InlineLevelLayoutManager inlineLM;
- private int iFirstIndex;
-
- public Update(InlineLevelLayoutManager lm, int index) {
- inlineLM = lm;
- iFirstIndex = index;
- }
- }
-
- // this class represents a paragraph
- private class Paragraph extends KnuthSequence {
- // space at the end of the last line (in millipoints)
- private MinOptMax lineFiller;
- private int textAlignment;
- private int textAlignmentLast;
- private int textIndent;
- private int lineWidth;
- // the LM which created the paragraph
- private LineLayoutManager layoutManager;
-
- public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
- int indent) {
- super();
- layoutManager = llm;
- textAlignment = alignment;
- textAlignmentLast = alignmentLast;
- textIndent = indent;
- }
-
- public void startParagraph(int lw) {
- lineWidth = lw;
- startSequence();
- }
-
- public void startSequence() {
- // set the minimum amount of empty space at the end of the
- // last line
- if (bTextAlignment == EN_CENTER) {
- lineFiller = new MinOptMax(0);
- } else {
- lineFiller = new MinOptMax(0, (int)(lineWidth / 12), lineWidth);
- }
-
- // add auxiliary elements at the beginning of the paragraph
- if (bTextAlignment == EN_CENTER && bTextAlignmentLast != EN_JUSTIFY) {
- this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
- null, false));
- ignoreAtStart ++;
- }
-
- // add the element representing text indentation
- // at the beginning of the first paragraph
- if (knuthParagraphs.size() == 0
- && fobj.getTextIndent().getValue() != 0) {
- this.add(new KnuthInlineBox(fobj.getTextIndent().getValue(), 0, 0, 0,
- null, false));
- ignoreAtStart ++;
- }
- }
-
- public void endParagraph() {
- KnuthSequence finishedPar = this.endSequence();
- if (finishedPar != null) {
- knuthParagraphs.add(finishedPar);
- }
- }
-
- public KnuthSequence endSequence() {
- // remove glue and penalty item at the end of the paragraph
- while (this.size() > ignoreAtStart
- && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
- this.remove(this.size() - 1);
- }
- if (this.size() > ignoreAtStart) {
- if (bTextAlignment == EN_CENTER
- && bTextAlignmentLast != EN_JUSTIFY) {
- this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
- null, false));
- this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
- false, null, false));
- ignoreAtEnd = 2;
- } else if (bTextAlignmentLast != EN_JUSTIFY) {
- // add the elements representing the space
- // at the end of the last line
- // and the forced break
- this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
- false, null, false));
- this.add(new KnuthGlue(lineFiller.opt,
- lineFiller.max - lineFiller.opt,
- lineFiller.opt - lineFiller.min, null, false));
- this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
- false, null, false));
- ignoreAtEnd = 3;
- } else {
- // add only the element representing the forced break
- this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
- false, null, false));
- ignoreAtEnd = 1;
- }
- return this;
- } else {
- this.clear();
- return null;
- }
- }
-
- }
-
- private class LineBreakingAlgorithm extends BreakingAlgorithm {
- private LineLayoutManager thisLLM;
- private int pageAlignment;
- private int activePossibility;
- private int addedPositions;
- private int textIndent;
- private int fillerMinWidth;
- private int lineHeight;
- private int lead;
- private int follow;
- private int middleshift;
- private int maxDiff;
- private static final double MAX_DEMERITS = 10e6;
-
- public LineBreakingAlgorithm (int pageAlign,
- int textAlign, int textAlignLast,
- int indent, int fillerWidth,
- int lh, int ld, int fl, int ms, boolean first,
- LineLayoutManager llm) {
- super(textAlign, textAlignLast, first, false);
- pageAlignment = pageAlign;
- textIndent = indent;
- fillerMinWidth = fillerWidth;
- lineHeight = lh;
- lead = ld;
- follow = fl;
- middleshift = ms;
- thisLLM = llm;
- activePossibility = -1;
- maxDiff = fobj.getWidows() >= fobj.getOrphans()
- ? fobj.getWidows()
- : fobj.getOrphans();
- }
-
- public void updateData1(int lineCount, double demerits) {
- lineLayouts.addPossibility(lineCount, demerits);
- log.trace("Layout possibility in " + lineCount + " lines; break at position:");
- }
-
- public void updateData2(KnuthNode bestActiveNode,
- KnuthSequence par,
- int total) {
- // compute indent and adjustment ratio, according to
- // the value of text-align and text-align-last
- int indent = 0;
- int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
- int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
- indent += (textAlign == Constants.EN_CENTER) ?
- difference / 2 :
- (textAlign == Constants.EN_END) ? difference : 0;
- indent += (bestActiveNode.line == 1 && bFirst) ?
- textIndent : 0;
- double ratio = (textAlign == Constants.EN_JUSTIFY
- || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
-
- // add nodes at the beginning of the list, as they are found
- // backwards, from the last one to the first one
-
- // the first time this method is called, initialize activePossibility
- if (activePossibility == -1) {
- activePossibility = 0;
- addedPositions = 0;
- }
-
- if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
- activePossibility ++;
- addedPositions = 0;
- //System.out.println(" ");
- }
-
- //System.out.println("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) + ") difference = " + difference + " ratio = " + ratio);
- lineLayouts.addBreakPosition(makeLineBreakPosition(par,
- (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1: 0),
- bestActiveNode.position,
- bestActiveNode.availableShrink - (addedPositions > 0 ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), bestActiveNode.availableStretch, difference, ratio, indent),
- activePossibility);
- addedPositions ++;
- }
-
- /* reset activePossibility, as if breakpoints have not yet been computed
- */
- public void resetAlgorithm() {
- activePossibility = -1;
- }
-
- private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
- int firstElementIndex,
- int lastElementIndex,
- int availableShrink, int availableStretch, int difference,
- double ratio,
- int indent) {
- // line height calculation
-
- int halfLeading = (lineHeight - lead - follow) / 2;
- // height before the main baseline
- int lineLead = lead;
- // maximum size of top and bottom alignment
- int maxtb = follow;
- // max size of middle alignment before and after the middle baseline
- int middlefollow = maxtb;
- // true if this line contains only zero-height, auxiliary boxes
- // and the actual line width is 0; in this case, the line "collapses"
- // i.e. the line area will have bpd = 0
- boolean bZeroHeightLine = (difference == iLineWidth);
-
- // if line-stacking-strategy is "font-height", the line height
- // is not affected by its content
- if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
- ListIterator inlineIterator
- = par.listIterator(firstElementIndex);
- for (int j = firstElementIndex;
- j <= lastElementIndex;
- j++) {
- KnuthElement element = (KnuthElement) inlineIterator.next();
- if (element.isBox()) {
- if (((KnuthInlineBox) element).getLead() > lineLead) {
- lineLead = ((KnuthInlineBox) element).getLead();
- }
- if (((KnuthInlineBox) element).getTotal() > maxtb) {
- maxtb = ((KnuthInlineBox) element).getTotal();
- }
- if (((KnuthInlineBox) element).getMiddle() > lineLead + middleShift) {
- lineLead += ((KnuthInlineBox) element).getMiddle()
- - lineLead - middleShift;
- }
- if (((KnuthInlineBox) element).getMiddle() > middlefollow - middleShift) {
- middlefollow += ((KnuthInlineBox) element).getMiddle()
- - middlefollow + middleShift;
- }
- if (bZeroHeightLine
- && (!element.isAuxiliary()
- || ((KnuthInlineBox) element).getTotal() > 0
- || ((KnuthInlineBox) element).getLead() > 0
- || ((KnuthInlineBox) element).getMiddle() > 0)) {
- bZeroHeightLine = false;
- }
- }
- }
-
- if (maxtb - lineLead > middlefollow) {
- middlefollow = maxtb - lineLead;
- }
- }
-
- constantLineHeight = lineLead + middlefollow + (lineHeight - lead - follow);
-
- if (bZeroHeightLine) {
- return new LineBreakPosition(thisLLM,
- knuthParagraphs.indexOf(par),
- lastElementIndex,
- availableShrink, availableStretch, difference, ratio, 0, indent,
- 0, iLineWidth,
- 0, 0, 0);
- } else {
- return new LineBreakPosition(thisLLM,
- knuthParagraphs.indexOf(par),
- lastElementIndex,
- availableShrink, availableStretch, difference, ratio, 0, indent,
- lineLead + middlefollow + (lineHeight - lead - follow), iLineWidth,
- lineLead + halfLeading,
- - lineLead, middlefollow);
- }
- }
-
- public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
- double threshold, boolean force,
- boolean hyphenationAllowed) {
- return super.findBreakingPoints(par, /*lineWidth,*/
- threshold, force, hyphenationAllowed);
- }
-
- protected int filterActiveNodes() {
- KnuthNode bestActiveNode = null;
-
- if (pageAlignment == EN_JUSTIFY) {
- // leave all active nodes and find the optimum line number
- //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- //System.out.println(" + lines = " + node.line + " demerits = " + node.totalDemerits);
- bestActiveNode = compareNodes(bestActiveNode, node);
- }
- }
-
- // scan the node set once again and remove some nodes
- //System.out.println("LBA.filterActiveList> layout selection");
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
- //if (false) {
- if (node.line != bestActiveNode.line
- && node.totalDemerits > MAX_DEMERITS) {
- //System.out.println(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
- removeNode(i, node);
- } else {
- //System.out.println(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
- }
- }
- }
- } else {
- // leave only the active node with fewest total demerits
- for (int i = startLine; i < endLine; i++) {
- for (KnuthNode node = getNode(i); node != null; node = node.next) {
- bestActiveNode = compareNodes(bestActiveNode, node);
- if (node != bestActiveNode) {
- removeNode(i, node);
- }
- }
- }
- }
- return bestActiveNode.line;
- }
- }
-
-
- private int constantLineHeight = 12000;
-
-
- /**
- * Create a new Line Layout Manager.
- * This is used by the block layout manager to create
- * line managers for handling inline areas flowing into line areas.
- *
- * @param lh the default line height
- * @param l the default lead, from top to baseline
- * @param f the default follow, from baseline to bottom
- */
- public LineLayoutManager(Block block, int lh, int l, int f, int ms) {
- super(block);
- fobj = block;
- // the child FObj are owned by the parent BlockLM
- // this LM has all its childLMs preloaded
- fobjIter = null;
- lineHeight = lh;
- lead = l;
- follow = f;
- middleShift = ms;
- initialize(); // Normally done when started by parent!
- }
-
- public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
- // Get a break from currently active child LM
- // Set up constraints for inline level managers
- InlineLevelLayoutManager curLM ; // currently active LM
-
- // IPD remaining in line
- MinOptMax availIPD = context.getStackLimit();
-
- clearPrevIPD();
- int iPrevLineEnd = vecInlineBreaks.size();
-
- if (iPrevLineEnd == 0 && bTextAlignment == EN_START) {
- availIPD.subtract(new MinOptMax(textIndent.getValue()));
- }
-
- //PHASE 1: Create Knuth elements
- if (knuthParagraphs == null) {
- // it's the first time this method is called
- knuthParagraphs = new ArrayList();
-
- // here starts Knuth's algorithm
- //TODO availIPD should not really be used here, so we can later support custom line
- //widths for for each line (side-floats, differing available IPD after page break)
- collectInlineKnuthElements(context, availIPD);
- } else {
- // this method has been called before
- // all line breaks are already calculated
- }
-
- // return finished when there's no content
- if (knuthParagraphs.size() == 0) {
- setFinished(true);
- return null;
- }
-
- //PHASE 2: Create line breaks
- return findOptimalLineBreakingPoints(alignment);
- /*
- LineBreakPosition lbp = null;
- if (breakpoints == null) {
- // find the optimal line breaking points for each paragraph
- breakpoints = new ArrayList();
- ListIterator paragraphsIterator
- = knuthParagraphs.listIterator(knuthParagraphs.size());
- Paragraph currPar = null;
- while (paragraphsIterator.hasPrevious()) {
- currPar = (Paragraph) paragraphsIterator.previous();
- findBreakingPoints(currPar, context.getStackLimit().opt);
- }
- }*/
-
- //PHASE 3: Return lines
-
- /*
- // get a break point from the list
- lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
- if (iReturnedLBP == breakpoints.size()) {
- setFinished(true);
- }
-
- BreakPoss curLineBP = new BreakPoss(lbp);
- curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
- curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
- return curLineBP;
- */
- }
-
- /**
- * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
- * @param context the LayoutContext
- * @param availIPD available IPD for line (should be removed!)
- */
- private void collectInlineKnuthElements(LayoutContext context, MinOptMax availIPD) {
- LayoutContext inlineLC = new LayoutContext(context);
-
- InlineLevelLayoutManager curLM;
- KnuthElement thisElement = null;
- LinkedList returnedList = null;
- iLineWidth = context.getStackLimit().opt;
-
- // convert all the text in a sequence of paragraphs made
- // of KnuthBox, KnuthGlue and KnuthPenalty objects
- boolean bPrevWasKnuthBox = false;
- KnuthBox prevBox = null;
-
- Paragraph knuthPar = new Paragraph(this,
- bTextAlignment, bTextAlignmentLast,
- textIndent.getValue());
- knuthPar.startParagraph(availIPD.opt);
- while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
- if ((returnedList
- = curLM.getNextKnuthElements(inlineLC,
- effectiveAlignment))
- != null) {
- if (returnedList.size() == 0) {
- continue;
- }
- // look at the first element
- thisElement = (KnuthElement) returnedList.getFirst();
- if (thisElement.isBox() && !thisElement.isAuxiliary()
- && bPrevWasKnuthBox) {
- prevBox = (KnuthBox) knuthPar.removeLast();
- LinkedList oldList = new LinkedList();
- // 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;
- oldList.add(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
- oldList.add(prevBox);
- oldList.addFirst((KnuthGlue) knuthPar.removeLast());
- oldList.addFirst((KnuthPenalty) knuthPar.removeLast());
- }
- // adding a letter space could involve, according to the text
- // represented by oldList, replacing a glue element or adding
- // new elements
- knuthPar.addAll(((InlineLevelLayoutManager)
- prevBox.getLayoutManager())
- .addALetterSpaceTo(oldList));
- if (((KnuthInlineBox) prevBox).isAnchor()) {
- // prevBox represents a footnote citation: copy footnote info
- // from prevBox to the new box
- KnuthInlineBox newBox = (KnuthInlineBox) knuthPar.getLast();
- newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM());
- }
- }
-
- // look at the last element
- KnuthElement lastElement = (KnuthElement) returnedList.getLast();
- boolean bForceLinefeed = false;
- if (lastElement.isBox()) {
- bPrevWasKnuthBox = true;
- } else {
- 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) {
- if (knuthPar.size() == 0) {
- //only a forced linefeed on this line
- //-> compensate with a zero width box
- knuthPar.add(new KnuthInlineBox(0, 0, 0, 0,
- null, false));
- }
- knuthPar.endParagraph();
- knuthPar = new Paragraph(this,
- bTextAlignment, bTextAlignmentLast,
- textIndent.getValue());
- knuthPar.startParagraph(availIPD.opt);
- bPrevWasKnuthBox = false;
- }
- } else {
- // curLM returned null; this can happen
- // if it has nothing more to layout,
- // so just iterate once more to see
- // if there are other children
- }
- }
- knuthPar.endParagraph();
- ElementListObserver.observe(knuthPar, "line", null);
- }
-
- /**
- * Find a set of breaking points.
- * This method is called only once by getNextBreakPoss, and it
- * subsequently calls the other findBreakingPoints() method with
- * different parameters, until a set of breaking points is found.
- *
- * @param par the list of elements that must be parted
- * into lines
- * @param lineWidth the desired length ot the lines
- */
- /*
- private void findBreakingPoints(Paragraph par, int lineWidth) {
- // maximum adjustment ratio permitted
- float maxAdjustment = 1;
-
- // first try
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
- // the first try failed, now try something different
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
- if (hyphProps.hyphenate == Constants.EN_TRUE) {
- // consider every hyphenation point as a legal break
- findHyphenationPoints(par);
- } else {
- // try with a higher threshold
- maxAdjustment = 5;
- }
-
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
- // the second try failed too, try with a huge threshold;
- // if this fails too, use a different algorithm
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
- + (hyphProps.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
- maxAdjustment = 20;
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
- log.debug("No set of breaking points found, using first-fit algorithm");
- }
- }
- }
- }
-
- private boolean findBreakingPoints(Paragraph par, int lineWidth,
- double threshold, boolean force) {
- KnuthParagraph knuthPara = new KnuthParagraph(par);
- int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
- if (lines == 0) {
- return false;
- }
-
- for (int i = lines-1; i >= 0; i--) {
- int line = i+1;
- if (log.isTraceEnabled()) {
- log.trace("Making line from " + knuthPara.getStart(i) + " to " +
- knuthPara.getEnd(i));
- }
- // compute indent and adjustment ratio, according to
- // the value of text-align and text-align-last
-
- int difference = knuthPara.getDifference(i);
- if (line == lines) {
- difference += par.lineFillerWidth;
- }
- int textAlign = (line < lines)
- ? bTextAlignment : bTextAlignmentLast;
- int indent = (textAlign == EN_CENTER)
- ? difference / 2
- : (textAlign == EN_END) ? difference : 0;
- indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
- ? textIndent.getValue() : 0;
- double ratio = (textAlign == EN_JUSTIFY)
- ? knuthPara.getAdjustRatio(i) : 0;
-
- int start = knuthPara.getStart(i);
- int end = knuthPara.getEnd(i);
- makeLineBreakPosition(par, start, end, 0, ratio, indent);
- }
- return true;
- }
-
- 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 above the main baseline
- int lineLead = lead + halfLeading;
- // maximum size of top and bottom alignment
- int maxtb = follow + halfLeading;
- // max size of middle alignment above and below the middle baseline
- int middlefollow = maxtb;
-
- ListIterator inlineIterator
- = par.listIterator(firstElementIndex);
- for (int j = firstElementIndex;
- j <= lastElementIndex;
- j++) {
- KnuthElement element = (KnuthElement) inlineIterator.next();
- if (element.isBox()) {
- KnuthInlineBox box = (KnuthInlineBox)element;
- if (box.getLead() > lineLead) {
- lineLead = box.getLead();
- }
- if (box.getTotal() > maxtb) {
- maxtb = box.getTotal();
- }
- if (box.getMiddle() > lineLead + middleShift) {
- lineLead += box.getMiddle()
- - lineLead - middleShift;
- }
- if (box.getMiddle() > middlefollow - middleShift) {
- middlefollow += box.getMiddle()
- - middlefollow + middleShift;
- }
- }
- }
-
- if (maxtb - lineLead > middlefollow) {
- middlefollow = maxtb - lineLead;
- }
-
- breakpoints.add(insertIndex,
- new LineBreakPosition(this,
- knuthParagraphs.indexOf(par),
- lastElementIndex ,
- ratio, 0, indent,
- lineLead + middlefollow,
- lineLead));
- }*/
-
-
- /**
- * Phase 2 of Knuth algorithm: find optimal break points.
- * @param alignment alignmenr of the paragraph
- * @return a list of Knuth elements representing broken lines
- */
- private LinkedList findOptimalLineBreakingPoints(int alignment) {
-
- // find the optimal line breaking points for each paragraph
- ListIterator paragraphsIterator
- = knuthParagraphs.listIterator(knuthParagraphs.size());
- Paragraph currPar = null;
- LineBreakingAlgorithm alg;
- lineLayoutsList = new ArrayList(knuthParagraphs.size());
- while (paragraphsIterator.hasPrevious()) {
- lineLayouts = new LineLayoutPossibilities();
- currPar = (Paragraph) paragraphsIterator.previous();
- double maxAdjustment = 1;
- int iBPcount = 0;
- alg = new LineBreakingAlgorithm(alignment,
- bTextAlignment, bTextAlignmentLast,
- textIndent.getValue(), currPar.lineFiller.opt,
- lineHeight, lead, follow, middleShift,
- (knuthParagraphs.indexOf(currPar) == 0),
- this);
-
- if (hyphProps.hyphenate == EN_TRUE) {
- findHyphenationPoints(currPar);
- }
-
- // first try
- boolean bHyphenationAllowed = false;
- alg.setConstantLineWidth(iLineWidth);
- iBPcount = alg.findBreakingPoints(currPar,
- maxAdjustment, false, bHyphenationAllowed);
- if (iBPcount == 0 || alignment == EN_JUSTIFY) {
- // if the first try found a set of breaking points, save them
- if (iBPcount > 0) {
- alg.resetAlgorithm();
- lineLayouts.savePossibilities(false);
- } else {
- // the first try failed
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
- }
-
- // now try something different
- log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE));
- if (hyphProps.hyphenate == EN_TRUE) {
- // consider every hyphenation point as a legal break
- bHyphenationAllowed = true;
- } else {
- // try with a higher threshold
- maxAdjustment = 5;
- }
-
- if ((iBPcount
- = alg.findBreakingPoints(currPar,
- maxAdjustment, false, bHyphenationAllowed)) == 0) {
- // the second try failed too, try with a huge threshold
- // and force the algorithm to find
- // a set of breaking points
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
- + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : ""));
- maxAdjustment = 20;
- iBPcount
- = alg.findBreakingPoints(currPar,
- maxAdjustment, true, bHyphenationAllowed);
- }
-
- // use non-hyphenated breaks, when possible
- lineLayouts.restorePossibilities();
-
- /* extension (not in the XSL FO recommendation): if vertical alignment
- is justify and the paragraph has only one layout, try using
- shorter or longer lines */
- //TODO This code snippet is disabled. Reenable?
- if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) {
- //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
- //System.out.println(" layouts with fewer lines? " + lineLayouts.canUseLessLines());
- if (!lineLayouts.canUseMoreLines()) {
- alg.resetAlgorithm();
- lineLayouts.savePossibilities(true);
- // try with shorter lines
- int savedLineWidth = iLineWidth;
- iLineWidth = (int) (iLineWidth * 0.95);
- iBPcount = alg.findBreakingPoints(currPar,
- maxAdjustment, true, bHyphenationAllowed);
- // use normal lines, when possible
- lineLayouts.restorePossibilities();
- iLineWidth = savedLineWidth;
- }
- if (!lineLayouts.canUseLessLines()) {
- alg.resetAlgorithm();
- lineLayouts.savePossibilities(true);
- // try with longer lines
- int savedLineWidth = iLineWidth;
- iLineWidth = (int) (iLineWidth * 1.05);
- alg.setConstantLineWidth(iLineWidth);
- iBPcount = alg.findBreakingPoints(currPar,
- maxAdjustment, true, bHyphenationAllowed);
- // use normal lines, when possible
- lineLayouts.restorePossibilities();
- iLineWidth = savedLineWidth;
- }
- //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
- //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
- }
- }
- lineLayoutsList.add(0, lineLayouts);
- }
-
-
- setFinished(true);
-
- //Post-process the line breaks found
- return postProcessLineBreaks(alignment);
- }
-
- private LinkedList postProcessLineBreaks(int alignment) {
-
- LinkedList returnList = new LinkedList();
-
- for (int p = 0; p < knuthParagraphs.size(); p ++) {
- // null penalty between paragraphs
- if (p > 0
- && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
- returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
- }
-
- lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
-
- if (alignment == EN_JUSTIFY) {
- /* justified vertical alignment (not in the XSL FO recommendation):
- create a multi-layout sequence whose elements will contain
- a conventional Position */
- Position returnPosition = new LeafPosition(this, p);
- createElements(returnList, lineLayouts, returnPosition);
- } else {
- /* "normal" vertical alignment: create a sequence whose boxes
- represent effective lines, and contain LineBreakPositions */
- Position returnPosition = new LeafPosition(this, p);
- int startIndex = 0;
- for (int i = 0;
- i < lineLayouts.getChosenLineCount();
- i++) {
- if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
- && i >= fobj.getOrphans()
- && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()
- && returnList.size() > 0) {
- // null penalty allowing a page break between lines
- returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
- }
- int endIndex = ((LineBreakPosition) lineLayouts.getChosenPosition(i)).getLeafPos();
- // create a list of the FootnoteBodyLM handling footnotes
- // whose citations are in this line
- LinkedList footnoteList = new LinkedList();
- ListIterator elementIterator = ((Paragraph) knuthParagraphs.get(p)).listIterator(startIndex);
- while (elementIterator.nextIndex() <= endIndex) {
- KnuthElement element = (KnuthElement) elementIterator.next();
- if (element instanceof KnuthInlineBox
- && ((KnuthInlineBox) element).isAnchor()) {
- footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
- }
- }
- startIndex = endIndex + 1;
- returnList.add(new KnuthBlockBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight,
- footnoteList, lineLayouts.getChosenPosition(i), false));
- }
- }
- }
-
- return returnList;
- }
-
-
- private void createElements(List list, LineLayoutPossibilities lineLayouts,
- Position elementPosition) {
- /* number of normal, inner lines */
- int nInnerLines = 0;
- /* number of lines that can be used in order to fill more space */
- int nOptionalLines = 0;
- /* number of lines that can be used in order to fill more space
- only if the paragraph is not parted */
- int nConditionalOptionalLines = 0;
- /* number of lines that can be omitted in order to fill less space */
- int nEliminableLines = 0;
- /* number of lines that can be omitted in order to fill less space
- only if the paragraph is not parted */
- int nConditionalEliminableLines = 0;
- /* number of the first unbreakable lines */
- int nFirstLines = fobj.getOrphans();
- /* number of the last unbreakable lines */
- int nLastLines = fobj.getWidows();
- /* sub-sequence used to separate the elements representing different lines */
- List breaker = new LinkedList();
-
- /* comment out the next lines in order to test particular situations */
- if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMinLineCount()) {
- nInnerLines = lineLayouts.getMinLineCount() - (fobj.getOrphans() + fobj.getWidows());
- nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
- nEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
- } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getOptLineCount()) {
- nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
- nEliminableLines = lineLayouts.getOptLineCount() - (fobj.getOrphans() + fobj.getWidows());
- nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getMinLineCount();
- } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMaxLineCount()) {
- nOptionalLines = lineLayouts.getMaxLineCount() - (fobj.getOrphans() + fobj.getWidows());
- nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getOptLineCount();
- nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
- nFirstLines -= nConditionalOptionalLines;
- } else {
- nConditionalOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
- nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
- nFirstLines = lineLayouts.getOptLineCount();
- nLastLines = 0;
- }
- /* comment out the previous lines in order to test particular situations */
-
- /* use these lines to test particular situations
- nInnerLines = 0;
- nOptionalLines = 1;
- nConditionalOptionalLines = 2;
- nEliminableLines = 0;
- nConditionalEliminableLines = 0;
- nFirstLines = 1;
- nLastLines = 3;
- */
-
- if (nLastLines != 0
- && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
- breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
- breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
- -nConditionalEliminableLines * constantLineHeight,
- LINE_NUMBER_ADJUSTMENT, elementPosition, false));
- breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
- 0, false, elementPosition, false));
- breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
- nConditionalEliminableLines * constantLineHeight,
- LINE_NUMBER_ADJUSTMENT, elementPosition, false));
- } else if (nLastLines != 0) {
- breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
- }
-
- //System.out.println("first=" + nFirstLines + " inner=" + nInnerLines
- // + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
- // + " last=" + nLastLines
- // + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
-
- // creation of the elements:
- // first group of lines
- list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
- (nLastLines == 0
- && nConditionalOptionalLines == 0
- && nConditionalEliminableLines == 0 ? true : false)));
- if (nConditionalOptionalLines > 0
- || nConditionalEliminableLines > 0) {
- list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
- list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
- nConditionalEliminableLines * constantLineHeight,
- LINE_NUMBER_ADJUSTMENT, elementPosition, false));
- list.add(new KnuthBox(0, elementPosition,
- (nLastLines == 0 ? true : false)));
- }
-
- // optional lines
- for (int i = 0; i < nOptionalLines; i++) {
- list.addAll(breaker);
- list.add(new KnuthBox(0, elementPosition, false));
- list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
- list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
- LINE_NUMBER_ADJUSTMENT, elementPosition, false));
- list.add(new KnuthBox(0, elementPosition, false));
- }
-
- // eliminable lines
- for (int i = 0; i < nEliminableLines; i++) {
- list.addAll(breaker);
- list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
- list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
- list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
- LINE_NUMBER_ADJUSTMENT, elementPosition, false));
- list.add(new KnuthBox(0, elementPosition, false));
- }
-
- // inner lines
- for (int i = 0; i < nInnerLines; i++) {
- list.addAll(breaker);
- list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
- }
-
- // last group of lines
- if (nLastLines > 0) {
- list.addAll(breaker);
- list.add(new KnuthBox(nLastLines * constantLineHeight,
- elementPosition, true));
- }
- }
-
- public boolean mustKeepTogether() {
- return false;
- }
-
- public boolean mustKeepWithPrevious() {
- return false;
- }
-
- public boolean mustKeepWithNext() {
- return false;
- }
-
- public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
- LeafPosition pos = (LeafPosition)lastElement.getPosition();
- int totalAdj = adj;
- //if (lastElement.isPenalty()) {
- // totalAdj += lastElement.getW();
- //}
- //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
- int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight + (adj > 0 ? - 0.4 : 0.4));
- //System.out.println(" LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
- lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(pos.getLeafPos());
- lineNumberDifference = lineLayouts.applyLineCountAdjustment(lineNumberDifference);
- return lineNumberDifference * constantLineHeight;
- }
-
- public void discardSpace(KnuthGlue spaceGlue) {
- }
-
- public LinkedList getChangedKnuthElements(List oldList, int alignment) {
- LinkedList returnList = new LinkedList();
- for (int p = 0;
- p < knuthParagraphs.size();
- p ++) {
- lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
- //System.out.println("demerits of the chosen layout: " + lineLayouts.getChosenDemerits());
- for (int i = 0;
- i < lineLayouts.getChosenLineCount();
- i ++) {
- if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
- && i >= fobj.getOrphans()
- && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()) {
- // null penalty allowing a page break between lines
- returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
- }
- LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i);
- //System.out.println("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
- //System.out.println(" shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
-
- //System.out.println("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
- MinOptMax contentIPD;
- if (alignment == EN_JUSTIFY) {
- contentIPD = new MinOptMax(
- lbp.lineWidth - lbp.difference - lbp.availableShrink,
- lbp.lineWidth - lbp.difference,
- lbp.lineWidth - lbp.difference + lbp.availableStretch);
- } else if (alignment == EN_CENTER) {
- contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
- } else if (alignment == EN_END) {
- contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
- } else {
- contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
- }
- returnList.add(new KnuthBlockBox(lbp.lineHeight,
- contentIPD,
- (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0),
- lbp, false));
- }
- }
- return returnList;
- }
-
- /**
- * find hyphenation points for every word int the current paragraph
- * @ param currPar the paragraph whose words will be hyphenated
- */
- private void findHyphenationPoints(Paragraph currPar){
- // hyphenate every word
- ListIterator currParIterator
- = currPar.listIterator(currPar.ignoreAtStart);
- // list of TLM involved in hyphenation
- LinkedList updateList = new LinkedList();
- KnuthElement firstElement = null;
- KnuthElement nextElement = null;
- // current InlineLevelLayoutManager
- InlineLevelLayoutManager currLM = null;
- // number of KnuthBox elements containing word fragments
- int boxCount;
- // number of auxiliary KnuthElements between KnuthBoxes
- int auxCount;
- StringBuffer sbChars = null;
-
- // find all hyphenation points
- while (currParIterator.hasNext()) {
- firstElement = (KnuthElement) currParIterator.next();
- //
- if (firstElement.getLayoutManager() != currLM) {
- currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
- if (currLM != null) {
- updateList.add(new Update(currLM, currParIterator.previousIndex()));
- } else {
- break;
- }
- }
-
- // collect word fragments, ignoring auxiliary elements;
- // each word fragment was created by a different TextLM
- if (firstElement.isBox() && !firstElement.isAuxiliary()) {
- boxCount = 1;
- auxCount = 0;
- sbChars = new StringBuffer();
- currLM.getWordChars(sbChars, firstElement.getPosition());
- // look if next elements are boxes too
- while (currParIterator.hasNext()) {
- nextElement = (KnuthElement) currParIterator.next();
- if (nextElement.isBox() && !nextElement.isAuxiliary()) {
- // a non-auxiliary KnuthBox: append word chars
- if (currLM != nextElement.getLayoutManager()) {
- currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
- updateList.add(new Update(currLM, currParIterator.previousIndex()));
- }
- // append text to recreate the whole word
- boxCount ++;
- currLM.getWordChars(sbChars, nextElement.getPosition());
- } else if (!nextElement.isAuxiliary()) {
- // a non-auxiliary non-box KnuthElement: stop
- // go back to the last box or auxiliary element
- currParIterator.previous();
- break;
- } else {
- // an auxiliary KnuthElement: simply ignore it
- auxCount ++;
- }
- }
- log.trace(" Word to hyphenate: " + sbChars.toString());
- // find hyphenation points
- HyphContext hc = getHyphenContext(sbChars);
- // ask each LM to hyphenate its word fragment
- if (hc != null) {
- KnuthElement element = null;
- for (int i = 0; i < (boxCount + auxCount); i++) {
- currParIterator.previous();
- }
- for (int i = 0; i < (boxCount + auxCount); i++) {
- element = (KnuthElement) currParIterator.next();
- if (element.isBox() && !element.isAuxiliary()) {
- ((InlineLevelLayoutManager)
- element.getLayoutManager()).hyphenate(element.getPosition(), hc);
- } else {
- // nothing to do, element is an auxiliary KnuthElement
- }
- }
- }
- }
- }
-
- // create iterator for the updateList
- ListIterator updateListIterator = updateList.listIterator();
- Update currUpdate = null;
- //int iPreservedElements = 0;
- int iAddedElements = 0;
- //int iRemovedElements = 0;
-
- while (updateListIterator.hasNext()) {
- // ask the LMs to apply the changes and return
- // the new KnuthElements to replace the old ones
- currUpdate = (Update) updateListIterator.next();
- int fromIndex = currUpdate.iFirstIndex;
- int toIndex;
- if (updateListIterator.hasNext()) {
- Update nextUpdate = (Update) updateListIterator.next();
- toIndex = nextUpdate.iFirstIndex;
- updateListIterator.previous();
- } else {
- // maybe this is not always correct!
- toIndex = currPar.size() - currPar.ignoreAtEnd
- - iAddedElements;
- }
-
- // applyChanges() returns true if the LM modifies its data,
- // so it must return new KnuthElements to replace the old ones
- if (((InlineLevelLayoutManager) currUpdate.inlineLM)
- .applyChanges(currPar.subList(fromIndex + iAddedElements,
- toIndex + iAddedElements))) {
- // insert the new KnuthElements
- LinkedList newElements = null;
- newElements
- = currUpdate.inlineLM.getChangedKnuthElements
- (currPar.subList(fromIndex + iAddedElements,
- toIndex + iAddedElements),
- /*flaggedPenalty,*/ effectiveAlignment);
- // remove the old elements
- currPar.subList(fromIndex + iAddedElements,
- toIndex + iAddedElements).clear();
- // insert the new elements
- currPar.addAll(fromIndex + iAddedElements, newElements);
- iAddedElements += newElements.size() - (toIndex - fromIndex);
- }
- }
- updateListIterator = null;
- updateList.clear();
- }
-
- /** Line area is always considered to act as a fence. */
- protected boolean hasLeadingFence(boolean bNotFirst) {
- return true;
- }
-
- /** Line area is always considered to act as a fence. */
- protected boolean hasTrailingFence(boolean bNotLast) {
- return true;
- }
-
- private HyphContext getHyphenContext(StringBuffer sbChars) {
- // Find all hyphenation points in this word
- // (get in an array of offsets)
- // hyphProps are from the block level?.
- // Note that according to the spec,
- // they also "apply to" fo:character.
- // I don't know what that means, since
- // if we change language in the middle of a "word",
- // the effect would seem quite strange!
- // Or perhaps in that case, we say that it's several words.
- // We probably should bring the hyphenation props up from the actual
- // TextLM which generate the hyphenation buffer,
- // since these properties inherit and could be specified
- // on an inline or wrapper below the block level.
- Hyphenation hyph
- = Hyphenator.hyphenate(hyphProps.language,
- hyphProps.country, sbChars.toString(),
- hyphProps.hyphenationRemainCharacterCount,
- hyphProps.hyphenationPushCharacterCount);
- // They hyph structure contains the information we need
- // Now start from prev: reset to that position, ask that LM to get
- // a Position for the first hyphenation offset. If the offset isn't in
- // its characters, it returns null,
- // but must tell how many chars it had.
- // Keep looking at currentBP using next hyphenation point until the
- // returned size is greater than the available size
- // or no more hyphenation points remain. Choose the best break.
- if (hyph != null) {
- return new HyphContext(hyph.getHyphenationPoints());
- } else {
- return null;
- }
- }
-
- /**
- * Reset the positions to the given position.
- *
- * @param resetPos the position to reset to
- */
- public void resetPosition(Position resetPos) {
- if (resetPos == null) {
- setFinished(false);
- iReturnedLBP = 0;
- } else {
- if (isFinished()) {
- // if isFinished is true, iReturned LBP == breakpoints.size()
- // and breakpoints.get(iReturnedLBP) would generate
- // an IndexOutOfBoundException
- setFinished(false);
- iReturnedLBP--;
- }
- while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
- != (LineBreakPosition) resetPos) {
- iReturnedLBP--;
- }
- iReturnedLBP++;
- }
- }
-
- /**
- * Add the areas with the break points.
- *
- * @param parentIter the iterator of break positions
- * @param context the context for adding areas
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext context) {
- LayoutManager childLM;
- LayoutContext lc = new LayoutContext(0);
- int iCurrParIndex;
- while (parentIter.hasNext()) {
- Position pos = (Position) parentIter.next();
- if (pos instanceof LineBreakPosition) {
- ListIterator paragraphIterator = null;
- KnuthElement tempElement = null;
- // the TLM which created the last KnuthElement in this line
- LayoutManager lastLM = null;
-
- LineBreakPosition lbp = (LineBreakPosition) pos;
- LineArea lineArea = new LineArea();
- lineArea.setStartIndent(lbp.startIndent);
- lineArea.setBPD(lbp.lineHeight);
- lc.setBaseline(lbp.baseline);
- lc.setLineHeight(lbp.lineHeight);
- lc.setMiddleShift(middleShift);
- lc.setTopShift(lbp.topShift);
- lc.setBottomShift(lbp.bottomShift);
-
- iCurrParIndex = lbp.iParIndex;
- Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
- iEndElement = lbp.getLeafPos();
-
- // ignore the first elements added by the LineLayoutManager
- iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
- // ignore the last elements added by the LineLayoutManager
- iEndElement -= (iEndElement == (currPar.size() - 1))
- ? currPar.ignoreAtEnd : 0;
-
- // ignore the last element in the line if it is a KnuthGlue object
- paragraphIterator = currPar.listIterator(iEndElement);
- tempElement = (KnuthElement) paragraphIterator.next();
- if (tempElement.isGlue()) {
- iEndElement --;
- // this returns the same KnuthElement
- paragraphIterator.previous();
- tempElement = (KnuthElement) paragraphIterator.previous();
- }
- lastLM = tempElement.getLayoutManager();
-
- // ignore KnuthGlue and KnuthPenalty objects
- // at the beginning of the line
- paragraphIterator = currPar.listIterator(iStartElement);
- tempElement = (KnuthElement) paragraphIterator.next();
- while (!tempElement.isBox() && paragraphIterator.hasNext()) {
- tempElement = (KnuthElement) paragraphIterator.next();
- iStartElement ++;
- }
-
- // Add the inline areas to lineArea
- PositionIterator inlinePosIter
- = new KnuthPossPosIter(currPar, iStartElement,
- iEndElement + 1);
-
- iStartElement = lbp.getLeafPos() + 1;
- if (iStartElement == currPar.size()) {
- // advance to next paragraph
- iStartElement = 0;
- }
-
- lc.setSpaceAdjust(lbp.dAdjust);
- lc.setIPDAdjust(lbp.ipdAdjust);
- lc.setLeadingSpace(new SpaceSpecifier(true));
- lc.setTrailingSpace(new SpaceSpecifier(false));
- lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
-
- /* extension (not in the XSL FO recommendation): if the left and right margins
- have been optimized, recompute indents and / or adjust ratio, according
- to the paragraph horizontal alignment */
- if (false && bTextAlignment == EN_JUSTIFY) {
- // re-compute space adjust ratio
- int updatedDifference = context.getStackLimit().opt - lbp.lineWidth + lbp.difference;
- double updatedRatio = 0.0;
- if (updatedDifference > 0) {
- updatedRatio = (float) updatedDifference / lbp.availableStretch;
- } else if (updatedDifference < 0) {
- updatedRatio = (float) updatedDifference / lbp.availableShrink;
- }
- lc.setIPDAdjust(updatedRatio);
- //System.out.println("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
- //System.out.println(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
- } else if (false && bTextAlignment == EN_CENTER) {
- // re-compute indent
- int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth) / 2;
- lineArea.setStartIndent(updatedIndent);
- } else if (false && bTextAlignment == EN_END) {
- // re-compute indent
- int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth);
- lineArea.setStartIndent(updatedIndent);
- }
-
- setCurrentArea(lineArea);
- setChildContext(lc);
- while ((childLM = inlinePosIter.getNextChildLM()) != null) {
- lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
- childLM.addAreas(inlinePosIter, lc);
- lc.setLeadingSpace(lc.getTrailingSpace());
- lc.setTrailingSpace(new SpaceSpecifier(false));
- }
-
- // when can this be null?
- // if display-align is distribute, add space after
- if (context.getSpaceAfter() > 0
- && (!context.isLastArea() || parentIter.hasNext())) {
- lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
- }
- parentLM.addChildArea(lineArea);
- } else {
- // pos was the Position inside a penalty item, nothing to do
- }
- }
- setCurrentArea(null); // ?? necessary
- }
-}
-
+++ /dev/null
-/*
- * Copyright 2004-2005 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.List;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-public class LineLayoutPossibilities {
-
- /** logger instance */
- protected static Log log = LogFactory.getLog(LineLayoutPossibilities.class);
-
- private class Possibility {
- private int lineCount;
- private double demerits;
- private List breakPositions;
-
- private Possibility(int lc, double dem) {
- lineCount = lc;
- demerits = dem;
- breakPositions = new java.util.ArrayList(lc);
- }
-
- private int getLineCount() {
- return lineCount;
- }
-
- private double getDemerits() {
- return demerits;
- }
-
- private void addBreakPosition(Position pos) {
- // Positions are always added with index 0 because
- // they are created backward, from the last one to
- // the first one
- breakPositions.add(0, pos);
- }
-
- private Position getBreakPosition(int i) {
- return (Position)breakPositions.get(i);
- }
- }
-
- private List possibilitiesList;
- private List savedPossibilities;
- private int minimumIndex;
- private int optimumIndex;
- private int maximumIndex;
- private int chosenIndex;
- private int savedOptLineCount;
-
- public LineLayoutPossibilities() {
- possibilitiesList = new java.util.ArrayList();
- savedPossibilities = new java.util.ArrayList();
- optimumIndex = -1;
- }
-
- public void addPossibility(int ln, double dem) {
- possibilitiesList.add(new Possibility(ln, dem));
- if (possibilitiesList.size() == 1) {
- // first Possibility added
- minimumIndex = 0;
- optimumIndex = 0;
- maximumIndex = 0;
- chosenIndex = 0;
- } else {
- if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) {
- optimumIndex = possibilitiesList.size() - 1;
- chosenIndex = optimumIndex;
- }
- if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineCount()) {
- minimumIndex = possibilitiesList.size() - 1;
- }
- if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineCount()) {
- maximumIndex = possibilitiesList.size() - 1;
- }
- }
- }
-
- /* save in a different array the computed Possibilities,
- * so possibilitiesList is ready to store different Possibilities
- */
- public void savePossibilities(boolean bSaveOptLineCount) {
- if (bSaveOptLineCount) {
- savedOptLineCount = getOptLineCount();
- } else {
- savedOptLineCount = 0;
- }
- savedPossibilities = possibilitiesList;
- possibilitiesList = new java.util.ArrayList();
- }
-
- /* replace the Possibilities stored in possibilitiesList with
- * the ones stored in savedPossibilities and having the same line number
- */
- public void restorePossibilities() {
- int index = 0;
- while (savedPossibilities.size() > 0) {
- Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0);
- if (restoredPossibility.getLineCount() < getMinLineCount()) {
- // if the line number of restoredPossibility is less than the minimum one,
- // add restoredPossibility at the beginning of the list
- possibilitiesList.add(0, restoredPossibility);
- // update minimumIndex
- minimumIndex = 0;
- // shift the other indexes;
- optimumIndex ++;
- maximumIndex ++;
- chosenIndex ++;
- } else if (restoredPossibility.getLineCount() > getMaxLineCount()) {
- // if the line number of restoredPossibility is greater than the maximum one,
- // add restoredPossibility at the end of the list
- possibilitiesList.add(possibilitiesList.size(), restoredPossibility);
- // update maximumIndex
- maximumIndex = possibilitiesList.size() - 1;
- index = maximumIndex;
- } else {
- // find the index of the Possibility that will be replaced
- while (index < maximumIndex
- && getLineCount(index) < restoredPossibility.getLineCount()) {
- index ++;
- }
- if (getLineCount(index) == restoredPossibility.getLineCount()) {
- possibilitiesList.set(index, restoredPossibility);
- } else {
- // this should not happen
- log.error("LineLayoutPossibilities restorePossibilities(),"
- + " min= " + getMinLineCount()
- + " max= " + getMaxLineCount()
- + " restored= " + restoredPossibility.getLineCount());
- return;
- }
- }
- // update optimumIndex and chosenIndex
- if (savedOptLineCount == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits()
- || savedOptLineCount != 0 && restoredPossibility.getLineCount() == savedOptLineCount) {
- optimumIndex = index;
- chosenIndex = optimumIndex;
- }
- }
-/*LF*/ //System.out.println(">> minLineCount = " + getMinLineCount() + " optLineCount = " + getOptLineCount() + " maxLineCount() = " + getMaxLineCount());
- }
-
- public void addBreakPosition(Position pos, int i) {
- ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos);
- }
-
- public boolean canUseMoreLines() {
- return (getOptLineCount() < getMaxLineCount());
- }
-
- public boolean canUseLessLines() {
- return (getMinLineCount() < getOptLineCount());
- }
-
- public int getMinLineCount() {
- return getLineCount(minimumIndex);
- }
-
- public int getOptLineCount() {
- return getLineCount(optimumIndex);
- }
-
- public int getMaxLineCount() {
- return getLineCount(maximumIndex);
- }
-
- public int getChosenLineCount() {
- return getLineCount(chosenIndex);
- }
-
- public int getLineCount(int i) {
- return ((Possibility)possibilitiesList.get(i)).getLineCount();
- }
-
- public double getChosenDemerits() {
- return getDemerits(chosenIndex);
- }
-
- public double getDemerits(int i) {
- return ((Possibility)possibilitiesList.get(i)).getDemerits();
- }
-
- public int getPossibilitiesNumber() {
- return possibilitiesList.size();
- }
-
- public Position getChosenPosition(int i) {
- return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i);
- }
-
- public int applyLineCountAdjustment(int adj) {
- if (adj >= (getMinLineCount() - getChosenLineCount())
- && adj <= (getMaxLineCount() - getChosenLineCount())
- && getLineCount(chosenIndex + adj) == getChosenLineCount() + adj) {
- chosenIndex += adj;
- log.debug("chosenLineCount= " + (getChosenLineCount() - adj) + " adjustment= " + adj
- + " => chosenLineCount= " + getLineCount(chosenIndex));
- return adj;
- } else {
- // this should not happen!
- log.warn("Cannot apply the desired line count adjustment.");
- return 0;
- }
- }
-
- public void printAll() {
- System.out.println("++++++++++");
- System.out.println(" " + possibilitiesList.size() + " possibility':");
- for (int i = 0; i < possibilitiesList.size(); i ++) {
- System.out.println(" " + ((Possibility)possibilitiesList.get(i)).getLineCount()
- + (i == optimumIndex ? " *" : "")
- + (i == minimumIndex ? " -" : "")
- + (i == maximumIndex ? " +" : ""));
- }
- System.out.println("++++++++++");
- }
-}
+++ /dev/null
-/*
- * Copyright 1999-2005 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 org.apache.fop.fo.flow.PageNumberCitation;
-import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.Trait;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.UnresolvedPageNumber;
-import org.apache.fop.area.inline.TextArea;
-import org.apache.fop.fonts.Font;
-
-/**
- * LayoutManager for the fo:page-number-citation formatting object
- */
-public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager {
-
- private PageNumberCitation fobj;
- private Font font;
-
- // whether the page referred to by the citation has been resolved yet
- private boolean resolved = false;
-
- /**
- * Constructor
- *
- * @param node the formatting object that creates this area
- * @todo better retrieval of font info
- */
- public PageNumberCitationLayoutManager(PageNumberCitation node) {
- super(node);
- fobj = node;
- font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
- }
-
- public InlineArea get(LayoutContext context) {
- curArea = getPageNumberCitationInlineArea(parentLM);
- return curArea;
- }
-
- public void addAreas(PositionIterator posIter, LayoutContext context) {
- super.addAreas(posIter, context);
- if (!resolved) {
- getPSLM().addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
- }
- }
-
- /** @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#getLead() */
- public int getLead() {
- return font.getAscender();
- }
-
- protected void offsetArea(InlineArea area, LayoutContext context) {
- area.setOffset(context.getBaseline());
- }
-
- /**
- * if id can be resolved then simply return a word, otherwise
- * return a resolvable area
- */
- private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) {
- PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
- InlineArea inline = null;
- if (page != null) {
- String str = page.getPageNumberString();
- // get page string from parent, build area
- TextArea text = new TextArea();
- inline = text;
- int width = getStringWidth(str);
- text.setTextArea(str);
- inline.setIPD(width);
-
- resolved = true;
- } else {
- resolved = false;
- inline = new UnresolvedPageNumber(fobj.getRefId());
- String str = "MMM"; // reserve three spaces for page number
- int width = getStringWidth(str);
- inline.setIPD(width);
-
- }
- inline.setBPD(font.getAscender() - font.getDescender());
- inline.setOffset(font.getAscender());
- inline.addTrait(Trait.FONT_NAME, font.getFontName());
- inline.addTrait(Trait.FONT_SIZE, new Integer(font.getFontSize()));
- TraitSetter.addTextDecoration(inline, fobj.getTextDecoration());
-
- return inline;
- }
-
- /**
- * @param str string to be measured
- * @return width (in millipoints ??) of the string
- */
- private int getStringWidth(String str) {
- int width = 0;
- for (int count = 0; count < str.length(); count++) {
- width += font.getCharWidth(str.charAt(count));
- }
- return width;
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
-
+++ /dev/null
-/*
- * Copyright 1999-2005 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 org.apache.fop.fo.flow.PageNumber;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.TextArea;
-import org.apache.fop.area.Trait;
-import org.apache.fop.fonts.Font;
-
-/**
- * LayoutManager for the fo:page-number formatting object
- */
-public class PageNumberLayoutManager extends LeafNodeLayoutManager {
-
- private PageNumber fobj;
- private Font font;
-
- /**
- * Constructor
- *
- * @param node the fo:page-number formatting object that creates the area
- * @todo better null checking of node, font
- */
- public PageNumberLayoutManager(PageNumber node) {
- super(node);
- fobj = node;
- font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
- }
-
- public InlineArea get(LayoutContext context) {
- // get page string from parent, build area
- TextArea inline = new TextArea();
- String str = getCurrentPV().getPageNumberString();
- int width = 0;
- for (int count = 0; count < str.length(); count++) {
- width += font.getCharWidth(str.charAt(count));
- }
- inline.setTextArea(str);
- inline.setIPD(width);
- inline.setBPD(font.getAscender() - font.getDescender());
- inline.setOffset(font.getAscender());
- inline.addTrait(Trait.FONT_NAME, font.getFontName());
- inline.addTrait(Trait.FONT_SIZE,
- new Integer(font.getFontSize()));
-
- TraitSetter.addTextDecoration(inline, fobj.getTextDecoration());
-
- return inline;
- }
-
-
- /** @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#getLead() */
- public int getLead() {
- return font.getAscender();
- }
-
- protected void offsetArea(InlineArea area, LayoutContext context) {
- area.setOffset(context.getBaseline());
- }
-
- protected InlineArea getEffectiveArea() {
- TextArea baseArea = (TextArea)curArea;
- //TODO Maybe replace that with a clone() call or better, a copy constructor
- //TODO or even better: delay area creation until addAreas() stage
- //TextArea is cloned because the LM is reused in static areas and the area can't be.
- TextArea ta = new TextArea();
- ta.setIPD(baseArea.getIPD());
- ta.setBPD(baseArea.getBPD());
- ta.setOffset(baseArea.getOffset());
- ta.addTrait(Trait.FONT_NAME, font.getFontName()); //only to initialize the trait map
- ta.getTraits().putAll(baseArea.getTraits());
- updateContent(ta);
- return ta;
- }
-
- private void updateContent(TextArea area) {
- area.setTextArea(getCurrentPV().getPageNumberString());
- }
-
- protected void addId() {
- getPSLM().addIDToPage(fobj.getId());
- }
-}
-
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.traits.MinOptMax;
/**
+++ /dev/null
-/*
- * Copyright 1999-2005 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.ArrayList;
-import java.util.List;
-import java.util.LinkedList;
-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;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.TextArea;
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * LayoutManager for text (a sequence of characters) which generates one
- * or more inline areas.
- */
-public class TextLayoutManager extends LeafNodeLayoutManager {
-
- /**
- * Store information about each potential text area.
- * Index of character which ends the area, IPD of area, including
- * any word-space and letter-space.
- * Number of word-spaces?
- */
- private class AreaInfo {
- private short iStartIndex;
- private short iBreakIndex;
- private short iWScount;
- private short iLScount;
- private MinOptMax ipdArea;
- private boolean bHyphenated;
- public AreaInfo(short iSIndex, short iBIndex, short iWS, short iLS,
- MinOptMax ipd, boolean bHyph) {
- iStartIndex = iSIndex;
- iBreakIndex = iBIndex;
- iWScount = iWS;
- iLScount = iLS;
- ipdArea = ipd;
- bHyphenated = bHyph;
- }
- }
-
- // this class stores information about changes in vecAreaInfo
- // which are not yet applied
- private class PendingChange {
- public AreaInfo ai;
- public int index;
-
- public PendingChange(AreaInfo ai, int index) {
- this.ai = ai;
- this.index = index;
- }
- }
-
- // Hold all possible breaks for the text in this LM's FO.
- private ArrayList vecAreaInfo;
-
- /** Non-space characters on which we can end a line. */
- private static final String BREAK_CHARS = "-/" ;
-
- private FOText foText;
- private char[] textArray;
-
- private static final char NEWLINE = '\n';
- private static final char SPACE = '\u0020'; // Normal space
- private static final char NBSPACE = '\u00A0'; // Non-breaking space
- private static final char LINEBREAK = '\u2028';
- private static final char ZERO_WIDTH_SPACE = '\u200B';
- // byte order mark
- private static final char ZERO_WIDTH_NOBREAK_SPACE = '\uFEFF';
-
- /** Start index of first character in this parent Area */
- private short iAreaStart = 0;
- /** Start index of next TextArea */
- private short iNextStart = 0;
- /** Size since last makeArea call, except for last break */
- private MinOptMax ipdTotal;
- /** Size including last break possibility returned */
- // private MinOptMax nextIPD = new MinOptMax(0);
- /** size of a space character (U+0020) glyph in current font */
- private int spaceCharIPD;
- private MinOptMax wordSpaceIPD;
- private MinOptMax letterSpaceIPD;
- /** size of the hyphen character glyph in current font */
- private int hyphIPD;
- /** 1/2 of word-spacing value */
- private SpaceVal halfWS;
- /** Number of space characters after previous possible break position. */
- private int iNbSpacesPending;
- private Font fs;
-
- private boolean bChanged = false;
- private int iReturnedIndex = 0;
- private short iThisStart = 0;
- 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 = EN_BASELINE;
-
- /**
- * Create a Text layout manager.
- *
- * @param node The FOText object to be rendered
- */
- public TextLayoutManager(FOText node) {
- super();
- foText = node;
-
- textArray = new char[node.endIndex - node.startIndex];
- System.arraycopy(node.ca, node.startIndex, textArray, 0,
- node.endIndex - node.startIndex);
-
- vecAreaInfo = new java.util.ArrayList();
-
- fs = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo());
-
- // With CID fonts, space isn't neccesary currentFontState.width(32)
- spaceCharIPD = fs.getCharWidth(' ');
- // Use hyphenationChar property
- hyphIPD = fs.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter);
- // Make half-space: <space> on either side of a word-space)
- SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
- SpaceVal ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, fs);
- halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5),
- ws.isConditional(), ws.isForcing(), ws.getPrecedence());
-
- // letter space applies only to consecutive non-space characters,
- // while word space applies to space characters;
- // i.e. the spaces in the string "A SIMPLE TEST" are:
- // A<<ws>>S<ls>I<ls>M<ls>P<ls>L<ls>E<<ws>>T<ls>E<ls>S<ls>T
- // there is no letter space after the last character of a word,
- // nor after a space character
-
- // set letter space and word space dimension;
- // the default value "normal" was converted into a MinOptMax value
- // 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 EN_MIDDLE : middle = textHeight / 2 ;
- break;
- case EN_TOP : // fall through
- case EN_BOTTOM : total = textHeight;
- break;
- case EN_BASELINE: // fall through
- default : lead = fs.getAscender();
- total = textHeight;
- break;
- }
- }
-
- /**
- * Reset position for returning next BreakPossibility.
- *
- * @param prevPos the position to reset to
- */
- public void resetPosition(Position prevPos) {
- if (prevPos != null) {
- // ASSERT (prevPos.getLM() == this)
- if (prevPos.getLM() != this) {
- log.error("TextLayoutManager.resetPosition: "
- + "LM mismatch!!!");
- }
- LeafPosition tbp = (LeafPosition) prevPos;
- AreaInfo ai =
- (AreaInfo) vecAreaInfo.get(tbp.getLeafPos());
- if (ai.iBreakIndex != iNextStart) {
- iNextStart = ai.iBreakIndex;
- vecAreaInfo.ensureCapacity(tbp.getLeafPos() + 1);
- // TODO: reset or recalculate total IPD = sum of all word IPD
- // up to the break position
- ipdTotal = ai.ipdArea;
- setFinished(false);
- }
- } else {
- // Reset to beginning!
- vecAreaInfo.clear();
- iNextStart = 0;
- setFinished(false);
- }
- }
-
- // TODO: see if we can use normal getNextBreakPoss for this with
- // extra hyphenation information in LayoutContext
- private boolean getHyphenIPD(HyphContext hc, MinOptMax hyphIPD) {
- // Skip leading word-space before calculating count?
- boolean bCanHyphenate = true;
- int iStopIndex = iNextStart + hc.getNextHyphPoint();
-
- if (textArray.length < iStopIndex) {
- iStopIndex = textArray.length;
- bCanHyphenate = false;
- }
- hc.updateOffset(iStopIndex - iNextStart);
-
- for (; iNextStart < iStopIndex; iNextStart++) {
- char c = textArray[iNextStart];
- hyphIPD.opt += fs.getCharWidth(c);
- // letter-space?
- }
- // Need to include hyphen size too, but don't count it in the
- // stored running total, since it would be double counted
- // with later hyphenation points
- return bCanHyphenate;
- }
-
- /**
- * Generate and add areas to parent area.
- * This can either generate an area for each TextArea and each space, or
- * an area containing all text with a parameter controlling the size of
- * the word space. The latter is most efficient for PDF generation.
- * Set size of each area.
- * @param posIter Iterator over Position information returned
- * by this LayoutManager.
- * @param context LayoutContext for adjustments
- */
- public void addAreas(PositionIterator posIter, LayoutContext context) {
-
- // Add word areas
- AreaInfo ai = null;
- int iStart = -1;
- int iWScount = 0;
- int iLScount = 0;
- MinOptMax realWidth = new MinOptMax(0);
-
- /* On first area created, add any leading space.
- * Calculate word-space stretch value.
- */
- while (posIter.hasNext()) {
- LeafPosition tbpNext = (LeafPosition) posIter.next();
- //
- if (tbpNext.getLeafPos() != -1) {
- ai = (AreaInfo) vecAreaInfo.get(tbpNext.getLeafPos());
- if (iStart == -1) {
- iStart = ai.iStartIndex;
- }
- iWScount += ai.iWScount;
- iLScount += ai.iLScount;
- realWidth.add(ai.ipdArea);
- }
- }
- if (ai == null) {
- return;
- }
-
- // Make an area containing all characters between start and end.
- InlineArea word = null;
- int adjust = 0;
-
- // ignore newline character
- if (textArray[ai.iBreakIndex - 1] == NEWLINE) {
- adjust = 1;
- }
- String str = new String(textArray, iStart,
- ai.iBreakIndex - iStart - adjust);
-
- // add hyphenation character if the last word is hyphenated
- if (context.isLastArea() && ai.bHyphenated) {
- str += foText.getCommonHyphenation().hyphenationCharacter;
- realWidth.add(new MinOptMax(hyphIPD));
- }
-
- // Calculate adjustments
- int iDifference = 0;
- int iTotalAdjust = 0;
- int iWordSpaceDim = wordSpaceIPD.opt;
- int iLetterSpaceDim = letterSpaceIPD.opt;
- double dIPDAdjust = context.getIPDAdjust();
- double dSpaceAdjust = context.getSpaceAdjust(); // not used
-
- // calculate total difference between real and available width
- if (dIPDAdjust > 0.0) {
- iDifference = (int) ((double) (realWidth.max - realWidth.opt)
- * dIPDAdjust);
- } else {
- iDifference = (int) ((double) (realWidth.opt - realWidth.min)
- * dIPDAdjust);
- }
-
- // set letter space adjustment
- if (dIPDAdjust > 0.0) {
- iLetterSpaceDim
- += (int) ((double) (letterSpaceIPD.max - letterSpaceIPD.opt)
- * dIPDAdjust);
- } else {
- iLetterSpaceDim
- += (int) ((double) (letterSpaceIPD.opt - letterSpaceIPD.min)
- * dIPDAdjust);
- }
- iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount;
-
- // set word space adjustment
- //
- if (iWScount > 0) {
- iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount);
- } else {
- // there are no word spaces in this area
- }
- iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
-
- TextArea t = createTextArea(str, realWidth.opt + iTotalAdjust,
- context);
-
- // iWordSpaceDim is computed in relation to wordSpaceIPD.opt
- // but the renderer needs to know the adjustment in relation
- // to the size of the space character in the current font;
- // moreover, the pdf renderer adds the character spacing even to
- // the last character of a word and to space characters: in order
- // to avoid this, we must subtract the letter space width twice;
- // the renderer will compute the space width as:
- // space width =
- // = "normal" space width + letterSpaceAdjust + wordSpaceAdjust
- // = spaceCharIPD + letterSpaceAdjust +
- // + (iWordSpaceDim - spaceCharIPD - 2 * letterSpaceAdjust)
- // = iWordSpaceDim - letterSpaceAdjust
- t.setTextLetterSpaceAdjust(iLetterSpaceDim);
- t.setTextWordSpaceAdjust(iWordSpaceDim - spaceCharIPD
- - 2 * t.getTextLetterSpaceAdjust());
-
- word = t;
- if (word != null) {
- parentLM.addChildArea(word);
- }
- }
-
- /**
- * Create an inline word area.
- * This creates a TextArea and sets up the various attributes.
- *
- * @param str the string for the TextArea
- * @param width the width that the TextArea uses
- * @param base the baseline position
- * @return the new word area
- */
- protected TextArea createTextArea(String str, int width, LayoutContext context) {
- TextArea textArea = new TextArea();
- textArea.setIPD(width);
- textArea.setBPD(fs.getAscender() - fs.getDescender());
- int bpd = textArea.getBPD();
- switch (verticalAlignment) {
- case EN_MIDDLE:
- textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
- break;
- case EN_TOP:
- textArea.setOffset(context.getTopBaseline() + fs.getAscender());
- break;
- case EN_BOTTOM:
- textArea.setOffset(context.getBottomBaseline() - bpd + fs.getAscender());
- break;
- case EN_BASELINE:
- default:
- textArea.setOffset(context.getBaseline());
- break;
- }
-
- textArea.setTextArea(str);
- textArea.addTrait(Trait.FONT_NAME, fs.getFontName());
- textArea.addTrait(Trait.FONT_SIZE, new Integer(fs.getFontSize()));
- textArea.addTrait(Trait.COLOR, foText.getColor());
-
- TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
-
- 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
- || 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 EN_CENTER :
- vecAreaInfo.add
- (new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false));
- returnList.add
- (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 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- iNextStart ++;
- break;
-
- case EN_START : // fall through
- case EN_END :
- vecAreaInfo.add
- (new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false));
- returnList.add
- (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 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,
- - 3 * wordSpaceIPD.opt, 0,
- new LeafPosition(this, -1), true));
- iNextStart ++;
- break;
-
- case EN_JUSTIFY:
- vecAreaInfo.add
- (new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false));
- returnList.add
- (new KnuthGlue(wordSpaceIPD.opt,
- wordSpaceIPD.max - wordSpaceIPD.opt,
- wordSpaceIPD.opt - wordSpaceIPD.min,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- iNextStart ++;
- break;
-
- default:
- vecAreaInfo.add
- (new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false));
- returnList.add
- (new KnuthGlue(wordSpaceIPD.opt,
- wordSpaceIPD.max - wordSpaceIPD.opt, 0,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- iNextStart ++;
- }
- } else if (textArray[iNextStart] == NBSPACE) {
- // non breaking space
- vecAreaInfo.add
- (new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false));
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- returnList.add
- (new KnuthGlue(wordSpaceIPD.opt,
- wordSpaceIPD.max - wordSpaceIPD.opt,
- wordSpaceIPD.opt - wordSpaceIPD.min,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- iNextStart ++;
- } else if (textArray[iNextStart] == NEWLINE) {
- // linefeed; this can happen when linefeed-treatment="preserve"
- // add a penalty item to the list and return
- returnList.add
- (new KnuthPenalty(0, -KnuthElement.INFINITE,
- false, null, false));
- iNextStart ++;
- return returnList;
- } else {
- // the beginning of a word
- iThisStart = iNextStart;
- iTempStart = iNextStart;
- MinOptMax wordIPD = new MinOptMax(0);
- for (; iTempStart < textArray.length
- && textArray[iTempStart] != SPACE
- && textArray[iTempStart] != NBSPACE
- && textArray[iTempStart] != NEWLINE
- && !(iTempStart > iNextStart
- && alignment == EN_JUSTIFY
- && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
- iTempStart++) {
- wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
- }
- int iLetterSpaces = iTempStart - iThisStart - 1;
- wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
- vecAreaInfo.add
- (new AreaInfo(iThisStart, iTempStart, (short) 0,
- (short) iLetterSpaces,
- wordIPD, false));
- if (letterSpaceIPD.min == letterSpaceIPD.max) {
- // constant letter space; simply return a box
- // whose width includes letter spaces
- returnList.add
- (new KnuthInlineBox(wordIPD.opt, lead, total, middle,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- } else {
- // adjustable letter space;
- // some other KnuthElements are needed
- returnList.add
- (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt,
- lead, total, middle,
- new LeafPosition(this, vecAreaInfo.size() - 1), false));
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt,
- iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt),
- iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min),
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthInlineBox(0, lead, total, middle,
- new LeafPosition(this, -1), true));
- }
- // if the last character is '-' or '/', it could be used as a line end;
- // add a flagged penalty element and glue element representing a suppressible
- // letter space if the next character is not a space
- if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
- && iTempStart < textArray.length
- && textArray[iTempStart] != SPACE
- && textArray[iTempStart] != NBSPACE) {
- returnList.add
- (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- returnList.add
- (new KnuthGlue(letterSpaceIPD.opt,
- letterSpaceIPD.max - letterSpaceIPD.opt,
- letterSpaceIPD.opt - letterSpaceIPD.min,
- new LeafPosition(this, -1), false));
- // update the information in the AreaInfo, adding one more letter space
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(vecAreaInfo.size() - 1);
- ai.iLScount ++;
- }
- iNextStart = iTempStart;
- }
- } // end of while
- setFinished(true);
- if (returnList.size() > 0) {
- return returnList;
- } else {
- return null;
- }
- }
-
- public List addALetterSpaceTo(List oldList) {
- // old list contains only a box, or the sequence: box penalty glue box;
- // look at the Position stored in the first element in oldList
- // which is always a box
- ListIterator oldListIterator = oldList.listIterator();
- LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition();
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
- ai.iLScount ++;
- ai.ipdArea.add(letterSpaceIPD);
- if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
- // the last character could be used as a line break
- // append new elements to oldList
- oldListIterator = oldList.listIterator(oldList.size());
- oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
- letterSpaceIPD.max - letterSpaceIPD.opt,
- letterSpaceIPD.opt - letterSpaceIPD.min,
- new LeafPosition(this, -1), false));
- } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
- // constant letter space: replace the box
- oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle, pos, false));
- } else {
- // adjustable letter space: replace the glue
- oldListIterator.next(); // this would return the penalty element
- oldListIterator.next(); // this would return the glue element
- oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
- ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
- ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
- new LeafPosition(this, -1), true));
- }
- return oldList;
- }
-
- public void hyphenate(Position pos, HyphContext hc) {
- AreaInfo ai
- = (AreaInfo) vecAreaInfo.get(((LeafPosition) pos).getLeafPos());
- int iStartIndex = ai.iStartIndex;
- int iStopIndex;
- boolean bNothingChanged = true;
-
- while (iStartIndex < ai.iBreakIndex) {
- MinOptMax newIPD = new MinOptMax(0);
- boolean bHyphenFollows;
-
- if (hc.hasMoreHyphPoints()
- && (iStopIndex = iStartIndex + hc.getNextHyphPoint())
- <= ai.iBreakIndex) {
- // iStopIndex is the index of the first character
- // after a hyphenation point
- bHyphenFollows = true;
- } else {
- // there are no more hyphenation points,
- // or the next one is after ai.iBreakIndex
- bHyphenFollows = false;
- iStopIndex = ai.iBreakIndex;
- }
-
- hc.updateOffset(iStopIndex - iStartIndex);
-
- for (int i = iStartIndex; i < iStopIndex; i++) {
- char c = textArray[i];
- newIPD.add(new MinOptMax(fs.getCharWidth(c)));
- }
- // add letter spaces
- boolean bIsWordEnd
- = iStopIndex == ai.iBreakIndex
- && ai.iLScount < (ai.iBreakIndex - ai.iStartIndex);
- newIPD.add(MinOptMax.multiply(letterSpaceIPD,
- (bIsWordEnd
- ? (iStopIndex - iStartIndex - 1)
- : (iStopIndex - iStartIndex))));
-
- if (!(bNothingChanged
- && iStopIndex == ai.iBreakIndex
- && bHyphenFollows == false)) {
- // the new AreaInfo object is not equal to the old one
- if (changeList == null) {
- changeList = new LinkedList();
- }
- changeList.add
- (new PendingChange
- (new AreaInfo((short) iStartIndex, (short) iStopIndex,
- (short) 0,
- (short) (bIsWordEnd
- ? (iStopIndex - iStartIndex - 1)
- : (iStopIndex - iStartIndex)),
- newIPD, bHyphenFollows),
- ((LeafPosition) pos).getLeafPos()));
- bNothingChanged = false;
- }
- iStartIndex = iStopIndex;
- }
- if (!bChanged && !bNothingChanged) {
- bChanged = true;
- }
- }
-
- public boolean applyChanges(List oldList) {
- setFinished(false);
-
- if (changeList != null) {
- int iAddedAI = 0;
- int iRemovedAI = 0;
- int iOldIndex = -1;
- PendingChange currChange = null;
- ListIterator changeListIterator = changeList.listIterator();
- while (changeListIterator.hasNext()) {
- currChange = (PendingChange) changeListIterator.next();
- if (currChange.index != iOldIndex) {
- iRemovedAI ++;
- iAddedAI ++;
- iOldIndex = currChange.index;
- vecAreaInfo.remove(currChange.index + iAddedAI - iRemovedAI);
- vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
- currChange.ai);
- } else {
- iAddedAI ++;
- vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
- currChange.ai);
- }
- }
- changeList.clear();
- }
-
- iReturnedIndex = 0;
- return bChanged;
- }
-
- public LinkedList getChangedKnuthElements(List oldList,
- /*int flaggedPenalty,*/
- int alignment) {
- if (isFinished()) {
- return null;
- }
-
- LinkedList returnList = new LinkedList();
-
- while (iReturnedIndex < vecAreaInfo.size()) {
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
- if (ai.iWScount == 0) {
- // ai refers either to a word or a word fragment
-
- // if the last character is '-' or '/' and the next character is not a space
- // one of the letter spaces must be represented using a penalty and a glue,
- // and its width must be subtracted
- if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
- && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
- ai.ipdArea.add(new MinOptMax(-letterSpaceIPD.min, -letterSpaceIPD.opt, -letterSpaceIPD.max));
- }
- if (letterSpaceIPD.min == letterSpaceIPD.max) {
- returnList.add
- (new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
- new LeafPosition(this, iReturnedIndex), false));
- } else {
- returnList.add
- (new KnuthInlineBox(ai.ipdArea.opt
- - ai.iLScount * letterSpaceIPD.opt,
- lead, total, middle,
- new LeafPosition(this, iReturnedIndex), false));
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
- ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
- ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), true));
- }
- if (ai.bHyphenated) {
- returnList.add
- (new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- }
- // if the last character is '-' or '/', it could be used as a line end;
- // add a flagged penalty element and a glue element representing a suppressible
- // letter space if the next character is not a space
- if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
- && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
- returnList.add
- (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- returnList.add
- (new KnuthGlue(letterSpaceIPD.opt,
- letterSpaceIPD.max - letterSpaceIPD.opt,
- letterSpaceIPD.opt - letterSpaceIPD.min,
- new LeafPosition(this, -1), false));
- }
- iReturnedIndex ++;
- } else {
- // ai refers to a space
- if (textArray[ai.iStartIndex] == NBSPACE) {
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1),
- false));
- }
- switch (alignment) {
- case EN_CENTER :
- returnList.add
- (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 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- returnList.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- iReturnedIndex ++;
- break;
- case EN_START : // fall through
- case EN_END :
- returnList.add
- (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,
- - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- iReturnedIndex ++;
- break;
- case EN_JUSTIFY:
- returnList.add
- (new KnuthGlue(wordSpaceIPD.opt,
- wordSpaceIPD.max - wordSpaceIPD.opt,
- wordSpaceIPD.opt - wordSpaceIPD.min,
- new LeafPosition(this, iReturnedIndex), false));
- iReturnedIndex ++;
- break;
-
- default:
- returnList.add
- (new KnuthGlue(wordSpaceIPD.opt,
- wordSpaceIPD.max - wordSpaceIPD.opt, 0,
- new LeafPosition(this, iReturnedIndex), false));
- iReturnedIndex ++;
- }
- }
- } // end of while
- setFinished(true);
- return returnList;
- }
-
- public void getWordChars(StringBuffer sbChars, Position pos) {
- int iLeafValue = ((LeafPosition) pos).getLeafPos();
- if (iLeafValue != -1) {
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(iLeafValue);
- sbChars.append(new String(textArray, ai.iStartIndex,
- ai.iBreakIndex - ai.iStartIndex));
- }
- }
-}
-
+++ /dev/null
-/*\r
- * Copyright 2005 The Apache Software Foundation.\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-package org.apache.fop.layoutmgr;\r
-\r
-import org.apache.fop.area.inline.InlineArea;\r
-import org.apache.fop.fo.flow.Wrapper;\r
-\r
-/**\r
- * This is the layout manager for the fo:wrapper formatting object.\r
- */\r
-public class WrapperLayoutManager extends LeafNodeLayoutManager {\r
- \r
- private Wrapper fobj;\r
-\r
- /**\r
- * Creates a new LM for fo:wrapper.\r
- * @param node the fo:wrapper\r
- */\r
- public WrapperLayoutManager(Wrapper node) {\r
- super(node);\r
- fobj = node;\r
- }\r
-\r
- /** @see org.apache.fop.layoutmgr.LeafNodeLayoutManager */\r
- public InlineArea get(LayoutContext context) {\r
- //Create a zero-width, zero-height dummy area so this node can \r
- //participate in the ID handling. Otherwise, addId() wouldn't \r
- //be called.\r
- InlineArea area = new InlineArea();\r
- return area;\r
- }\r
- \r
- /** @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#addId() */\r
- protected void addId() {\r
- getPSLM().addIDToPage(fobj.getId());\r
- }\r
- \r
-}\r
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+import org.apache.fop.fo.flow.BasicLink;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.LinkResolver;
+import org.apache.fop.area.PageViewport;
+
+/**
+ * LayoutManager for the fo:basic-link formatting object
+ */
+public class BasicLinkLayoutManager extends InlineLayoutManager {
+ private BasicLink fobj;
+
+ /**
+ * Create an fo:basic-link layout manager.
+ *
+ * @param node the formatting object that creates the area
+ */
+ public BasicLinkLayoutManager(BasicLink node) {
+ super(node);
+ fobj = node;
+ }
+
+ protected InlineParent createArea() {
+ InlineParent area = super.createArea();
+ setupBasicLinkArea(parentLM, area);
+ return area;
+ }
+
+ private void setupBasicLinkArea(LayoutManager parentLM,
+ InlineParent area) {
+ if (fobj.getExternalDestination() != null) {
+ area.addTrait(Trait.EXTERNAL_LINK, fobj.getExternalDestination());
+ } else {
+ String idref = fobj.getInternalDestination();
+ PageViewport page = getPSLM().getFirstPVWithID(idref);
+ if (page != null) {
+ area.addTrait(Trait.INTERNAL_LINK, page.getKey());
+ } else {
+ LinkResolver res = new LinkResolver(idref, area);
+ getPSLM().addUnresolvedArea(idref, res);
+ }
+ }
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.fo.flow.BidiOverride;
+
+
+/**
+ * If this bidi has a different writing mode direction
+ * ltr or rtl than its parent writing mode then this
+ * reverses the inline areas (at the character level).
+ */
+public class BidiLayoutManager extends LeafNodeLayoutManager {
+
+ private List children;
+
+ public BidiLayoutManager(BidiOverride node, InlineLayoutManager cLM) {
+ super(node);
+ children = new ArrayList();
+/*
+ for (int count = cLM.size() - 1; count >= 0; count--) {
+ InlineArea ia = cLM.get(count);
+ if (ia instanceof Word) {
+ // reverse word
+ Word word = (Word) ia;
+ StringBuffer sb = new StringBuffer(word.getWord());
+ word.setWord(sb.reverse().toString());
+ }
+ children.add(ia);
+ }
+*/
+ }
+
+ public int size() {
+ return children.size();
+ }
+
+ public InlineArea get(int index) {
+ return (InlineArea) children.get(index);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import org.apache.fop.fo.flow.Character;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LeafPosition;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager.AreaInfo;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.Trait;
+import org.apache.fop.traits.MinOptMax;
+import org.apache.fop.traits.SpaceVal;
+
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * LayoutManager for the fo:character formatting object
+ */
+public class CharacterLayoutManager extends LeafNodeLayoutManager {
+ private Character fobj;
+ private MinOptMax letterSpaceIPD;
+ private int hyphIPD;
+ private Font fs;
+
+ /**
+ * Constructor
+ *
+ * @param node the fo:character formatting object
+ * @todo better null checking of node
+ */
+ public CharacterLayoutManager(Character node) {
+ super(node);
+ fobj = node;
+ InlineArea inline = getCharacterInlineArea(node);
+ setCurrentArea(inline);
+ setAlignment(fobj.getVerticalAlign());
+ fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+
+ SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing());
+ letterSpaceIPD = ls.getSpace();
+ hyphIPD = fs.getCharWidth(fobj.getCommonHyphenation().hyphenationCharacter);
+ }
+
+ private InlineArea getCharacterInlineArea(Character node) {
+ org.apache.fop.area.inline.Character ch =
+ new org.apache.fop.area.inline.Character(node.getCharacter());
+ TraitSetter.addTextDecoration(ch, fobj.getTextDecoration());
+ return ch;
+ }
+
+ /**
+ * Offset this area.
+ * Offset the inline area in the bpd direction when adding the
+ * inline area.
+ * This is used for vertical alignment.
+ * Subclasses should override this if necessary.
+ * @param area the inline area to be updated
+ * @param context the layout context used for adding the area
+ */
+ protected void offsetArea(InlineArea area, LayoutContext context) {
+ int bpd = area.getBPD();
+ switch (verticalAlignment) {
+ case EN_MIDDLE:
+ area.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
+ break;
+ case EN_TOP:
+ area.setOffset(fs.getAscender());
+ break;
+ case EN_BOTTOM:
+ area.setOffset(context.getLineHeight() - bpd + fs.getAscender());
+ break;
+ case EN_BASELINE:
+ default:
+ area.setOffset(context.getBaseline());
+ break;
+ }
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context,
+ int alignment) {
+ MinOptMax ipd;
+ curArea = get(context);
+ LinkedList returnList = new LinkedList();
+
+ if (curArea == null) {
+ setFinished(true);
+ return null;
+ }
+
+ ipd = new MinOptMax(fs.getCharWidth(((org.apache.fop.area.inline.Character) curArea).getChar().charAt(0)));
+
+ curArea.setIPD(ipd.opt);
+ curArea.setBPD(fs.getAscender() - fs.getDescender());
+
+ // offset is set in the offsetArea() method
+ //curArea.setOffset(textInfo.fs.getAscender());
+ //curArea.setOffset(context.getBaseline());
+
+ curArea.addTrait(Trait.FONT_NAME, fs.getFontName());
+ curArea.addTrait(Trait.FONT_SIZE, new Integer(fs.getFontSize()));
+ curArea.addTrait(Trait.COLOR, fobj.getColor());
+
+ int bpd = curArea.getBPD();
+ int lead = 0;
+ int total = 0;
+ int middle = 0;
+ switch (verticalAlignment) {
+ case EN_MIDDLE : middle = bpd / 2 ;
+ break;
+ case EN_TOP : // fall through
+ case EN_BOTTOM : total = bpd;
+ break;
+ case EN_BASELINE: // fall through
+ default : lead = fs.getAscender();
+ total = bpd;
+ break;
+ }
+
+ // create the AreaInfo object to store the computed values
+ areaInfo = new AreaInfo((short) 0, ipd, false,
+ lead, total, middle);
+
+ // node is a fo:Character
+ if (letterSpaceIPD.min == letterSpaceIPD.max) {
+ // constant letter space, only return a box
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), false));
+ } else {
+ // adjustable letter space, return a sequence of elements;
+ // at the moment the character is supposed to have no letter spaces,
+ // but returning this sequence allows us to change only one element
+ // if addALetterSpaceTo() is called
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), false));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add(new KnuthGlue(0, 0, 0,
+ new LeafPosition(this, -1), true));
+ returnList.add(new KnuthInlineBox(0, 0, 0, 0,
+ new LeafPosition(this, -1), true));
+ }
+
+ setFinished(true);
+ return returnList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position bp) {
+ sbChars.append
+ (((org.apache.fop.area.inline.Character) curArea).getChar());
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ if (hc.getNextHyphPoint() == 1) {
+ // the character ends a syllable
+ areaInfo.bHyphenated = true;
+ bSomethingChanged = true;
+ } else {
+ // hc.getNextHyphPoint() returned -1 (no more hyphenation points)
+ // or a number > 1;
+ // the character does not end a syllable
+ }
+ hc.updateOffset(1);
+ }
+
+ public boolean applyChanges(List oldList) {
+ setFinished(false);
+ if (bSomethingChanged) {
+ // there is nothing to do,
+ // possible changes have already been applied
+ // in the hyphenate() method
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ int flaggedPenalty,
+ int alignment) {
+ if (isFinished()) {
+ return null;
+ }
+
+ LinkedList returnList = new LinkedList();
+
+ if (letterSpaceIPD.min == letterSpaceIPD.max
+ || areaInfo.iLScount == 0) {
+ // constant letter space, or no letter space
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), false));
+ if (areaInfo.bHyphenated) {
+ returnList.add
+ (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
+ new LeafPosition(this, -1), false));
+ }
+ } else {
+ // adjustable letter space
+ returnList.add
+ (new KnuthInlineBox(areaInfo.ipdArea.opt
+ - areaInfo.iLScount * letterSpaceIPD.opt,
+ areaInfo.lead, areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), false));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(areaInfo.iLScount * letterSpaceIPD.opt,
+ areaInfo.iLScount * letterSpaceIPD.max - letterSpaceIPD.opt,
+ areaInfo.iLScount * letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), true));
+ returnList.add(new KnuthInlineBox(0, 0, 0, 0,
+ new LeafPosition(this, -1), true));
+ if (areaInfo.bHyphenated) {
+ returnList.add
+ (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
+ new LeafPosition(this, -1), false));
+ }
+ }
+
+ setFinished(true);
+ return returnList;
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.pagination.Title;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.SpaceSpecifier;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.LineArea;
+import org.apache.fop.area.inline.InlineArea;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.ArrayList;
+import org.apache.fop.traits.MinOptMax;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Content Layout Manager.
+ * For use with objects that contain inline areas such as
+ * leader use-content and title.
+ */
+public class ContentLayoutManager implements InlineLevelLayoutManager {
+ private FOUserAgent userAgent;
+ private Area holder;
+ private int stackSize;
+ private LayoutManager parentLM;
+ private InlineLevelLayoutManager childLM = null;
+
+ /**
+ * logging instance
+ */
+ protected static Log log = LogFactory.getLog(LayoutManager.class);
+
+ /**
+ * Constructs a new ContentLayoutManager
+ *
+ * @param area The parent area
+ */
+ public ContentLayoutManager(Area area, LayoutManager parentLM) {
+ holder = area;
+ this.parentLM = parentLM;
+ }
+
+ /**
+ * Constructor using a fo:title formatting object and its
+ * PageSequenceLayoutManager parent.
+ */
+ public ContentLayoutManager(Title foTitle, PageSequenceLayoutManager pslm) {
+ // get breaks then add areas to title
+ this.parentLM = pslm;
+ holder = new LineArea();
+
+ setUserAgent(foTitle.getUserAgent());
+
+ // use special layout manager to add the inline areas
+ // to the Title.
+ InlineLayoutManager lm;
+ lm = new InlineLayoutManager(foTitle);
+ addChildLM(lm);
+ fillArea(lm);
+ }
+
+ public void fillArea(LayoutManager curLM) {
+
+ int ipd = 1000000;
+
+ LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA);
+ childLC.setLeadingSpace(new SpaceSpecifier(false));
+ childLC.setTrailingSpace(new SpaceSpecifier(false));
+ // set stackLimit for lines
+ childLC.setStackLimit(new MinOptMax(ipd));
+ childLC.setRefIPD(ipd);
+
+ int lineHeight = 14000;
+ int lead = 12000;
+ int follow = 2000;
+
+ 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;
+
+ stackSize = 0;
+
+ LinkedList contentList =
+ getNextKnuthElements(childLC, Constants.EN_START);
+ ListIterator contentIter = contentList.listIterator();
+ while (contentIter.hasNext()) {
+ KnuthElement element = (KnuthElement) contentIter.next();
+ if (element.isBox()) {
+ KnuthInlineBox box = (KnuthInlineBox) element;
+ if (box.getLead() > lineLead) {
+ lineLead = box.getLead();
+ }
+ if (box.getTotal() > maxtb) {
+ maxtb = box.getTotal();
+ }
+ // Is this needed? cf. LineLM.makeLineBreakPosition
+ // if (box.getMiddle() > lineLead) {
+ // lineLead = box.getMiddle();
+ // }
+ if (box.getMiddle() > middlefollow) {
+ middlefollow = box.getMiddle();
+ }
+ }
+ }
+
+ if (maxtb - lineLead > middlefollow) {
+ middlefollow = maxtb - lineLead;
+ }
+
+ LayoutContext lc = new LayoutContext(0);
+ lc.setBaseline(lineLead);
+ lc.setLineHeight(lineHeight);
+
+ lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
+ lc.setLeadingSpace(new SpaceSpecifier(false));
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ KnuthPossPosIter contentPosIter =
+ new KnuthPossPosIter(contentList, 0, contentList.size());
+ curLM.addAreas(contentPosIter, lc);
+ }
+
+ 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;
+ }
+
+ /** @see org.apache.fop.layoutmgr.LayoutManager */
+ public Area getParentArea(Area childArea) {
+ return holder;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
+ **/
+ public void addChildArea(Area childArea) {
+ holder.addChildArea(childArea);
+ }
+
+ /**
+ * Set the user agent.
+ *
+ * @param ua the user agent
+ */
+ public void setUserAgent(FOUserAgent ua) {
+ userAgent = ua;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getUserAgent()
+ */
+ public FOUserAgent getUserAgent() {
+ return userAgent;
+ }
+
+ /** @see org.apache.fop.layoutmgr.LayoutManager */
+ public void setParent(LayoutManager lm) {
+ parentLM = lm;
+ }
+
+ public LayoutManager getParent() {
+ return this.parentLM;
+ }
+
+ /** @see org.apache.fop.layoutmgr.LayoutManager */
+ public boolean isFinished() {
+ return false;
+ }
+
+ /** @see org.apache.fop.layoutmgr.LayoutManager */
+ public void setFinished(boolean isFinished) {
+ //to be done
+ }
+
+ /** @see org.apache.fop.layoutmgr.LayoutManager */
+ public void resetPosition(Position position) {
+ //to be done
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#createNextChildLMs
+ */
+ public boolean createNextChildLMs(int pos) {
+ return false;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs
+ */
+ public List getChildLMs() {
+ List childLMs = new ArrayList(1);
+ childLMs.add(childLM);
+ return childLMs;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#addChildLM
+ */
+ public void addChildLM(LayoutManager lm) {
+ if (lm == null) {
+ return;
+ }
+ lm.setParent(this);
+ childLM = (InlineLevelLayoutManager)lm;
+ log.trace(this.getClass().getName()
+ + ": Adding child LM " + lm.getClass().getName());
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#addChildLMs
+ */
+ public void addChildLMs(List newLMs) {
+ if (newLMs == null || newLMs.size() == 0) {
+ return;
+ }
+ ListIterator iter = newLMs.listIterator();
+ while (iter.hasNext()) {
+ LayoutManager lm = (LayoutManager) iter.next();
+ addChildLM(lm);
+ }
+ }
+
+ 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 contentList;
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ return oldList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ }
+
+ public boolean applyChanges(List oldList) {
+ return false;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ /*int flaggedPenalty,*/
+ int alignment) {
+ return null;
+ }
+
+ public PageSequenceLayoutManager getPSLM() {
+ return parentLM.getPSLM();
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+// Java
+import java.awt.geom.Rectangle2D;
+
+// FOP
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.Viewport;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.flow.ExternalGraphic;
+import org.apache.fop.layoutmgr.TraitSetter;
+
+/**
+ * LayoutManager for the fo:external-graphic formatting object
+ */
+public class ExternalGraphicLayoutManager extends LeafNodeLayoutManager {
+
+ private ExternalGraphic fobj;
+
+ private int breakAfter;
+ private int breakBefore;
+ private int align;
+ private int startIndent;
+ private int endIndent;
+ private int spaceBefore;
+ private int spaceAfter;
+ private int viewWidth = -1;
+ private int viewHeight = -1;
+ private boolean clip = false;
+ private Rectangle2D placement = null;
+
+ /**
+ * Constructor
+ *
+ * @param node the fo:external-graphic formatting object that creates the area
+ */
+ public ExternalGraphicLayoutManager(ExternalGraphic node) {
+ super(node);
+ fobj = node;
+ setup();
+ InlineArea area = getExternalGraphicInlineArea();
+ setCurrentArea(area);
+ setAlignment(fobj.getVerticalAlign());
+ setLead(viewHeight);
+ }
+
+ /**
+ * Setup this image.
+ * This gets the sizes for the image and the dimensions and clipping.
+ * @todo see if can simplify property handling logic
+ */
+ private void setup() {
+ // assume lr-tb for now and just use the .optimum value of the range
+ Length ipd = fobj.getInlineProgressionDimension().getOptimum().getLength();
+ if (ipd.getEnum() != EN_AUTO) {
+ viewWidth = ipd.getValue();
+ } else {
+ ipd = fobj.getWidth();
+ if (ipd.getEnum() != EN_AUTO) {
+ viewWidth = ipd.getValue();
+ }
+ }
+ Length bpd = fobj.getBlockProgressionDimension().getOptimum().getLength();
+ if (bpd.getEnum() != EN_AUTO) {
+ viewHeight = bpd.getValue();
+ } else {
+ bpd = fobj.getHeight();
+ if (bpd.getEnum() != EN_AUTO) {
+ viewHeight = bpd.getValue();
+ }
+ }
+
+ int cwidth = -1;
+ int cheight = -1;
+ Length ch = fobj.getContentHeight();
+ if (ch.getEnum() != EN_AUTO) {
+ if (ch.getEnum() == EN_SCALE_TO_FIT) {
+ if (viewHeight != -1) {
+ cheight = viewHeight;
+ }
+ } else {
+ cheight = ch.getValue();
+ }
+ }
+ Length cw = fobj.getContentWidth();
+ if (cw.getEnum() != EN_AUTO) {
+ if (cw.getEnum() == EN_SCALE_TO_FIT) {
+ if (viewWidth != -1) {
+ cwidth = viewWidth;
+ }
+ } else {
+ cwidth = cw.getValue();
+ }
+ }
+
+ int scaling = fobj.getScaling();
+ if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
+ if (cwidth == -1 && cheight == -1) {
+ cwidth = fobj.getIntrinsicWidth();
+ cheight = fobj.getIntrinsicHeight();
+ } else if (cwidth == -1) {
+ cwidth = (int)(fobj.getIntrinsicWidth() * (double)cheight
+ / fobj.getIntrinsicHeight());
+ } else if (cheight == -1) {
+ cheight = (int)(fobj.getIntrinsicHeight() * (double)cwidth
+ / fobj.getIntrinsicWidth());
+ } else {
+ // adjust the larger
+ double rat1 = cwidth / fobj.getIntrinsicWidth();
+ double rat2 = cheight / fobj.getIntrinsicHeight();
+ if (rat1 < rat2) {
+ // reduce cheight
+ cheight = (int)(rat1 * fobj.getIntrinsicHeight());
+ } else if (rat1 > rat2) {
+ cwidth = (int)(rat2 * fobj.getIntrinsicWidth());
+ }
+ }
+ }
+
+ if (viewWidth == -1) {
+ viewWidth = cwidth;
+ }
+ if (viewHeight == -1) {
+ viewHeight = cheight;
+ }
+
+ if (cwidth > viewWidth || cheight > viewHeight) {
+ int overflow = fobj.getOverflow();
+ if (overflow == EN_HIDDEN) {
+ clip = true;
+ } else if (overflow == EN_ERROR_IF_OVERFLOW) {
+ fobj.getLogger().error("Image: " + fobj.getURL()
+ + " overflows the viewport, clipping to viewport");
+ clip = true;
+ }
+ }
+
+ int xoffset = 0;
+ int yoffset = 0;
+ switch(fobj.getDisplayAlign()) {
+ case EN_BEFORE:
+ break;
+ case EN_AFTER:
+ yoffset = viewHeight - cheight;
+ break;
+ case EN_CENTER:
+ yoffset = (viewHeight - cheight) / 2;
+ break;
+ case EN_AUTO:
+ default:
+ break;
+ }
+
+ switch(fobj.getTextAlign()) {
+ case EN_CENTER:
+ xoffset = (viewWidth - cwidth) / 2;
+ break;
+ case EN_END:
+ xoffset = viewWidth - cwidth;
+ break;
+ case EN_START:
+ break;
+ case EN_JUSTIFY:
+ default:
+ break;
+ }
+ placement = new Rectangle2D.Float(xoffset, yoffset, cwidth, cheight);
+ }
+
+ /**
+ * Get the inline area for this external grpahic.
+ * This creates the image area and puts it inside a viewport.
+ *
+ * @return the viewport containing the image area
+ */
+ public InlineArea getExternalGraphicInlineArea() {
+ Image imArea = new Image(fobj.getSrc());
+ Viewport vp = new Viewport(imArea);
+ vp.setIPD(viewWidth);
+ vp.setBPD(viewHeight);
+ vp.setClip(clip);
+ vp.setContentPosition(placement);
+ vp.setOffset(0);
+
+ // Common Border, Padding, and Background Properties
+ TraitSetter.addBorders(vp, fobj.getCommonBorderPaddingBackground());
+ TraitSetter.addBackground(vp, fobj.getCommonBorderPaddingBackground());
+
+ return vp;
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.fop.fo.flow.Footnote;
+import org.apache.fop.layoutmgr.AbstractLayoutManager;
+import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.Position;
+
+public class FootnoteLayoutManager extends AbstractLayoutManager
+ implements InlineLevelLayoutManager {
+
+ private Footnote footnote;
+ private InlineStackingLayoutManager citationLM;
+ private FootnoteBodyLayoutManager bodyLM;
+
+ /**
+ * Create a new footnote layout manager.
+ * @param node footnote to create the layout manager for
+ */
+ public FootnoteLayoutManager(Footnote node) {
+ super(node);
+ footnote = node;
+
+ // create an InlineStackingLM handling the fo:inline child of fo:footnote
+ citationLM = new InlineStackingLayoutManager(footnote.getFootnoteCitation());
+
+ // create a FootnoteBodyLM handling the fo:footnote-body child of fo:footnote
+ bodyLM = new FootnoteBodyLayoutManager(footnote.getFootnoteBody());
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context,
+ int alignment) {
+ // this is the only method that must be implemented:
+ // all other methods will never be called, as the returned elements
+ // contain Positions created by the citationLM, so its methods will
+ // be called instead
+
+ // set the citationLM parent to be this LM's parent
+ citationLM.setParent(getParent());
+ bodyLM.setParent(this);
+
+ // get Knuth elements representing the footnote citation
+ LinkedList returnedList = new LinkedList();
+ while (!citationLM.isFinished()) {
+ LinkedList partialList = citationLM.getNextKnuthElements(context, alignment);
+ if (partialList != null) {
+ returnedList.addAll(partialList);
+ }
+ }
+ if (returnedList.size() == 0) {
+ //Inline part of the footnote is empty. Need to send back an auxiliary
+ //zero-width, zero-height inline box so the footnote gets painted.
+ returnedList.add(new KnuthInlineBox(0, 0, 0, 0, null, true));
+ }
+ setFinished(true);
+
+ addAnchor(returnedList);
+
+ return returnedList;
+ }
+
+ private void addAnchor(LinkedList citationList) {
+ // find the last box in the sequence, and add a reference
+ // to the FootnoteBodyLM
+ ListIterator citationIterator = citationList.listIterator(citationList.size());
+ KnuthInlineBox lastBox = null;
+ while (citationIterator.hasPrevious() && lastBox == null) {
+ KnuthElement element = (KnuthElement) citationIterator.previous();
+ if (element instanceof KnuthInlineBox) {
+ lastBox = (KnuthInlineBox) element;
+ }
+ }
+ if (lastBox != null) {
+ lastBox.setFootnoteBodyLM(bodyLM);
+ }
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ log.warn("null implementation of addALetterSpaceTo() called!");
+ return oldList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ log.warn("null implementation of getWordChars() called!");
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ log.warn("null implementation of hyphenate called!");
+ }
+
+ public boolean applyChanges(List oldList) {
+ log.warn("null implementation of applyChanges() called!");
+ return false;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ int alignment) {
+ log.warn("null implementation of getChangeKnuthElement() called!");
+ return null;
+ }
+}
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+/**
+ * This class is used to pass information to the getNextBreakPoss()
+ * method concerning hyphenation. A reference to an instance of the
+ * class is contained in the LayoutContext object passed to each
+ * LayoutManager. It contains information concerning the hyphenation
+ * points in a word and the how many of those have previously been
+ * processed by a Layout Manager to generate size information.
+ */
+public class HyphContext {
+ private int[] hyphPoints;
+ private int currentOffset = 0;
+ private int currentIndex = 0;
+
+ public HyphContext(int[] hyphPoints) {
+ this.hyphPoints = hyphPoints;
+ }
+
+ public int getNextHyphPoint() {
+ for (; currentIndex < hyphPoints.length; currentIndex++) {
+ if (hyphPoints[currentIndex] > currentOffset) {
+ return (hyphPoints[currentIndex] - currentOffset);
+ }
+ }
+ return -1; // AT END!
+ }
+
+ public boolean hasMoreHyphPoints() {
+ for (; currentIndex < hyphPoints.length; currentIndex++) {
+ if (hyphPoints[currentIndex] > currentOffset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void updateOffset(int iCharsProcessed) {
+ currentOffset += iCharsProcessed;
+ }
+}
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+// Java
+import java.util.List;
+
+// FOP
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.fo.flow.InlineContainer;
+/**
+ * This creates a single inline container area after
+ * laying out the child block areas. All footnotes, floats
+ * and id areas are maintained for later retrieval.
+ */
+public class ICLayoutManager extends LeafNodeLayoutManager {
+ private InlineContainer fobj;
+ private List childrenLM;
+
+ public ICLayoutManager(InlineContainer node, List childLM) {
+ super(node);
+ fobj = node;
+ childrenLM = childLM;
+ }
+
+ public InlineArea get(int index) {
+ return null;
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
--- /dev/null
+/*
+ * Copyright 1999-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.inline;
+
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+import org.apache.fop.fo.flow.InlineLevel;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonMarginInline;
+import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.NonLeafPosition;
+import org.apache.fop.layoutmgr.SpaceSpecifier;
+import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.traits.MinOptMax;
+import org.apache.fop.traits.SpaceVal;
+
+/**
+ * LayoutManager for objects which stack children in the inline direction,
+ * such as Inline or Line
+ */
+public class InlineLayoutManager extends InlineStackingLayoutManager
+ implements InlineLevelLayoutManager {
+ private InlineLevel fobj;
+
+ private CommonMarginInline inlineProps = null;
+ private CommonBorderPaddingBackground borderProps = null;
+
+ /**
+ * 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
+ */
+ // The node should be FObjMixed
+ public InlineLayoutManager(InlineLevel node) {
+ super(node);
+ fobj = node;
+ initialize();
+ }
+
+ private void initialize() {
+ inlineProps = fobj.getCommonMarginInline();
+ borderProps = fobj.getCommonBorderPaddingBackground();
+
+ int iPad = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false);
+ iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
+ false);
+ iPad += borderProps.getPadding(CommonBorderPaddingBackground.AFTER, false);
+ iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.AFTER, false);
+ extraBPD = new MinOptMax(iPad);
+ }
+
+ protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
+ int iBP = borderProps.getPadding(CommonBorderPaddingBackground.START,
+ bNotFirst);
+ iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.START,
+ bNotFirst);
+ iBP += borderProps.getPadding(CommonBorderPaddingBackground.END, bNotLast);
+ iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, bNotLast);
+ return new MinOptMax(iBP);
+ }
+
+
+ protected boolean hasLeadingFence(boolean bNotFirst) {
+ int iBP = borderProps.getPadding(CommonBorderPaddingBackground.START,
+ bNotFirst);
+ iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.START,
+ bNotFirst);
+ return (iBP > 0);
+ }
+
+ protected boolean hasTrailingFence(boolean bNotLast) {
+ int iBP = borderProps.getPadding(CommonBorderPaddingBackground.END, bNotLast);
+ iBP += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, bNotLast);
+ return (iBP > 0);
+ }
+
+ protected SpaceProperty getSpaceStart() {
+ return inlineProps.spaceStart;
+ }
+ protected SpaceProperty getSpaceEnd() {
+ return inlineProps.spaceEnd;
+ }
+
+ protected void setTraits(boolean bNotFirst, boolean bNotLast) {
+
+ // Add border and padding to current area and set flags (FIRST, LAST ...)
+ TraitSetter.setBorderPaddingTraits(getCurrentArea(),
+ borderProps, bNotFirst, bNotLast);
+
+ if (borderProps != null) {
+ TraitSetter.addBorders(getCurrentArea(), borderProps);
+ TraitSetter.addBackground(getCurrentArea(), borderProps);
+ }
+ }
+
+ 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-2005 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.inline;
+
+import java.util.List;
+
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.Position;
+
+/**
+ * The interface for LayoutManagers which generate inline areas
+ */
+public interface InlineLevelLayoutManager extends LayoutManager {
+
+ /**
+ * Tell the LM to modify its data, adding a letter space
+ * to the word fragment represented by the given elements,
+ * and returning the corrected elements
+ *
+ * @param oldList the elements which must be given one more letter space
+ * @return the new elements replacing the old ones
+ */
+ List addALetterSpaceTo(List oldList);
+
+ /**
+ * 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);
+
+}
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.HashMap;
+
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.layoutmgr.AbstractLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+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.traits.SpaceVal;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.area.inline.Space;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Class modelling the commonalities of layoutmanagers for objects
+ * which stack children in the inline direction, such as Inline or
+ * Line. It should not be instantiated directly.
+ */
+public class InlineStackingLayoutManager extends AbstractLayoutManager
+ implements InlineLevelLayoutManager {
+
+
+ private static class StackingIter extends PositionIterator {
+
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+
+
+ /**
+ * Size of any start or end borders and padding.
+ */
+ private MinOptMax allocIPD = new MinOptMax(0);
+
+ /**
+ * Size of border and padding in BPD (ie, before and after).
+ */
+ protected MinOptMax extraBPD;
+
+ private Area currentArea; // LineArea or InlineParent
+
+ //private BreakPoss prevBP;
+ protected LayoutContext childLC;
+
+ private LayoutManager lastChildLM = null; // Set when return last breakposs
+ private boolean bAreaCreated = false;
+
+ //private LayoutManager currentLM = null;
+
+ /** Used to store previous content IPD for each child LM. */
+ private HashMap hmPrevIPD = new HashMap();
+
+ /**
+ * Create an inline stacking layout manager.
+ * This is used for fo's that create areas that
+ * contain inline areas.
+ *
+ * @param node the formatting object that creates the area
+ */
+ protected InlineStackingLayoutManager(FObj node) {
+ super(node);
+ extraBPD = new MinOptMax(0);
+ }
+
+ /**
+ * Set the iterator.
+ *
+ * @param iter the iterator for this LM
+ */
+ public void setLMiter(ListIterator iter) {
+ childLMiter = iter;
+ }
+
+ protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
+ return new MinOptMax(0);
+ }
+
+
+ protected boolean hasLeadingFence(boolean bNotFirst) {
+ return false;
+ }
+
+ protected boolean hasTrailingFence(boolean bNotLast) {
+ return false;
+ }
+
+ protected SpaceProperty getSpaceStart() {
+ return null;
+ }
+
+ protected SpaceProperty getSpaceEnd() {
+ return null;
+ }
+
+ /**
+ * Reset position for returning next BreakPossibility.
+ * @param prevPos a Position returned by this layout manager
+ * representing a potential break decision.
+ */
+ public void resetPosition(Position prevPos) {
+ if (prevPos != null) {
+ // ASSERT (prevPos.getLM() == this)
+ if (prevPos.getLM() != this) {
+ //getLogger().error(
+ // "InlineStackingLayoutManager.resetPosition: " +
+ // "LM mismatch!!!");
+ }
+ // Back up the child LM Position
+ Position childPos = prevPos.getPosition();
+ reset(childPos);
+ /*
+ if (prevBP != null
+ && prevBP.getLayoutManager() != childPos.getLM()) {
+ childLC = null;
+ }
+ prevBP = new BreakPoss(childPos);
+ */
+ } else {
+ // Backup to start of first child layout manager
+ //prevBP = null;
+ // super.resetPosition(prevPos);
+ reset(prevPos);
+ // If any areas created, we are restarting!
+ bAreaCreated = false;
+ }
+ // Do we need to reset some context like pending or prevContent?
+ // What about prevBP?
+ }
+
+ protected MinOptMax getPrevIPD(LayoutManager lm) {
+ return (MinOptMax) hmPrevIPD.get(lm);
+ }
+
+ /**
+ * Clear the previous IPD calculation.
+ */
+ protected void clearPrevIPD() {
+ hmPrevIPD.clear();
+ }
+
+ protected InlineParent createArea() {
+ return new InlineParent();
+ }
+
+ /**
+ * Generate and add areas to parent area.
+ * Set size of each area. This should only create and return one
+ * inline area for any inline parent area.
+ *
+ * @param parentIter Iterator over Position information returned
+ * by this LayoutManager.
+ * @param dSpaceAdjust Factor controlling how much extra space to add
+ * in order to justify the line.
+ */
+ public void addAreas(PositionIterator parentIter,
+ LayoutContext context) {
+ InlineParent parent = createArea();
+ parent.setBPD(context.getLineHeight());
+ parent.setOffset(0);
+ setCurrentArea(parent);
+
+ setChildContext(new LayoutContext(context)); // Store current value
+
+ // If has fence, make a new leadingSS
+ /* How to know if first area created by this LM? Keep a count and
+ * reset it if getNextBreakPoss() is called again.
+ */
+ if (hasLeadingFence(bAreaCreated)) {
+ 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()));
+ }
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ // also set lastLM to be the LayoutManager which created
+ // the last Position: if the LAST_AREA flag is set in context,
+ // it must be also set in the LayoutContext given to lastLM,
+ // but unset in the LayoutContext given to the other LMs
+ LinkedList positionList = new LinkedList();
+ NonLeafPosition pos;
+ LayoutManager lastLM = null; // last child LM in this iterator
+ while (parentIter.hasNext()) {
+ pos = (NonLeafPosition) parentIter.next();
+ lastLM = pos.getPosition().getLM();
+ positionList.add(pos.getPosition());
+ }
+
+ StackingIter childPosIter
+ = new StackingIter(positionList.listIterator());
+
+ LayoutManager prevLM = null;
+ InlineLevelLayoutManager childLM;
+ while ((childLM = (InlineLevelLayoutManager) 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;
+ }
+
+ /* If has trailing fence,
+ * resolve trailing space specs from descendants.
+ * Otherwise, propagate any trailing space specs to parent LM via
+ * the context object.
+ * If the last child LM called return ISLAST in the context object
+ * and it is the last child LM for this LM, then this must be
+ * the last area for the current LM also.
+ */
+ boolean bIsLast =
+ (getContext().isLastArea() && prevLM == lastChildLM);
+ if (hasTrailingFence(bIsLast)) {
+ addSpace(getCurrentArea(),
+ getContext().getTrailingSpace().resolve(false),
+ getContext().getSpaceAdjust());
+ context.setTrailingSpace(new SpaceSpecifier(false));
+ } else {
+ // Propagate trailing space-spec sequence to parent LM in context
+ context.setTrailingSpace(getContext().getTrailingSpace());
+ }
+ // Add own trailing space to parent context (or set on area?)
+ if (context.getTrailingSpace() != null && getSpaceEnd() != null) {
+ context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd()));
+ }
+ setTraits(bAreaCreated, !bIsLast);
+
+ parentLM.addChildArea(getCurrentArea());
+ context.setFlags(LayoutContext.LAST_AREA, bIsLast);
+ bAreaCreated = true;
+ }
+
+ protected Area getCurrentArea() {
+ return currentArea;
+ }
+
+ protected void setCurrentArea(Area area) {
+ currentArea = area;
+ }
+
+ protected void setTraits(boolean bNotFirst, boolean bNotLast) {
+
+ }
+
+ public void addChildArea(Area childArea) {
+ // Make sure childArea is inline area
+ if (childArea instanceof InlineArea) {
+ Area parent = getCurrentArea();
+ if (getContext().resolveLeadingSpace()) {
+ addSpace(parent,
+ getContext().getLeadingSpace().resolve(false),
+ getContext().getSpaceAdjust());
+ }
+ parent.addChildArea(childArea);
+ }
+ }
+
+ protected void setChildContext(LayoutContext lc) {
+ childLC = lc;
+ }
+
+ // Current child layout context
+ protected LayoutContext getContext() {
+ return childLC;
+ }
+
+ protected void addSpace(Area parentArea, MinOptMax spaceRange,
+ double dSpaceAdjust) {
+ if (spaceRange != null) {
+ int iAdjust = spaceRange.opt;
+ if (dSpaceAdjust > 0.0) {
+ // Stretch by factor
+ iAdjust += (int) ((double) (spaceRange.max
+ - spaceRange.opt) * dSpaceAdjust);
+ } else if (dSpaceAdjust < 0.0) {
+ // Shrink by factor
+ iAdjust += (int) ((double) (spaceRange.opt
+ - spaceRange.min) * dSpaceAdjust);
+ }
+ if (iAdjust != 0) {
+ //getLogger().debug("Add leading space: " + iAdjust);
+ Space ls = new Space();
+ ls.setIPD(iAdjust);
+ parentArea.addChildArea(ls);
+ }
+ }
+ }
+
+ 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);
+ 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);
+ }
+ setFinished(curLM.isFinished() && (getChildLM() == null));
+ 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 List addALetterSpaceTo(List oldList) {
+ // old list contains only a box, or the sequence: box penalty glue box
+
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement element = null;
+ // "unwrap" the Position stored in each element of oldList
+ while (oldListIterator.hasNext()) {
+ element = (KnuthElement) oldListIterator.next();
+ element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
+ }
+
+ oldList = ((InlineLevelLayoutManager)
+ element.getLayoutManager()).addALetterSpaceTo(oldList);
+
+ // "wrap" againg the Position stored in each element of oldList
+ oldListIterator = oldList.listIterator();
+ while (oldListIterator.hasNext()) {
+ element = (KnuthElement) oldListIterator.next();
+ element.setPosition(new NonLeafPosition(this, element.getPosition()));
+ }
+
+ return oldList;
+ }
+
+ 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 1999-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.inline;
+
+// Java
+import java.awt.geom.Rectangle2D;
+
+// FOP
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.XMLObj;
+import org.apache.fop.fo.flow.InstreamForeignObject;
+import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.area.inline.ForeignObject;
+import org.apache.fop.area.inline.Viewport;
+
+/**
+ * LayoutManager for the fo:instream-foreign-object formatting object
+ */
+public class InstreamForeignObjectLM extends LeafNodeLayoutManager {
+
+ private InstreamForeignObject fobj;
+
+ /**
+ * Constructor
+ * @param node the formatting object that creates this area
+ */
+ public InstreamForeignObjectLM(InstreamForeignObject node) {
+ super(node);
+ fobj = node;
+ Viewport areaCurrent = getInlineArea();
+ setCurrentArea(areaCurrent);
+ setAlignment(node.getVerticalAlign());
+ setLead(areaCurrent.getBPD());
+ }
+
+ /**
+ * Get the inline area created by this element.
+ *
+ * @return the viewport inline area
+ */
+ private Viewport getInlineArea() {
+ XMLObj child = (XMLObj) fobj.childNodes.get(0);
+
+ // viewport size is determined by block-progression-dimension
+ // and inline-progression-dimension
+
+ // if replaced then use height then ignore block-progression-dimension
+ //int h = this.propertyList.get("height").getLength().mvalue();
+
+ // use specified line-height then ignore dimension in height direction
+ boolean hasLH = false; //propertyList.get("line-height").getSpecifiedValue() != null;
+
+ Length len;
+
+ int bpd = -1;
+ int ipd = -1;
+ boolean bpdauto = false;
+ if (hasLH) {
+ bpd = fobj.getLineHeight().getValue();
+ } else {
+ // this property does not apply when the line-height applies
+ // isn't the block-progression-dimension always in the same
+ // direction as the line height?
+ len = fobj.getBlockProgressionDimension().getOptimum().getLength();
+ if (len.getEnum() != EN_AUTO) {
+ bpd = len.getValue();
+ } else {
+ len = fobj.getHeight();
+ if (len.getEnum() != EN_AUTO) {
+ bpd = len.getValue();
+ }
+ }
+ }
+
+ len = fobj.getInlineProgressionDimension().getOptimum().getLength();
+ if (len.getEnum() != EN_AUTO) {
+ ipd = len.getValue();
+ } else {
+ len = fobj.getWidth();
+ if (len.getEnum() != EN_AUTO) {
+ ipd = len.getValue();
+ }
+ }
+
+ // if auto then use the intrinsic size of the content scaled
+ // to the content-height and content-width
+ int cwidth = -1;
+ int cheight = -1;
+ len = fobj.getContentWidth();
+ if (len.getEnum() != EN_AUTO) {
+ if (len.getEnum() == EN_SCALE_TO_FIT) {
+ if (ipd != -1) {
+ cwidth = ipd;
+ }
+ } else {
+ cwidth = len.getValue();
+ }
+ }
+ len = fobj.getContentHeight();
+ if (len.getEnum() != EN_AUTO) {
+ if (len.getEnum() == EN_SCALE_TO_FIT) {
+ if (bpd != -1) {
+ cwidth = bpd;
+ }
+ } else {
+ cheight = len.getValue();
+ }
+ }
+
+ int scaling = fobj.getScaling();
+ if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
+ if (cwidth == -1 && cheight == -1) {
+ cwidth = fobj.getIntrinsicWidth();
+ cheight = fobj.getIntrinsicHeight();
+ } else if (cwidth == -1) {
+ cwidth = (int)(fobj.getIntrinsicWidth() * (double)cheight
+ / fobj.getIntrinsicHeight());
+ } else if (cheight == -1) {
+ cheight = (int)(fobj.getIntrinsicHeight() * (double)cwidth
+ / fobj.getIntrinsicWidth());
+ } else {
+ // adjust the larger
+ double rat1 = cwidth / fobj.getIntrinsicWidth();
+ double rat2 = cheight / fobj.getIntrinsicHeight();
+ if (rat1 < rat2) {
+ // reduce cheight
+ cheight = (int)(rat1 * fobj.getIntrinsicHeight());
+ } else if (rat1 > rat2) {
+ cwidth = (int)(rat2 * fobj.getIntrinsicWidth());
+ }
+ }
+ }
+
+ if (ipd == -1) {
+ ipd = cwidth;
+ }
+ if (bpd == -1) {
+ bpd = cheight;
+ }
+
+ boolean clip = false;
+ if (cwidth > ipd || cheight > bpd) {
+ int overflow = fobj.getOverflow();
+ if (overflow == EN_HIDDEN) {
+ clip = true;
+ } else if (overflow == EN_ERROR_IF_OVERFLOW) {
+ fobj.getLogger().error("Instream foreign object overflows the viewport: clipping");
+ clip = true;
+ }
+ }
+
+ int xoffset = fobj.computeXOffset(ipd, cwidth);
+ int yoffset = fobj.computeYOffset(bpd, cheight);
+
+ Rectangle2D placement = new Rectangle2D.Float(xoffset, yoffset, cwidth, cheight);
+
+ org.w3c.dom.Document doc = child.getDOMDocument();
+ String ns = child.getDocumentNamespace();
+
+ //fobj.childNodes = null; This is bad for i-f-o in static-content!!!!!
+ ForeignObject foreign = new ForeignObject(doc, ns);
+
+ Viewport vp = new Viewport(foreign);
+ vp.setIPD(ipd);
+ vp.setBPD(bpd);
+ vp.setContentPosition(placement);
+ vp.setClip(clip);
+ vp.setOffset(0);
+
+ // Common Border, Padding, and Background Properties
+ TraitSetter.addBorders(vp, fobj.getCommonBorderPaddingBackground());
+ TraitSetter.addBackground(vp, fobj.getCommonBorderPaddingBackground());
+
+ return vp;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager#addId()
+ */
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 2004-2005 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.inline;
+
+import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.Position;
+
+public class KnuthInlineBox extends KnuthBox {
+
+ private int lead;
+ private int total;
+ private int middle;
+ private FootnoteBodyLayoutManager footnoteBodyLM = null;
+
+ /**
+ * Create a new KnuthBox.
+ *
+ * @param w the width of this box
+ * @param l the height of this box above the main baseline
+ * @param t the total height of this box
+ * @param m the height of this box above and below the middle baseline
+ * @param pos the Position stored in this box
+ * @param bAux is this box auxiliary?
+ */
+ public KnuthInlineBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+ super(w, pos, bAux);
+ lead = l;
+ total = t;
+ middle = m;
+ }
+
+ /**
+ * @return the height of this box above the main baseline.
+ */
+ public int getLead() {
+ return lead;
+ }
+
+ /**
+ * @return the total height of this box.
+ */
+ public int getTotal() {
+ return total;
+ }
+
+ /**
+ * @return the height of this box above and below the middle baseline.
+ */
+ public int getMiddle() {
+ return middle;
+ }
+
+ /**
+ * @param fblm the FootnoteBodyLM this box must hold a reference to
+ */
+ public void setFootnoteBodyLM(FootnoteBodyLayoutManager fblm) {
+ footnoteBodyLM = fblm;
+ }
+
+ /**
+ * @return the FootnoteBodyLM this box holds a reference to
+ */
+ public FootnoteBodyLayoutManager getFootnoteBodyLM() {
+ return footnoteBodyLM;
+ }
+
+ /**
+ * @return true if this box holds a reference to a FootnoteBodyLM
+ */
+ public boolean isAnchor() {
+ return (footnoteBodyLM != null);
+ }
+
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(super.toString());
+ sb.append(" lead=").append(lead);
+ sb.append(" total=").append(total);
+ sb.append(" middle=").append(middle);
+ return sb.toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+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.datatypes.PercentBase;
+import org.apache.fop.fo.flow.Leader;
+import org.apache.fop.fonts.Font;
+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.LayoutContext;
+import org.apache.fop.layoutmgr.LeafPosition;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.traits.MinOptMax;
+
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * LayoutManager for the fo:leader formatting object
+ */
+public class LeaderLayoutManager extends LeafNodeLayoutManager {
+ private Leader fobj;
+ Font font = null;
+
+ private LinkedList contentList = null;
+ private ContentLayoutManager clm = null;
+
+ /**
+ * Constructor
+ *
+ * @param node the formatting object that creates this area
+ * @todo better null checking of font object
+ */
+ public LeaderLayoutManager(Leader node) {
+ super(node);
+ fobj = node;
+ font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+ // 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) {
+ return getLeaderInlineArea();
+ }
+
+ protected MinOptMax getAllocationIPD(int refIPD) {
+ return getLeaderAllocIPD(refIPD);
+ }
+
+ private MinOptMax getLeaderAllocIPD(int ipd) {
+ // length of the leader
+ fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd);
+ int opt = fobj.getLeaderLength().getOptimum().getLength().getValue();
+ int min = fobj.getLeaderLength().getMinimum().getLength().getValue();
+ int max = fobj.getLeaderLength().getMaximum().getLength().getValue();
+ return new MinOptMax(min, opt, max);
+ }
+
+ private InlineArea getLeaderInlineArea() {
+ InlineArea leaderArea = null;
+
+ if (fobj.getLeaderPattern() == EN_RULE) {
+ org.apache.fop.area.inline.Leader leader =
+ new org.apache.fop.area.inline.Leader();
+ leader.setRuleStyle(fobj.getRuleStyle());
+ leader.setRuleThickness(fobj.getRuleThickness().getValue());
+ leaderArea = leader;
+ } else if (fobj.getLeaderPattern() == EN_SPACE) {
+ leaderArea = new Space();
+ } else if (fobj.getLeaderPattern() == EN_DOTS) {
+ TextArea t = new TextArea();
+ 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()));
+ int width = font.getCharWidth(dot);
+ Space spacer = null;
+ if (fobj.getLeaderPatternWidth().getValue() > width) {
+ spacer = new Space();
+ spacer.setIPD(fobj.getLeaderPatternWidth().getValue() - width);
+ width = fobj.getLeaderPatternWidth().getValue();
+ }
+ FilledArea fa = new FilledArea();
+ fa.setUnitWidth(width);
+ fa.addChildArea(t);
+ if (spacer != null) {
+ fa.addChildArea(spacer);
+ }
+ fa.setBPD(font.getAscender());
+
+ leaderArea = fa;
+ } else if (fobj.getLeaderPattern() == EN_USECONTENT) {
+ if (fobj.getChildNodes() == null) {
+ fobj.getLogger().error("Leader use-content with no content");
+ 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);
+ clm.setUserAgent(fobj.getUserAgent());
+ addChildLM(clm);
+
+ InlineLayoutManager lm;
+ lm = new InlineLayoutManager(fobj);
+ clm.addChildLM(lm);
+
+ contentList = clm.getNextKnuthElements(new LayoutContext(0), 0);
+ int width = clm.getStackingSize();
+ Space spacer = null;
+ if (fobj.getLeaderPatternWidth().getValue() > width) {
+ spacer = new Space();
+ spacer.setIPD(fobj.getLeaderPatternWidth().getValue() - width);
+ width = fobj.getLeaderPatternWidth().getValue();
+ }
+ fa.setUnitWidth(width);
+ if (spacer != null) {
+ fa.addChildArea(spacer);
+ }
+ leaderArea = fa;
+ }
+ return leaderArea;
+ }
+
+ protected void offsetArea(InlineArea area, LayoutContext context) {
+ int pattern = fobj.getLeaderPattern();
+ int bpd = area.getBPD();
+
+ switch (pattern) {
+ case EN_RULE:
+ switch (verticalAlignment) {
+ case EN_TOP:
+ area.setOffset(0);
+ break;
+ case EN_MIDDLE:
+ area.setOffset(context.getMiddleBaseline() - bpd / 2);
+ break;
+ case EN_BOTTOM:
+ area.setOffset(context.getLineHeight() - bpd);
+ break;
+ case EN_BASELINE: // fall through
+ default:
+ area.setOffset(context.getBaseline() - bpd);
+ break;
+ }
+ break;
+ case EN_DOTS:
+ switch (verticalAlignment) {
+ case EN_TOP:
+ area.setOffset(0);
+ break;
+ case EN_MIDDLE:
+ area.setOffset(context.getMiddleBaseline());
+ break;
+ case EN_BOTTOM:
+ area.setOffset(context.getLineHeight() - bpd + font.getAscender());
+ break;
+ case EN_BASELINE: // fall through
+ default:
+ area.setOffset(context.getBaseline());
+ break;
+ }
+ break;
+ case EN_SPACE:
+ // nothing to do
+ break;
+ case EN_USECONTENT:
+ switch (verticalAlignment) {
+ case EN_TOP:
+ area.setOffset(0);
+ break;
+ case EN_MIDDLE:
+ area.setOffset(context.getMiddleBaseline());
+ break;
+ case EN_BOTTOM:
+ area.setOffset(context.getLineHeight() - bpd);
+ break;
+ case EN_BASELINE: // fall through
+ default:
+ area.setOffset(context.getBaseline());
+ break;
+ }
+ break;
+ }
+ }
+
+ public void addAreas(PositionIterator posIter, LayoutContext context) {
+ if (fobj.getLeaderPattern() != EN_USECONTENT) {
+ // use LeafNodeLayoutManager.addAreas()
+ super.addAreas(posIter, context);
+ } else {
+ addId();
+
+ widthAdjustArea(curArea, context);
+
+ // add content areas
+ KnuthPossPosIter contentIter = new KnuthPossPosIter(contentList, 0, contentList.size());
+ clm.addAreas(contentIter, context);
+ offsetArea(curArea, context);
+
+ parentLM.addChildArea(curArea);
+
+ while (posIter.hasNext()) {
+ posIter.next();
+ }
+ }
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context,
+ int alignment) {
+ MinOptMax ipd;
+ curArea = get(context);
+ LinkedList returnList = new LinkedList();
+
+ if (curArea == null) {
+ setFinished(true);
+ return null;
+ }
+
+ ipd = getAllocationIPD(context.getRefIPD());
+
+ int bpd = curArea.getBPD();
+ int lead = 0;
+ int total = 0;
+ int middle = 0;
+ switch (verticalAlignment) {
+ case EN_MIDDLE : middle = bpd / 2 ;
+ break;
+ case EN_TOP : // fall through
+ case EN_BOTTOM : total = bpd;
+ break;
+ case EN_BASELINE: // fall through
+ default: lead = bpd;
+ break;
+ }
+
+ // create the AreaInfo object to store the computed values
+ areaInfo = new AreaInfo((short) 0, ipd, false,
+ lead, total, middle);
+
+ // node is a fo:Leader
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
+ areaInfo.middle,
+ new LeafPosition(this, -1), true));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(areaInfo.ipdArea.opt,
+ areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
+ areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
+ new LeafPosition(this, 0), false));
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
+ areaInfo.middle,
+ new LeafPosition(this, -1), true));
+
+ setFinished(true);
+ return returnList;
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ // use the AbstractLayoutManager.hyphenate() null implementation
+ super.hyphenate(pos, hc);
+ }
+
+ public boolean applyChanges(List oldList) {
+ setFinished(false);
+ return false;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ int flaggedPenalty,
+ int alignment) {
+ if (isFinished()) {
+ return null;
+ }
+
+ LinkedList returnList = new LinkedList();
+
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
+ areaInfo.middle,
+ new LeafPosition(this, -1), true));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(areaInfo.ipdArea.opt,
+ areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
+ areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
+ new LeafPosition(this, 0), false));
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
+ areaInfo.middle,
+ new LeafPosition(this, -1), true));
+
+ setFinished(true);
+ return returnList;
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.layoutmgr.AbstractLayoutManager;
+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.traits.MinOptMax;
+
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * Base LayoutManager for leaf-node FObj, ie: ones which have no children.
+ * These are all inline objects. Most of them cannot be split (Text is
+ * an exception to this rule.)
+ * This class can be extended to handle the creation and adding of the
+ * inline area.
+ */
+public abstract class LeafNodeLayoutManager extends AbstractLayoutManager
+ implements InlineLevelLayoutManager {
+ /**
+ * The inline area that this leafnode will add.
+ */
+ protected InlineArea curArea = null;
+ protected int verticalAlignment;
+ private int lead;
+ private MinOptMax ipd;
+
+ protected boolean bSomethingChanged = false;
+ protected AreaInfo areaInfo = null;
+
+ /**
+ * Store information about the inline area
+ */
+ protected class AreaInfo {
+ protected short iLScount;
+ protected MinOptMax ipdArea;
+ protected boolean bHyphenated;
+ protected int lead;
+ protected int total;
+ protected int middle;
+
+ public AreaInfo(short iLS, MinOptMax ipd, boolean bHyph,
+ int l, int t, int m) {
+ iLScount = iLS;
+ ipdArea = ipd;
+ bHyphenated = bHyph;
+ lead = l;
+ total = t;
+ middle = m;
+ }
+ }
+
+
+ /**
+ * Create a Leaf node layout mananger.
+ * @param node the FObj to attach to this LM.
+ */
+ public LeafNodeLayoutManager(FObj node) {
+ super(node);
+ }
+
+ /**
+ * Create a Leaf node layout mananger.
+ */
+ public LeafNodeLayoutManager() {
+ }
+
+ /**
+ * get the inline area.
+ * @param context the context used to create the area
+ * @return the current inline area for this layout manager
+ */
+ public InlineArea get(LayoutContext context) {
+ return curArea;
+ }
+
+ /**
+ * Check if this inline area is resolved due to changes in
+ * page or ipd.
+ * Currently not used.
+ * @return true if the area is resolved when adding
+ */
+ public boolean resolved() {
+ return false;
+ }
+
+ /**
+ * Set the current inline area.
+ * @param ia the inline area to set for this layout manager
+ */
+ public void setCurrentArea(InlineArea ia) {
+ curArea = ia;
+ }
+
+ /**
+ * Set the alignment of the inline area.
+ * @param al the vertical alignment positioning
+ */
+ public void setAlignment(int al) {
+ verticalAlignment = al;
+ }
+
+ /**
+ * Set the lead for this inline area.
+ * The lead is the distance from the top of the object
+ * to the baseline.
+ * @param l the lead value
+ */
+ public void setLead(int l) {
+ lead = l;
+ }
+
+ /** @return the lead value (distance from the top of the object to the baseline) */
+ public int getLead() {
+ return this.lead;
+ }
+
+ /**
+ * This is a leaf-node, so this method is never called.
+ * @param childArea the childArea to add
+ */
+ public void addChildArea(Area childArea) {
+ }
+
+ /**
+ * This is a leaf-node, so this method is never called.
+ * @param childArea the childArea to get the parent for
+ * @return the parent area
+ */
+ public Area getParentArea(Area childArea) {
+ return null;
+ }
+
+ /**
+ * Get the allocation ipd of the inline area.
+ * This method may be overridden to handle percentage values.
+ * @param refIPD the ipd of the parent reference area
+ * @return the min/opt/max ipd of the inline area
+ */
+ protected MinOptMax getAllocationIPD(int refIPD) {
+ return new MinOptMax(curArea.getIPD());
+ }
+
+ /**
+ * Add the area for this layout manager.
+ * This adds the single inline area to the parent.
+ * @param posIter the position iterator
+ * @param context the layout context for adding the area
+ */
+ public void addAreas(PositionIterator posIter, LayoutContext context) {
+ addId();
+
+ InlineArea area = getEffectiveArea();
+ if (area.getAllocIPD() > 0 || area.getAllocBPD() > 0) {
+ offsetArea(area, context);
+ widthAdjustArea(area, context);
+ parentLM.addChildArea(area);
+ }
+
+ while (posIter.hasNext()) {
+ posIter.next();
+ }
+ }
+
+ /**
+ * @return the effective area to be added to the area tree. Normally, this is simply "curArea"
+ * but in the case of page-number(-citation) curArea is cloned, updated and returned.
+ */
+ protected InlineArea getEffectiveArea() {
+ return curArea;
+ }
+
+ protected void addId() {
+ // Do nothing here, overriden in subclasses that have an 'id' property.
+ }
+
+ /**
+ * Offset this area.
+ * Offset the inline area in the bpd direction when adding the
+ * inline area.
+ * This is used for vertical alignment.
+ * Subclasses should override this if necessary.
+ * @param area the inline area to be updated
+ * @param context the layout context used for adding the area
+ */
+ protected void offsetArea(InlineArea area, LayoutContext context) {
+ int bpd = area.getBPD();
+ switch (verticalAlignment) {
+ case EN_MIDDLE:
+ area.setOffset(context.getMiddleBaseline() - bpd / 2);
+ break;
+ case EN_TOP:
+ area.setOffset(context.getTopBaseline());
+ break;
+ case EN_BOTTOM:
+ area.setOffset(context.getBottomBaseline() - bpd);
+ break;
+ case EN_BASELINE:
+ default:
+ area.setOffset(context.getBaseline() - bpd);
+ break;
+ }
+ }
+
+ /**
+ * Adjust the width of the area when adding.
+ * This uses the min/opt/max values to adjust the with
+ * of the inline area by a percentage.
+ * @param area the inline area to be updated
+ * @param context the layout context for adding this area
+ */
+ protected void widthAdjustArea(InlineArea area, LayoutContext context) {
+ double dAdjust = context.getIPDAdjust();
+ int width = areaInfo.ipdArea.opt;
+ if (dAdjust < 0) {
+ width = (int) (width + dAdjust * (areaInfo.ipdArea.opt
+ - areaInfo.ipdArea.min));
+ } else if (dAdjust > 0) {
+ width = (int) (width + dAdjust * (areaInfo.ipdArea.max
+ - areaInfo.ipdArea.opt));
+ }
+ area.setIPD(width);
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context,
+ int alignment) {
+ MinOptMax ipd;
+ curArea = get(context);
+
+ if (curArea == null) {
+ setFinished(true);
+ return null;
+ }
+ ipd = getAllocationIPD(context.getRefIPD());
+
+ int bpd = curArea.getBPD();
+ int lead = 0;
+ int total = 0;
+ int middle = 0;
+ switch (verticalAlignment) {
+ case EN_MIDDLE : middle = bpd / 2 ;
+ lead = bpd / 2 ;
+ break;
+ case EN_TOP : total = bpd;
+ break;
+ case EN_BOTTOM : total = bpd;
+ break;
+ case EN_BASELINE:
+ default:
+ //lead = bpd;
+ lead = getLead();
+ total = bpd;
+ break;
+ }
+
+ // create the AreaInfo object to store the computed values
+ areaInfo = new AreaInfo((short) 0, ipd, false,
+ lead, total, middle);
+
+ // node is a fo:ExternalGraphic, fo:InstreamForeignObject,
+ // fo:PageNumber or fo:PageNumberCitation
+ LinkedList returnList = new LinkedList();
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), false));
+ setFinished(true);
+ return returnList;
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ // return the unchanged elements
+ return oldList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ }
+
+ public boolean applyChanges(List oldList) {
+ setFinished(false);
+ return false;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ /*int flaggedPenalty,*/
+ int alignment) {
+ if (isFinished()) {
+ return null;
+ }
+
+ LinkedList returnList = new LinkedList();
+
+ // fobj is a fo:ExternalGraphic, fo:InstreamForeignObject,
+ // fo:PageNumber or fo:PageNumberCitation
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), true));
+
+ setFinished(true);
+ return returnList;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-2005 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: LineLayoutManager.java,v 1.17 2004/04/02 10:38:29 cbowditch Exp $ */
+
+package org.apache.fop.layoutmgr.inline;
+
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.fo.properties.CommonHyphenation;
+import org.apache.fop.hyphenation.Hyphenation;
+import org.apache.fop.hyphenation.Hyphenator;
+import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
+import org.apache.fop.layoutmgr.BreakingAlgorithm;
+import org.apache.fop.layoutmgr.ElementListObserver;
+import org.apache.fop.layoutmgr.KnuthBlockBox;
+import org.apache.fop.layoutmgr.KnuthBox;
+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.LayoutManager;
+import org.apache.fop.layoutmgr.LeafPosition;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.SpaceSpecifier;
+import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
+import org.apache.fop.area.LineArea;
+
+import java.util.ListIterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * LayoutManager for lines. It builds one or more lines containing
+ * inline areas generated by its sub layout managers.
+ * A break is found for each line which may contain one of more
+ * breaks from the child layout managers.
+ * Once a break is found then it is return for the parent layout
+ * manager to handle.
+ * When the areas are being added to the page this manager
+ * creates a line area to contain the inline areas added by the
+ * child layout managers.
+ */
+public class LineLayoutManager extends InlineStackingLayoutManager
+ implements BlockLevelLayoutManager {
+
+ private Block fobj;
+
+ private void initialize() {
+ bTextAlignment = fobj.getTextAlign();
+ bTextAlignmentLast = fobj.getTextAlignLast();
+ textIndent = fobj.getTextIndent();
+ hyphProps = fobj.getCommonHyphenation();
+
+ //
+ if (bTextAlignment != EN_JUSTIFY && bTextAlignmentLast == EN_JUSTIFY) {
+ effectiveAlignment = 0;
+ } else {
+ effectiveAlignment = bTextAlignment;
+ }
+ }
+
+ /**
+ * Private class to store information about inline breaks.
+ * Each value holds the start and end indexes into a List of
+ * inline break positions.
+ */
+ private static class LineBreakPosition extends LeafPosition {
+ // int iPos;
+ int iParIndex; // index of the Paragraph this Position refers to
+ int availableShrink;
+ int availableStretch;
+ int difference;
+ double dAdjust; // Percentage to adjust (stretch or shrink)
+ double ipdAdjust; // Percentage to adjust (stretch or shrink)
+ int startIndent;
+ int lineHeight;
+ int lineWidth;
+ int baseline;
+ int topShift;
+ int bottomShift;
+
+ LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
+ int shrink, int stretch, int diff,
+ double ipdA, double adjust, int ind,
+ int lh, int lw, int bl, int ts, int bs) {
+ super(lm, iBreakIndex);
+ availableShrink = shrink;
+ availableStretch = stretch;
+ difference = diff;
+ iParIndex = index;
+ ipdAdjust = ipdA;
+ dAdjust = adjust;
+ startIndent = ind;
+ lineHeight = lh;
+ lineWidth = lw;
+ baseline = bl;
+ topShift = ts;
+ bottomShift = bs;
+ }
+
+ }
+
+
+ /** Break positions returned by inline content. */
+ private List vecInlineBreaks = new java.util.ArrayList();
+
+ private int bTextAlignment = EN_JUSTIFY;
+ private int bTextAlignmentLast;
+ private int effectiveAlignment;
+ private Length textIndent;
+ private int iIndents = 0;
+ private CommonHyphenation hyphProps;
+ //private LayoutProps layoutProps;
+
+ private int lineHeight;
+ private int lead;
+ private int follow;
+ // offset of the middle baseline with respect to the main baseline
+ private int middleShift;
+
+ private List knuthParagraphs = null;
+ private int iReturnedLBP = 0;
+ private int iStartElement = 0;
+ private int iEndElement = 0;
+
+ // parameters of Knuth's algorithm:
+ // penalty value for flagged penalties
+ private int flaggedPenalty = 50;
+
+ private LineLayoutPossibilities lineLayouts;
+ private List lineLayoutsList;
+ private int iLineWidth = 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;
+
+
+ // this class is used to remember
+ // which was the first element in the paragraph
+ // returned by each LM
+ private class Update {
+ private InlineLevelLayoutManager inlineLM;
+ private int iFirstIndex;
+
+ public Update(InlineLevelLayoutManager lm, int index) {
+ inlineLM = lm;
+ iFirstIndex = index;
+ }
+ }
+
+ // this class represents a paragraph
+ private class Paragraph extends KnuthSequence {
+ // space at the end of the last line (in millipoints)
+ private MinOptMax lineFiller;
+ private int textAlignment;
+ private int textAlignmentLast;
+ private int textIndent;
+ private int lineWidth;
+ // the LM which created the paragraph
+ private LineLayoutManager layoutManager;
+
+ public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
+ int indent) {
+ super();
+ layoutManager = llm;
+ textAlignment = alignment;
+ textAlignmentLast = alignmentLast;
+ textIndent = indent;
+ }
+
+ public void startParagraph(int lw) {
+ lineWidth = lw;
+ startSequence();
+ }
+
+ public void startSequence() {
+ // set the minimum amount of empty space at the end of the
+ // last line
+ if (bTextAlignment == EN_CENTER) {
+ lineFiller = new MinOptMax(0);
+ } else {
+ lineFiller = new MinOptMax(0, (int)(lineWidth / 12), lineWidth);
+ }
+
+ // add auxiliary elements at the beginning of the paragraph
+ if (bTextAlignment == EN_CENTER && bTextAlignmentLast != EN_JUSTIFY) {
+ this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
+ null, false));
+ ignoreAtStart ++;
+ }
+
+ // add the element representing text indentation
+ // at the beginning of the first paragraph
+ if (knuthParagraphs.size() == 0
+ && fobj.getTextIndent().getValue() != 0) {
+ this.add(new KnuthInlineBox(fobj.getTextIndent().getValue(), 0, 0, 0,
+ null, false));
+ ignoreAtStart ++;
+ }
+ }
+
+ public void endParagraph() {
+ KnuthSequence finishedPar = this.endSequence();
+ if (finishedPar != null) {
+ knuthParagraphs.add(finishedPar);
+ }
+ }
+
+ public KnuthSequence endSequence() {
+ // remove glue and penalty item at the end of the paragraph
+ while (this.size() > ignoreAtStart
+ && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
+ this.remove(this.size() - 1);
+ }
+ if (this.size() > ignoreAtStart) {
+ if (bTextAlignment == EN_CENTER
+ && bTextAlignmentLast != EN_JUSTIFY) {
+ this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
+ null, false));
+ this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+ false, null, false));
+ ignoreAtEnd = 2;
+ } else if (bTextAlignmentLast != EN_JUSTIFY) {
+ // add the elements representing the space
+ // at the end of the last line
+ // and the forced break
+ this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+ false, null, false));
+ this.add(new KnuthGlue(lineFiller.opt,
+ lineFiller.max - lineFiller.opt,
+ lineFiller.opt - lineFiller.min, null, false));
+ this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+ false, null, false));
+ ignoreAtEnd = 3;
+ } else {
+ // add only the element representing the forced break
+ this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+ false, null, false));
+ ignoreAtEnd = 1;
+ }
+ return this;
+ } else {
+ this.clear();
+ return null;
+ }
+ }
+
+ }
+
+ private class LineBreakingAlgorithm extends BreakingAlgorithm {
+ private LineLayoutManager thisLLM;
+ private int pageAlignment;
+ private int activePossibility;
+ private int addedPositions;
+ private int textIndent;
+ private int fillerMinWidth;
+ private int lineHeight;
+ private int lead;
+ private int follow;
+ private int middleshift;
+ private int maxDiff;
+ private static final double MAX_DEMERITS = 10e6;
+
+ public LineBreakingAlgorithm (int pageAlign,
+ int textAlign, int textAlignLast,
+ int indent, int fillerWidth,
+ int lh, int ld, int fl, int ms, boolean first,
+ LineLayoutManager llm) {
+ super(textAlign, textAlignLast, first, false);
+ pageAlignment = pageAlign;
+ textIndent = indent;
+ fillerMinWidth = fillerWidth;
+ lineHeight = lh;
+ lead = ld;
+ follow = fl;
+ middleshift = ms;
+ thisLLM = llm;
+ activePossibility = -1;
+ maxDiff = fobj.getWidows() >= fobj.getOrphans()
+ ? fobj.getWidows()
+ : fobj.getOrphans();
+ }
+
+ public void updateData1(int lineCount, double demerits) {
+ lineLayouts.addPossibility(lineCount, demerits);
+ log.trace("Layout possibility in " + lineCount + " lines; break at position:");
+ }
+
+ public void updateData2(KnuthNode bestActiveNode,
+ KnuthSequence par,
+ int total) {
+ // compute indent and adjustment ratio, according to
+ // the value of text-align and text-align-last
+ int indent = 0;
+ int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+ int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+ indent += (textAlign == Constants.EN_CENTER) ?
+ difference / 2 :
+ (textAlign == Constants.EN_END) ? difference : 0;
+ indent += (bestActiveNode.line == 1 && bFirst) ?
+ textIndent : 0;
+ double ratio = (textAlign == Constants.EN_JUSTIFY
+ || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+ // add nodes at the beginning of the list, as they are found
+ // backwards, from the last one to the first one
+
+ // the first time this method is called, initialize activePossibility
+ if (activePossibility == -1) {
+ activePossibility = 0;
+ addedPositions = 0;
+ }
+
+ if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
+ activePossibility ++;
+ addedPositions = 0;
+ //System.out.println(" ");
+ }
+
+ //System.out.println("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) + ") difference = " + difference + " ratio = " + ratio);
+ lineLayouts.addBreakPosition(makeLineBreakPosition(par,
+ (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1: 0),
+ bestActiveNode.position,
+ bestActiveNode.availableShrink - (addedPositions > 0 ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), bestActiveNode.availableStretch, difference, ratio, indent),
+ activePossibility);
+ addedPositions ++;
+ }
+
+ /* reset activePossibility, as if breakpoints have not yet been computed
+ */
+ public void resetAlgorithm() {
+ activePossibility = -1;
+ }
+
+ private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
+ int firstElementIndex,
+ int lastElementIndex,
+ int availableShrink, int availableStretch, int difference,
+ double ratio,
+ int indent) {
+ // line height calculation
+
+ int halfLeading = (lineHeight - lead - follow) / 2;
+ // height before the main baseline
+ int lineLead = lead;
+ // maximum size of top and bottom alignment
+ int maxtb = follow;
+ // max size of middle alignment before and after the middle baseline
+ int middlefollow = maxtb;
+ // true if this line contains only zero-height, auxiliary boxes
+ // and the actual line width is 0; in this case, the line "collapses"
+ // i.e. the line area will have bpd = 0
+ boolean bZeroHeightLine = (difference == iLineWidth);
+
+ // if line-stacking-strategy is "font-height", the line height
+ // is not affected by its content
+ if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
+ ListIterator inlineIterator
+ = par.listIterator(firstElementIndex);
+ for (int j = firstElementIndex;
+ j <= lastElementIndex;
+ j++) {
+ KnuthElement element = (KnuthElement) inlineIterator.next();
+ if (element.isBox()) {
+ if (((KnuthInlineBox) element).getLead() > lineLead) {
+ lineLead = ((KnuthInlineBox) element).getLead();
+ }
+ if (((KnuthInlineBox) element).getTotal() > maxtb) {
+ maxtb = ((KnuthInlineBox) element).getTotal();
+ }
+ if (((KnuthInlineBox) element).getMiddle() > lineLead + middleShift) {
+ lineLead += ((KnuthInlineBox) element).getMiddle()
+ - lineLead - middleShift;
+ }
+ if (((KnuthInlineBox) element).getMiddle() > middlefollow - middleShift) {
+ middlefollow += ((KnuthInlineBox) element).getMiddle()
+ - middlefollow + middleShift;
+ }
+ if (bZeroHeightLine
+ && (!element.isAuxiliary()
+ || ((KnuthInlineBox) element).getTotal() > 0
+ || ((KnuthInlineBox) element).getLead() > 0
+ || ((KnuthInlineBox) element).getMiddle() > 0)) {
+ bZeroHeightLine = false;
+ }
+ }
+ }
+
+ if (maxtb - lineLead > middlefollow) {
+ middlefollow = maxtb - lineLead;
+ }
+ }
+
+ constantLineHeight = lineLead + middlefollow + (lineHeight - lead - follow);
+
+ if (bZeroHeightLine) {
+ return new LineBreakPosition(thisLLM,
+ knuthParagraphs.indexOf(par),
+ lastElementIndex,
+ availableShrink, availableStretch, difference, ratio, 0, indent,
+ 0, iLineWidth,
+ 0, 0, 0);
+ } else {
+ return new LineBreakPosition(thisLLM,
+ knuthParagraphs.indexOf(par),
+ lastElementIndex,
+ availableShrink, availableStretch, difference, ratio, 0, indent,
+ lineLead + middlefollow + (lineHeight - lead - follow), iLineWidth,
+ lineLead + halfLeading,
+ - lineLead, middlefollow);
+ }
+ }
+
+ public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
+ double threshold, boolean force,
+ boolean hyphenationAllowed) {
+ return super.findBreakingPoints(par, /*lineWidth,*/
+ threshold, force, hyphenationAllowed);
+ }
+
+ protected int filterActiveNodes() {
+ KnuthNode bestActiveNode = null;
+
+ if (pageAlignment == EN_JUSTIFY) {
+ // leave all active nodes and find the optimum line number
+ //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //System.out.println(" + lines = " + node.line + " demerits = " + node.totalDemerits);
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ }
+ }
+
+ // scan the node set once again and remove some nodes
+ //System.out.println("LBA.filterActiveList> layout selection");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
+ //if (false) {
+ if (node.line != bestActiveNode.line
+ && node.totalDemerits > MAX_DEMERITS) {
+ //System.out.println(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+ removeNode(i, node);
+ } else {
+ //System.out.println(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
+ }
+ }
+ }
+ } else {
+ // leave only the active node with fewest total demerits
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ if (node != bestActiveNode) {
+ removeNode(i, node);
+ }
+ }
+ }
+ }
+ return bestActiveNode.line;
+ }
+ }
+
+
+ private int constantLineHeight = 12000;
+
+
+ /**
+ * Create a new Line Layout Manager.
+ * This is used by the block layout manager to create
+ * line managers for handling inline areas flowing into line areas.
+ *
+ * @param lh the default line height
+ * @param l the default lead, from top to baseline
+ * @param f the default follow, from baseline to bottom
+ */
+ public LineLayoutManager(Block block, int lh, int l, int f, int ms) {
+ super(block);
+ fobj = block;
+ // the child FObj are owned by the parent BlockLM
+ // this LM has all its childLMs preloaded
+ fobjIter = null;
+ lineHeight = lh;
+ lead = l;
+ follow = f;
+ middleShift = ms;
+ initialize(); // Normally done when started by parent!
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ // Get a break from currently active child LM
+ // Set up constraints for inline level managers
+ InlineLevelLayoutManager curLM ; // currently active LM
+
+ // IPD remaining in line
+ MinOptMax availIPD = context.getStackLimit();
+
+ clearPrevIPD();
+ int iPrevLineEnd = vecInlineBreaks.size();
+
+ if (iPrevLineEnd == 0 && bTextAlignment == EN_START) {
+ availIPD.subtract(new MinOptMax(textIndent.getValue()));
+ }
+
+ //PHASE 1: Create Knuth elements
+ if (knuthParagraphs == null) {
+ // it's the first time this method is called
+ knuthParagraphs = new ArrayList();
+
+ // here starts Knuth's algorithm
+ //TODO availIPD should not really be used here, so we can later support custom line
+ //widths for for each line (side-floats, differing available IPD after page break)
+ collectInlineKnuthElements(context, availIPD);
+ } else {
+ // this method has been called before
+ // all line breaks are already calculated
+ }
+
+ // return finished when there's no content
+ if (knuthParagraphs.size() == 0) {
+ setFinished(true);
+ return null;
+ }
+
+ //PHASE 2: Create line breaks
+ return findOptimalLineBreakingPoints(alignment);
+ /*
+ LineBreakPosition lbp = null;
+ if (breakpoints == null) {
+ // find the optimal line breaking points for each paragraph
+ breakpoints = new ArrayList();
+ ListIterator paragraphsIterator
+ = knuthParagraphs.listIterator(knuthParagraphs.size());
+ Paragraph currPar = null;
+ while (paragraphsIterator.hasPrevious()) {
+ currPar = (Paragraph) paragraphsIterator.previous();
+ findBreakingPoints(currPar, context.getStackLimit().opt);
+ }
+ }*/
+
+ //PHASE 3: Return lines
+
+ /*
+ // get a break point from the list
+ lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
+ if (iReturnedLBP == breakpoints.size()) {
+ setFinished(true);
+ }
+
+ BreakPoss curLineBP = new BreakPoss(lbp);
+ curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
+ curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
+ return curLineBP;
+ */
+ }
+
+ /**
+ * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
+ * @param context the LayoutContext
+ * @param availIPD available IPD for line (should be removed!)
+ */
+ private void collectInlineKnuthElements(LayoutContext context, MinOptMax availIPD) {
+ LayoutContext inlineLC = new LayoutContext(context);
+
+ InlineLevelLayoutManager curLM;
+ KnuthElement thisElement = null;
+ LinkedList returnedList = null;
+ iLineWidth = context.getStackLimit().opt;
+
+ // convert all the text in a sequence of paragraphs made
+ // of KnuthBox, KnuthGlue and KnuthPenalty objects
+ boolean bPrevWasKnuthBox = false;
+ KnuthBox prevBox = null;
+
+ Paragraph knuthPar = new Paragraph(this,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue());
+ knuthPar.startParagraph(availIPD.opt);
+ while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+ if ((returnedList
+ = curLM.getNextKnuthElements(inlineLC,
+ effectiveAlignment))
+ != null) {
+ if (returnedList.size() == 0) {
+ continue;
+ }
+ // look at the first element
+ thisElement = (KnuthElement) returnedList.getFirst();
+ if (thisElement.isBox() && !thisElement.isAuxiliary()
+ && bPrevWasKnuthBox) {
+ prevBox = (KnuthBox) knuthPar.removeLast();
+ LinkedList oldList = new LinkedList();
+ // 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;
+ oldList.add(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
+ oldList.add(prevBox);
+ oldList.addFirst((KnuthGlue) knuthPar.removeLast());
+ oldList.addFirst((KnuthPenalty) knuthPar.removeLast());
+ }
+ // adding a letter space could involve, according to the text
+ // represented by oldList, replacing a glue element or adding
+ // new elements
+ knuthPar.addAll(((InlineLevelLayoutManager)
+ prevBox.getLayoutManager())
+ .addALetterSpaceTo(oldList));
+ if (((KnuthInlineBox) prevBox).isAnchor()) {
+ // prevBox represents a footnote citation: copy footnote info
+ // from prevBox to the new box
+ KnuthInlineBox newBox = (KnuthInlineBox) knuthPar.getLast();
+ newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM());
+ }
+ }
+
+ // look at the last element
+ KnuthElement lastElement = (KnuthElement) returnedList.getLast();
+ boolean bForceLinefeed = false;
+ if (lastElement.isBox()) {
+ bPrevWasKnuthBox = true;
+ } else {
+ 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) {
+ if (knuthPar.size() == 0) {
+ //only a forced linefeed on this line
+ //-> compensate with a zero width box
+ knuthPar.add(new KnuthInlineBox(0, 0, 0, 0,
+ null, false));
+ }
+ knuthPar.endParagraph();
+ knuthPar = new Paragraph(this,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue());
+ knuthPar.startParagraph(availIPD.opt);
+ bPrevWasKnuthBox = false;
+ }
+ } else {
+ // curLM returned null; this can happen
+ // if it has nothing more to layout,
+ // so just iterate once more to see
+ // if there are other children
+ }
+ }
+ knuthPar.endParagraph();
+ ElementListObserver.observe(knuthPar, "line", null);
+ }
+
+ /**
+ * Find a set of breaking points.
+ * This method is called only once by getNextBreakPoss, and it
+ * subsequently calls the other findBreakingPoints() method with
+ * different parameters, until a set of breaking points is found.
+ *
+ * @param par the list of elements that must be parted
+ * into lines
+ * @param lineWidth the desired length ot the lines
+ */
+ /*
+ private void findBreakingPoints(Paragraph par, int lineWidth) {
+ // maximum adjustment ratio permitted
+ float maxAdjustment = 1;
+
+ // first try
+ if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
+ // the first try failed, now try something different
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
+ if (hyphProps.hyphenate == Constants.EN_TRUE) {
+ // consider every hyphenation point as a legal break
+ findHyphenationPoints(par);
+ } else {
+ // try with a higher threshold
+ maxAdjustment = 5;
+ }
+
+ if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
+ // the second try failed too, try with a huge threshold;
+ // if this fails too, use a different algorithm
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
+ + (hyphProps.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
+ maxAdjustment = 20;
+ if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
+ log.debug("No set of breaking points found, using first-fit algorithm");
+ }
+ }
+ }
+ }
+
+ private boolean findBreakingPoints(Paragraph par, int lineWidth,
+ double threshold, boolean force) {
+ KnuthParagraph knuthPara = new KnuthParagraph(par);
+ int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
+ if (lines == 0) {
+ return false;
+ }
+
+ for (int i = lines-1; i >= 0; i--) {
+ int line = i+1;
+ if (log.isTraceEnabled()) {
+ log.trace("Making line from " + knuthPara.getStart(i) + " to " +
+ knuthPara.getEnd(i));
+ }
+ // compute indent and adjustment ratio, according to
+ // the value of text-align and text-align-last
+
+ int difference = knuthPara.getDifference(i);
+ if (line == lines) {
+ difference += par.lineFillerWidth;
+ }
+ int textAlign = (line < lines)
+ ? bTextAlignment : bTextAlignmentLast;
+ int indent = (textAlign == EN_CENTER)
+ ? difference / 2
+ : (textAlign == EN_END) ? difference : 0;
+ indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
+ ? textIndent.getValue() : 0;
+ double ratio = (textAlign == EN_JUSTIFY)
+ ? knuthPara.getAdjustRatio(i) : 0;
+
+ int start = knuthPara.getStart(i);
+ int end = knuthPara.getEnd(i);
+ makeLineBreakPosition(par, start, end, 0, ratio, indent);
+ }
+ return true;
+ }
+
+ 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 above the main baseline
+ int lineLead = lead + halfLeading;
+ // maximum size of top and bottom alignment
+ int maxtb = follow + halfLeading;
+ // max size of middle alignment above and below the middle baseline
+ int middlefollow = maxtb;
+
+ ListIterator inlineIterator
+ = par.listIterator(firstElementIndex);
+ for (int j = firstElementIndex;
+ j <= lastElementIndex;
+ j++) {
+ KnuthElement element = (KnuthElement) inlineIterator.next();
+ if (element.isBox()) {
+ KnuthInlineBox box = (KnuthInlineBox)element;
+ if (box.getLead() > lineLead) {
+ lineLead = box.getLead();
+ }
+ if (box.getTotal() > maxtb) {
+ maxtb = box.getTotal();
+ }
+ if (box.getMiddle() > lineLead + middleShift) {
+ lineLead += box.getMiddle()
+ - lineLead - middleShift;
+ }
+ if (box.getMiddle() > middlefollow - middleShift) {
+ middlefollow += box.getMiddle()
+ - middlefollow + middleShift;
+ }
+ }
+ }
+
+ if (maxtb - lineLead > middlefollow) {
+ middlefollow = maxtb - lineLead;
+ }
+
+ breakpoints.add(insertIndex,
+ new LineBreakPosition(this,
+ knuthParagraphs.indexOf(par),
+ lastElementIndex ,
+ ratio, 0, indent,
+ lineLead + middlefollow,
+ lineLead));
+ }*/
+
+
+ /**
+ * Phase 2 of Knuth algorithm: find optimal break points.
+ * @param alignment alignmenr of the paragraph
+ * @return a list of Knuth elements representing broken lines
+ */
+ private LinkedList findOptimalLineBreakingPoints(int alignment) {
+
+ // find the optimal line breaking points for each paragraph
+ ListIterator paragraphsIterator
+ = knuthParagraphs.listIterator(knuthParagraphs.size());
+ Paragraph currPar = null;
+ LineBreakingAlgorithm alg;
+ lineLayoutsList = new ArrayList(knuthParagraphs.size());
+ while (paragraphsIterator.hasPrevious()) {
+ lineLayouts = new LineLayoutPossibilities();
+ currPar = (Paragraph) paragraphsIterator.previous();
+ double maxAdjustment = 1;
+ int iBPcount = 0;
+ alg = new LineBreakingAlgorithm(alignment,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue(), currPar.lineFiller.opt,
+ lineHeight, lead, follow, middleShift,
+ (knuthParagraphs.indexOf(currPar) == 0),
+ this);
+
+ if (hyphProps.hyphenate == EN_TRUE) {
+ findHyphenationPoints(currPar);
+ }
+
+ // first try
+ boolean bHyphenationAllowed = false;
+ alg.setConstantLineWidth(iLineWidth);
+ iBPcount = alg.findBreakingPoints(currPar,
+ maxAdjustment, false, bHyphenationAllowed);
+ if (iBPcount == 0 || alignment == EN_JUSTIFY) {
+ // if the first try found a set of breaking points, save them
+ if (iBPcount > 0) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(false);
+ } else {
+ // the first try failed
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
+ }
+
+ // now try something different
+ log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE));
+ if (hyphProps.hyphenate == EN_TRUE) {
+ // consider every hyphenation point as a legal break
+ bHyphenationAllowed = true;
+ } else {
+ // try with a higher threshold
+ maxAdjustment = 5;
+ }
+
+ if ((iBPcount
+ = alg.findBreakingPoints(currPar,
+ maxAdjustment, false, bHyphenationAllowed)) == 0) {
+ // the second try failed too, try with a huge threshold
+ // and force the algorithm to find
+ // a set of breaking points
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
+ + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : ""));
+ maxAdjustment = 20;
+ iBPcount
+ = alg.findBreakingPoints(currPar,
+ maxAdjustment, true, bHyphenationAllowed);
+ }
+
+ // use non-hyphenated breaks, when possible
+ lineLayouts.restorePossibilities();
+
+ /* extension (not in the XSL FO recommendation): if vertical alignment
+ is justify and the paragraph has only one layout, try using
+ shorter or longer lines */
+ //TODO This code snippet is disabled. Reenable?
+ if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) {
+ //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
+ //System.out.println(" layouts with fewer lines? " + lineLayouts.canUseLessLines());
+ if (!lineLayouts.canUseMoreLines()) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(true);
+ // try with shorter lines
+ int savedLineWidth = iLineWidth;
+ iLineWidth = (int) (iLineWidth * 0.95);
+ iBPcount = alg.findBreakingPoints(currPar,
+ maxAdjustment, true, bHyphenationAllowed);
+ // use normal lines, when possible
+ lineLayouts.restorePossibilities();
+ iLineWidth = savedLineWidth;
+ }
+ if (!lineLayouts.canUseLessLines()) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(true);
+ // try with longer lines
+ int savedLineWidth = iLineWidth;
+ iLineWidth = (int) (iLineWidth * 1.05);
+ alg.setConstantLineWidth(iLineWidth);
+ iBPcount = alg.findBreakingPoints(currPar,
+ maxAdjustment, true, bHyphenationAllowed);
+ // use normal lines, when possible
+ lineLayouts.restorePossibilities();
+ iLineWidth = savedLineWidth;
+ }
+ //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
+ //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
+ }
+ }
+ lineLayoutsList.add(0, lineLayouts);
+ }
+
+
+ setFinished(true);
+
+ //Post-process the line breaks found
+ return postProcessLineBreaks(alignment);
+ }
+
+ private LinkedList postProcessLineBreaks(int alignment) {
+
+ LinkedList returnList = new LinkedList();
+
+ for (int p = 0; p < knuthParagraphs.size(); p ++) {
+ // null penalty between paragraphs
+ if (p > 0
+ && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+
+ if (alignment == EN_JUSTIFY) {
+ /* justified vertical alignment (not in the XSL FO recommendation):
+ create a multi-layout sequence whose elements will contain
+ a conventional Position */
+ Position returnPosition = new LeafPosition(this, p);
+ createElements(returnList, lineLayouts, returnPosition);
+ } else {
+ /* "normal" vertical alignment: create a sequence whose boxes
+ represent effective lines, and contain LineBreakPositions */
+ Position returnPosition = new LeafPosition(this, p);
+ int startIndex = 0;
+ for (int i = 0;
+ i < lineLayouts.getChosenLineCount();
+ i++) {
+ if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+ && i >= fobj.getOrphans()
+ && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()
+ && returnList.size() > 0) {
+ // null penalty allowing a page break between lines
+ returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+ }
+ int endIndex = ((LineBreakPosition) lineLayouts.getChosenPosition(i)).getLeafPos();
+ // create a list of the FootnoteBodyLM handling footnotes
+ // whose citations are in this line
+ LinkedList footnoteList = new LinkedList();
+ ListIterator elementIterator = ((Paragraph) knuthParagraphs.get(p)).listIterator(startIndex);
+ while (elementIterator.nextIndex() <= endIndex) {
+ KnuthElement element = (KnuthElement) elementIterator.next();
+ if (element instanceof KnuthInlineBox
+ && ((KnuthInlineBox) element).isAnchor()) {
+ footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
+ }
+ }
+ startIndex = endIndex + 1;
+ returnList.add(new KnuthBlockBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight,
+ footnoteList, lineLayouts.getChosenPosition(i), false));
+ }
+ }
+ }
+
+ return returnList;
+ }
+
+
+ private void createElements(List list, LineLayoutPossibilities lineLayouts,
+ Position elementPosition) {
+ /* number of normal, inner lines */
+ int nInnerLines = 0;
+ /* number of lines that can be used in order to fill more space */
+ int nOptionalLines = 0;
+ /* number of lines that can be used in order to fill more space
+ only if the paragraph is not parted */
+ int nConditionalOptionalLines = 0;
+ /* number of lines that can be omitted in order to fill less space */
+ int nEliminableLines = 0;
+ /* number of lines that can be omitted in order to fill less space
+ only if the paragraph is not parted */
+ int nConditionalEliminableLines = 0;
+ /* number of the first unbreakable lines */
+ int nFirstLines = fobj.getOrphans();
+ /* number of the last unbreakable lines */
+ int nLastLines = fobj.getWidows();
+ /* sub-sequence used to separate the elements representing different lines */
+ List breaker = new LinkedList();
+
+ /* comment out the next lines in order to test particular situations */
+ if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMinLineCount()) {
+ nInnerLines = lineLayouts.getMinLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getOptLineCount()) {
+ nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nEliminableLines = lineLayouts.getOptLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getMinLineCount();
+ } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMaxLineCount()) {
+ nOptionalLines = lineLayouts.getMaxLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getOptLineCount();
+ nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ nFirstLines -= nConditionalOptionalLines;
+ } else {
+ nConditionalOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ nFirstLines = lineLayouts.getOptLineCount();
+ nLastLines = 0;
+ }
+ /* comment out the previous lines in order to test particular situations */
+
+ /* use these lines to test particular situations
+ nInnerLines = 0;
+ nOptionalLines = 1;
+ nConditionalOptionalLines = 2;
+ nEliminableLines = 0;
+ nConditionalEliminableLines = 0;
+ nFirstLines = 1;
+ nLastLines = 3;
+ */
+
+ if (nLastLines != 0
+ && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
+ breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
+ -nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
+ 0, false, elementPosition, false));
+ breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+ nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ } else if (nLastLines != 0) {
+ breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
+ }
+
+ //System.out.println("first=" + nFirstLines + " inner=" + nInnerLines
+ // + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
+ // + " last=" + nLastLines
+ // + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
+
+ // creation of the elements:
+ // first group of lines
+ list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
+ (nLastLines == 0
+ && nConditionalOptionalLines == 0
+ && nConditionalEliminableLines == 0 ? true : false)));
+ if (nConditionalOptionalLines > 0
+ || nConditionalEliminableLines > 0) {
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+ nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition,
+ (nLastLines == 0 ? true : false)));
+ }
+
+ // optional lines
+ for (int i = 0; i < nOptionalLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(0, elementPosition, false));
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition, false));
+ }
+
+ // eliminable lines
+ for (int i = 0; i < nEliminableLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition, false));
+ }
+
+ // inner lines
+ for (int i = 0; i < nInnerLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+ }
+
+ // last group of lines
+ if (nLastLines > 0) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(nLastLines * constantLineHeight,
+ elementPosition, true));
+ }
+ }
+
+ public boolean mustKeepTogether() {
+ return false;
+ }
+
+ public boolean mustKeepWithPrevious() {
+ return false;
+ }
+
+ public boolean mustKeepWithNext() {
+ return false;
+ }
+
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ LeafPosition pos = (LeafPosition)lastElement.getPosition();
+ int totalAdj = adj;
+ //if (lastElement.isPenalty()) {
+ // totalAdj += lastElement.getW();
+ //}
+ //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
+ int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight + (adj > 0 ? - 0.4 : 0.4));
+ //System.out.println(" LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(pos.getLeafPos());
+ lineNumberDifference = lineLayouts.applyLineCountAdjustment(lineNumberDifference);
+ return lineNumberDifference * constantLineHeight;
+ }
+
+ public void discardSpace(KnuthGlue spaceGlue) {
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+ LinkedList returnList = new LinkedList();
+ for (int p = 0;
+ p < knuthParagraphs.size();
+ p ++) {
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+ //System.out.println("demerits of the chosen layout: " + lineLayouts.getChosenDemerits());
+ for (int i = 0;
+ i < lineLayouts.getChosenLineCount();
+ i ++) {
+ if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+ && i >= fobj.getOrphans()
+ && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()) {
+ // null penalty allowing a page break between lines
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i);
+ //System.out.println("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
+ //System.out.println(" shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
+
+ //System.out.println("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
+ MinOptMax contentIPD;
+ if (alignment == EN_JUSTIFY) {
+ contentIPD = new MinOptMax(
+ lbp.lineWidth - lbp.difference - lbp.availableShrink,
+ lbp.lineWidth - lbp.difference,
+ lbp.lineWidth - lbp.difference + lbp.availableStretch);
+ } else if (alignment == EN_CENTER) {
+ contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
+ } else if (alignment == EN_END) {
+ contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
+ } else {
+ contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
+ }
+ returnList.add(new KnuthBlockBox(lbp.lineHeight,
+ contentIPD,
+ (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0),
+ lbp, false));
+ }
+ }
+ return returnList;
+ }
+
+ /**
+ * find hyphenation points for every word int the current paragraph
+ * @ param currPar the paragraph whose words will be hyphenated
+ */
+ private void findHyphenationPoints(Paragraph currPar){
+ // hyphenate every word
+ ListIterator currParIterator
+ = currPar.listIterator(currPar.ignoreAtStart);
+ // list of TLM involved in hyphenation
+ LinkedList updateList = new LinkedList();
+ KnuthElement firstElement = null;
+ KnuthElement nextElement = null;
+ // current InlineLevelLayoutManager
+ InlineLevelLayoutManager currLM = null;
+ // number of KnuthBox elements containing word fragments
+ int boxCount;
+ // number of auxiliary KnuthElements between KnuthBoxes
+ int auxCount;
+ StringBuffer sbChars = null;
+
+ // find all hyphenation points
+ while (currParIterator.hasNext()) {
+ firstElement = (KnuthElement) currParIterator.next();
+ //
+ if (firstElement.getLayoutManager() != currLM) {
+ currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
+ if (currLM != null) {
+ updateList.add(new Update(currLM, currParIterator.previousIndex()));
+ } else {
+ break;
+ }
+ }
+
+ // collect word fragments, ignoring auxiliary elements;
+ // each word fragment was created by a different TextLM
+ if (firstElement.isBox() && !firstElement.isAuxiliary()) {
+ boxCount = 1;
+ auxCount = 0;
+ sbChars = new StringBuffer();
+ currLM.getWordChars(sbChars, firstElement.getPosition());
+ // look if next elements are boxes too
+ while (currParIterator.hasNext()) {
+ nextElement = (KnuthElement) currParIterator.next();
+ if (nextElement.isBox() && !nextElement.isAuxiliary()) {
+ // a non-auxiliary KnuthBox: append word chars
+ if (currLM != nextElement.getLayoutManager()) {
+ currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
+ updateList.add(new Update(currLM, currParIterator.previousIndex()));
+ }
+ // append text to recreate the whole word
+ boxCount ++;
+ currLM.getWordChars(sbChars, nextElement.getPosition());
+ } else if (!nextElement.isAuxiliary()) {
+ // a non-auxiliary non-box KnuthElement: stop
+ // go back to the last box or auxiliary element
+ currParIterator.previous();
+ break;
+ } else {
+ // an auxiliary KnuthElement: simply ignore it
+ auxCount ++;
+ }
+ }
+ log.trace(" Word to hyphenate: " + sbChars.toString());
+ // find hyphenation points
+ HyphContext hc = getHyphenContext(sbChars);
+ // ask each LM to hyphenate its word fragment
+ if (hc != null) {
+ KnuthElement element = null;
+ for (int i = 0; i < (boxCount + auxCount); i++) {
+ currParIterator.previous();
+ }
+ for (int i = 0; i < (boxCount + auxCount); i++) {
+ element = (KnuthElement) currParIterator.next();
+ if (element.isBox() && !element.isAuxiliary()) {
+ ((InlineLevelLayoutManager)
+ element.getLayoutManager()).hyphenate(element.getPosition(), hc);
+ } else {
+ // nothing to do, element is an auxiliary KnuthElement
+ }
+ }
+ }
+ }
+ }
+
+ // create iterator for the updateList
+ ListIterator updateListIterator = updateList.listIterator();
+ Update currUpdate = null;
+ //int iPreservedElements = 0;
+ int iAddedElements = 0;
+ //int iRemovedElements = 0;
+
+ while (updateListIterator.hasNext()) {
+ // ask the LMs to apply the changes and return
+ // the new KnuthElements to replace the old ones
+ currUpdate = (Update) updateListIterator.next();
+ int fromIndex = currUpdate.iFirstIndex;
+ int toIndex;
+ if (updateListIterator.hasNext()) {
+ Update nextUpdate = (Update) updateListIterator.next();
+ toIndex = nextUpdate.iFirstIndex;
+ updateListIterator.previous();
+ } else {
+ // maybe this is not always correct!
+ toIndex = currPar.size() - currPar.ignoreAtEnd
+ - iAddedElements;
+ }
+
+ // applyChanges() returns true if the LM modifies its data,
+ // so it must return new KnuthElements to replace the old ones
+ if (((InlineLevelLayoutManager) currUpdate.inlineLM)
+ .applyChanges(currPar.subList(fromIndex + iAddedElements,
+ toIndex + iAddedElements))) {
+ // insert the new KnuthElements
+ LinkedList newElements = null;
+ newElements
+ = currUpdate.inlineLM.getChangedKnuthElements
+ (currPar.subList(fromIndex + iAddedElements,
+ toIndex + iAddedElements),
+ /*flaggedPenalty,*/ effectiveAlignment);
+ // remove the old elements
+ currPar.subList(fromIndex + iAddedElements,
+ toIndex + iAddedElements).clear();
+ // insert the new elements
+ currPar.addAll(fromIndex + iAddedElements, newElements);
+ iAddedElements += newElements.size() - (toIndex - fromIndex);
+ }
+ }
+ updateListIterator = null;
+ updateList.clear();
+ }
+
+ /** Line area is always considered to act as a fence. */
+ protected boolean hasLeadingFence(boolean bNotFirst) {
+ return true;
+ }
+
+ /** Line area is always considered to act as a fence. */
+ protected boolean hasTrailingFence(boolean bNotLast) {
+ return true;
+ }
+
+ private HyphContext getHyphenContext(StringBuffer sbChars) {
+ // Find all hyphenation points in this word
+ // (get in an array of offsets)
+ // hyphProps are from the block level?.
+ // Note that according to the spec,
+ // they also "apply to" fo:character.
+ // I don't know what that means, since
+ // if we change language in the middle of a "word",
+ // the effect would seem quite strange!
+ // Or perhaps in that case, we say that it's several words.
+ // We probably should bring the hyphenation props up from the actual
+ // TextLM which generate the hyphenation buffer,
+ // since these properties inherit and could be specified
+ // on an inline or wrapper below the block level.
+ Hyphenation hyph
+ = Hyphenator.hyphenate(hyphProps.language,
+ hyphProps.country, sbChars.toString(),
+ hyphProps.hyphenationRemainCharacterCount,
+ hyphProps.hyphenationPushCharacterCount);
+ // They hyph structure contains the information we need
+ // Now start from prev: reset to that position, ask that LM to get
+ // a Position for the first hyphenation offset. If the offset isn't in
+ // its characters, it returns null,
+ // but must tell how many chars it had.
+ // Keep looking at currentBP using next hyphenation point until the
+ // returned size is greater than the available size
+ // or no more hyphenation points remain. Choose the best break.
+ if (hyph != null) {
+ return new HyphContext(hyph.getHyphenationPoints());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Reset the positions to the given position.
+ *
+ * @param resetPos the position to reset to
+ */
+ public void resetPosition(Position resetPos) {
+ if (resetPos == null) {
+ setFinished(false);
+ iReturnedLBP = 0;
+ } else {
+ if (isFinished()) {
+ // if isFinished is true, iReturned LBP == breakpoints.size()
+ // and breakpoints.get(iReturnedLBP) would generate
+ // an IndexOutOfBoundException
+ setFinished(false);
+ iReturnedLBP--;
+ }
+ while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
+ != (LineBreakPosition) resetPos) {
+ iReturnedLBP--;
+ }
+ iReturnedLBP++;
+ }
+ }
+
+ /**
+ * Add the areas with the break points.
+ *
+ * @param parentIter the iterator of break positions
+ * @param context the context for adding areas
+ */
+ public void addAreas(PositionIterator parentIter,
+ LayoutContext context) {
+ LayoutManager childLM;
+ LayoutContext lc = new LayoutContext(0);
+ int iCurrParIndex;
+ while (parentIter.hasNext()) {
+ Position pos = (Position) parentIter.next();
+ if (pos instanceof LineBreakPosition) {
+ ListIterator paragraphIterator = null;
+ KnuthElement tempElement = null;
+ // the TLM which created the last KnuthElement in this line
+ LayoutManager lastLM = null;
+
+ LineBreakPosition lbp = (LineBreakPosition) pos;
+ LineArea lineArea = new LineArea();
+ lineArea.setStartIndent(lbp.startIndent);
+ lineArea.setBPD(lbp.lineHeight);
+ lc.setBaseline(lbp.baseline);
+ lc.setLineHeight(lbp.lineHeight);
+ lc.setMiddleShift(middleShift);
+ lc.setTopShift(lbp.topShift);
+ lc.setBottomShift(lbp.bottomShift);
+
+ iCurrParIndex = lbp.iParIndex;
+ Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
+ iEndElement = lbp.getLeafPos();
+
+ // ignore the first elements added by the LineLayoutManager
+ iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
+
+ // ignore the last elements added by the LineLayoutManager
+ iEndElement -= (iEndElement == (currPar.size() - 1))
+ ? currPar.ignoreAtEnd : 0;
+
+ // ignore the last element in the line if it is a KnuthGlue object
+ paragraphIterator = currPar.listIterator(iEndElement);
+ tempElement = (KnuthElement) paragraphIterator.next();
+ if (tempElement.isGlue()) {
+ iEndElement --;
+ // this returns the same KnuthElement
+ paragraphIterator.previous();
+ tempElement = (KnuthElement) paragraphIterator.previous();
+ }
+ lastLM = tempElement.getLayoutManager();
+
+ // ignore KnuthGlue and KnuthPenalty objects
+ // at the beginning of the line
+ paragraphIterator = currPar.listIterator(iStartElement);
+ tempElement = (KnuthElement) paragraphIterator.next();
+ while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+ tempElement = (KnuthElement) paragraphIterator.next();
+ iStartElement ++;
+ }
+
+ // Add the inline areas to lineArea
+ PositionIterator inlinePosIter
+ = new KnuthPossPosIter(currPar, iStartElement,
+ iEndElement + 1);
+
+ iStartElement = lbp.getLeafPos() + 1;
+ if (iStartElement == currPar.size()) {
+ // advance to next paragraph
+ iStartElement = 0;
+ }
+
+ lc.setSpaceAdjust(lbp.dAdjust);
+ lc.setIPDAdjust(lbp.ipdAdjust);
+ lc.setLeadingSpace(new SpaceSpecifier(true));
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
+
+ /* extension (not in the XSL FO recommendation): if the left and right margins
+ have been optimized, recompute indents and / or adjust ratio, according
+ to the paragraph horizontal alignment */
+ if (false && bTextAlignment == EN_JUSTIFY) {
+ // re-compute space adjust ratio
+ int updatedDifference = context.getStackLimit().opt - lbp.lineWidth + lbp.difference;
+ double updatedRatio = 0.0;
+ if (updatedDifference > 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableStretch;
+ } else if (updatedDifference < 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableShrink;
+ }
+ lc.setIPDAdjust(updatedRatio);
+ //System.out.println("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
+ //System.out.println(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
+ } else if (false && bTextAlignment == EN_CENTER) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth) / 2;
+ lineArea.setStartIndent(updatedIndent);
+ } else if (false && bTextAlignment == EN_END) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth);
+ lineArea.setStartIndent(updatedIndent);
+ }
+
+ setCurrentArea(lineArea);
+ setChildContext(lc);
+ while ((childLM = inlinePosIter.getNextChildLM()) != null) {
+ lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
+ childLM.addAreas(inlinePosIter, lc);
+ lc.setLeadingSpace(lc.getTrailingSpace());
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ }
+
+ // when can this be null?
+ // if display-align is distribute, add space after
+ if (context.getSpaceAfter() > 0
+ && (!context.isLastArea() || parentIter.hasNext())) {
+ lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+ }
+ parentLM.addChildArea(lineArea);
+ } else {
+ // pos was the Position inside a penalty item, nothing to do
+ }
+ }
+ setCurrentArea(null); // ?? necessary
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 2004-2005 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.inline;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.layoutmgr.Position;
+
+public class LineLayoutPossibilities {
+
+ /** logger instance */
+ protected static Log log = LogFactory.getLog(LineLayoutPossibilities.class);
+
+ private class Possibility {
+ private int lineCount;
+ private double demerits;
+ private List breakPositions;
+
+ private Possibility(int lc, double dem) {
+ lineCount = lc;
+ demerits = dem;
+ breakPositions = new java.util.ArrayList(lc);
+ }
+
+ private int getLineCount() {
+ return lineCount;
+ }
+
+ private double getDemerits() {
+ return demerits;
+ }
+
+ private void addBreakPosition(Position pos) {
+ // Positions are always added with index 0 because
+ // they are created backward, from the last one to
+ // the first one
+ breakPositions.add(0, pos);
+ }
+
+ private Position getBreakPosition(int i) {
+ return (Position)breakPositions.get(i);
+ }
+ }
+
+ private List possibilitiesList;
+ private List savedPossibilities;
+ private int minimumIndex;
+ private int optimumIndex;
+ private int maximumIndex;
+ private int chosenIndex;
+ private int savedOptLineCount;
+
+ public LineLayoutPossibilities() {
+ possibilitiesList = new java.util.ArrayList();
+ savedPossibilities = new java.util.ArrayList();
+ optimumIndex = -1;
+ }
+
+ public void addPossibility(int ln, double dem) {
+ possibilitiesList.add(new Possibility(ln, dem));
+ if (possibilitiesList.size() == 1) {
+ // first Possibility added
+ minimumIndex = 0;
+ optimumIndex = 0;
+ maximumIndex = 0;
+ chosenIndex = 0;
+ } else {
+ if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) {
+ optimumIndex = possibilitiesList.size() - 1;
+ chosenIndex = optimumIndex;
+ }
+ if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineCount()) {
+ minimumIndex = possibilitiesList.size() - 1;
+ }
+ if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineCount()) {
+ maximumIndex = possibilitiesList.size() - 1;
+ }
+ }
+ }
+
+ /* save in a different array the computed Possibilities,
+ * so possibilitiesList is ready to store different Possibilities
+ */
+ public void savePossibilities(boolean bSaveOptLineCount) {
+ if (bSaveOptLineCount) {
+ savedOptLineCount = getOptLineCount();
+ } else {
+ savedOptLineCount = 0;
+ }
+ savedPossibilities = possibilitiesList;
+ possibilitiesList = new java.util.ArrayList();
+ }
+
+ /* replace the Possibilities stored in possibilitiesList with
+ * the ones stored in savedPossibilities and having the same line number
+ */
+ public void restorePossibilities() {
+ int index = 0;
+ while (savedPossibilities.size() > 0) {
+ Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0);
+ if (restoredPossibility.getLineCount() < getMinLineCount()) {
+ // if the line number of restoredPossibility is less than the minimum one,
+ // add restoredPossibility at the beginning of the list
+ possibilitiesList.add(0, restoredPossibility);
+ // update minimumIndex
+ minimumIndex = 0;
+ // shift the other indexes;
+ optimumIndex ++;
+ maximumIndex ++;
+ chosenIndex ++;
+ } else if (restoredPossibility.getLineCount() > getMaxLineCount()) {
+ // if the line number of restoredPossibility is greater than the maximum one,
+ // add restoredPossibility at the end of the list
+ possibilitiesList.add(possibilitiesList.size(), restoredPossibility);
+ // update maximumIndex
+ maximumIndex = possibilitiesList.size() - 1;
+ index = maximumIndex;
+ } else {
+ // find the index of the Possibility that will be replaced
+ while (index < maximumIndex
+ && getLineCount(index) < restoredPossibility.getLineCount()) {
+ index ++;
+ }
+ if (getLineCount(index) == restoredPossibility.getLineCount()) {
+ possibilitiesList.set(index, restoredPossibility);
+ } else {
+ // this should not happen
+ log.error("LineLayoutPossibilities restorePossibilities(),"
+ + " min= " + getMinLineCount()
+ + " max= " + getMaxLineCount()
+ + " restored= " + restoredPossibility.getLineCount());
+ return;
+ }
+ }
+ // update optimumIndex and chosenIndex
+ if (savedOptLineCount == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits()
+ || savedOptLineCount != 0 && restoredPossibility.getLineCount() == savedOptLineCount) {
+ optimumIndex = index;
+ chosenIndex = optimumIndex;
+ }
+ }
+/*LF*/ //System.out.println(">> minLineCount = " + getMinLineCount() + " optLineCount = " + getOptLineCount() + " maxLineCount() = " + getMaxLineCount());
+ }
+
+ public void addBreakPosition(Position pos, int i) {
+ ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos);
+ }
+
+ public boolean canUseMoreLines() {
+ return (getOptLineCount() < getMaxLineCount());
+ }
+
+ public boolean canUseLessLines() {
+ return (getMinLineCount() < getOptLineCount());
+ }
+
+ public int getMinLineCount() {
+ return getLineCount(minimumIndex);
+ }
+
+ public int getOptLineCount() {
+ return getLineCount(optimumIndex);
+ }
+
+ public int getMaxLineCount() {
+ return getLineCount(maximumIndex);
+ }
+
+ public int getChosenLineCount() {
+ return getLineCount(chosenIndex);
+ }
+
+ public int getLineCount(int i) {
+ return ((Possibility)possibilitiesList.get(i)).getLineCount();
+ }
+
+ public double getChosenDemerits() {
+ return getDemerits(chosenIndex);
+ }
+
+ public double getDemerits(int i) {
+ return ((Possibility)possibilitiesList.get(i)).getDemerits();
+ }
+
+ public int getPossibilitiesNumber() {
+ return possibilitiesList.size();
+ }
+
+ public Position getChosenPosition(int i) {
+ return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i);
+ }
+
+ public int applyLineCountAdjustment(int adj) {
+ if (adj >= (getMinLineCount() - getChosenLineCount())
+ && adj <= (getMaxLineCount() - getChosenLineCount())
+ && getLineCount(chosenIndex + adj) == getChosenLineCount() + adj) {
+ chosenIndex += adj;
+ log.debug("chosenLineCount= " + (getChosenLineCount() - adj) + " adjustment= " + adj
+ + " => chosenLineCount= " + getLineCount(chosenIndex));
+ return adj;
+ } else {
+ // this should not happen!
+ log.warn("Cannot apply the desired line count adjustment.");
+ return 0;
+ }
+ }
+
+ public void printAll() {
+ System.out.println("++++++++++");
+ System.out.println(" " + possibilitiesList.size() + " possibility':");
+ for (int i = 0; i < possibilitiesList.size(); i ++) {
+ System.out.println(" " + ((Possibility)possibilitiesList.get(i)).getLineCount()
+ + (i == optimumIndex ? " *" : "")
+ + (i == minimumIndex ? " -" : "")
+ + (i == maximumIndex ? " +" : ""));
+ }
+ System.out.println("++++++++++");
+ }
+}
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import org.apache.fop.fo.flow.PageNumberCitation;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.Resolvable;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.UnresolvedPageNumber;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.TraitSetter;
+
+/**
+ * LayoutManager for the fo:page-number-citation formatting object
+ */
+public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager {
+
+ private PageNumberCitation fobj;
+ private Font font;
+
+ // whether the page referred to by the citation has been resolved yet
+ private boolean resolved = false;
+
+ /**
+ * Constructor
+ *
+ * @param node the formatting object that creates this area
+ * @todo better retrieval of font info
+ */
+ public PageNumberCitationLayoutManager(PageNumberCitation node) {
+ super(node);
+ fobj = node;
+ font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+ }
+
+ public InlineArea get(LayoutContext context) {
+ curArea = getPageNumberCitationInlineArea(parentLM);
+ return curArea;
+ }
+
+ public void addAreas(PositionIterator posIter, LayoutContext context) {
+ super.addAreas(posIter, context);
+ if (!resolved) {
+ getPSLM().addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
+ }
+ }
+
+ /** @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager#getLead() */
+ public int getLead() {
+ return font.getAscender();
+ }
+
+ protected void offsetArea(InlineArea area, LayoutContext context) {
+ area.setOffset(context.getBaseline());
+ }
+
+ /**
+ * if id can be resolved then simply return a word, otherwise
+ * return a resolvable area
+ */
+ private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) {
+ PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
+ InlineArea inline = null;
+ if (page != null) {
+ String str = page.getPageNumberString();
+ // get page string from parent, build area
+ TextArea text = new TextArea();
+ inline = text;
+ int width = getStringWidth(str);
+ text.setTextArea(str);
+ inline.setIPD(width);
+
+ resolved = true;
+ } else {
+ resolved = false;
+ inline = new UnresolvedPageNumber(fobj.getRefId());
+ String str = "MMM"; // reserve three spaces for page number
+ int width = getStringWidth(str);
+ inline.setIPD(width);
+
+ }
+ inline.setBPD(font.getAscender() - font.getDescender());
+ inline.setOffset(font.getAscender());
+ inline.addTrait(Trait.FONT_NAME, font.getFontName());
+ inline.addTrait(Trait.FONT_SIZE, new Integer(font.getFontSize()));
+ TraitSetter.addTextDecoration(inline, fobj.getTextDecoration());
+
+ return inline;
+ }
+
+ /**
+ * @param str string to be measured
+ * @return width (in millipoints ??) of the string
+ */
+ private int getStringWidth(String str) {
+ int width = 0;
+ for (int count = 0; count < str.length(); count++) {
+ width += font.getCharWidth(str.charAt(count));
+ }
+ return width;
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import org.apache.fop.fo.flow.PageNumber;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.Trait;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.TraitSetter;
+
+/**
+ * LayoutManager for the fo:page-number formatting object
+ */
+public class PageNumberLayoutManager extends LeafNodeLayoutManager {
+
+ private PageNumber fobj;
+ private Font font;
+
+ /**
+ * Constructor
+ *
+ * @param node the fo:page-number formatting object that creates the area
+ * @todo better null checking of node, font
+ */
+ public PageNumberLayoutManager(PageNumber node) {
+ super(node);
+ fobj = node;
+ font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+ }
+
+ public InlineArea get(LayoutContext context) {
+ // get page string from parent, build area
+ TextArea inline = new TextArea();
+ String str = getCurrentPV().getPageNumberString();
+ int width = 0;
+ for (int count = 0; count < str.length(); count++) {
+ width += font.getCharWidth(str.charAt(count));
+ }
+ inline.setTextArea(str);
+ inline.setIPD(width);
+ inline.setBPD(font.getAscender() - font.getDescender());
+ inline.setOffset(font.getAscender());
+ inline.addTrait(Trait.FONT_NAME, font.getFontName());
+ inline.addTrait(Trait.FONT_SIZE,
+ new Integer(font.getFontSize()));
+
+ TraitSetter.addTextDecoration(inline, fobj.getTextDecoration());
+
+ return inline;
+ }
+
+
+ /** @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager#getLead() */
+ public int getLead() {
+ return font.getAscender();
+ }
+
+ protected void offsetArea(InlineArea area, LayoutContext context) {
+ area.setOffset(context.getBaseline());
+ }
+
+ protected InlineArea getEffectiveArea() {
+ TextArea baseArea = (TextArea)curArea;
+ //TODO Maybe replace that with a clone() call or better, a copy constructor
+ //TODO or even better: delay area creation until addAreas() stage
+ //TextArea is cloned because the LM is reused in static areas and the area can't be.
+ TextArea ta = new TextArea();
+ ta.setIPD(baseArea.getIPD());
+ ta.setBPD(baseArea.getBPD());
+ ta.setOffset(baseArea.getOffset());
+ ta.addTrait(Trait.FONT_NAME, font.getFontName()); //only to initialize the trait map
+ ta.getTraits().putAll(baseArea.getTraits());
+ updateContent(ta);
+ return ta;
+ }
+
+ private void updateContent(TextArea area) {
+ area.setTextArea(getCurrentPV().getPageNumberString());
+ }
+
+ protected void addId() {
+ getPSLM().addIDToPage(fobj.getId());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 1999-2005 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.inline;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.LinkedList;
+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.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+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.SpaceVal;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * LayoutManager for text (a sequence of characters) which generates one
+ * or more inline areas.
+ */
+public class TextLayoutManager extends LeafNodeLayoutManager {
+
+ /**
+ * Store information about each potential text area.
+ * Index of character which ends the area, IPD of area, including
+ * any word-space and letter-space.
+ * Number of word-spaces?
+ */
+ private class AreaInfo {
+ private short iStartIndex;
+ private short iBreakIndex;
+ private short iWScount;
+ private short iLScount;
+ private MinOptMax ipdArea;
+ private boolean bHyphenated;
+ public AreaInfo(short iSIndex, short iBIndex, short iWS, short iLS,
+ MinOptMax ipd, boolean bHyph) {
+ iStartIndex = iSIndex;
+ iBreakIndex = iBIndex;
+ iWScount = iWS;
+ iLScount = iLS;
+ ipdArea = ipd;
+ bHyphenated = bHyph;
+ }
+ }
+
+ // this class stores information about changes in vecAreaInfo
+ // which are not yet applied
+ private class PendingChange {
+ public AreaInfo ai;
+ public int index;
+
+ public PendingChange(AreaInfo ai, int index) {
+ this.ai = ai;
+ this.index = index;
+ }
+ }
+
+ // Hold all possible breaks for the text in this LM's FO.
+ private ArrayList vecAreaInfo;
+
+ /** Non-space characters on which we can end a line. */
+ private static final String BREAK_CHARS = "-/" ;
+
+ private FOText foText;
+ private char[] textArray;
+
+ private static final char NEWLINE = '\n';
+ private static final char SPACE = '\u0020'; // Normal space
+ private static final char NBSPACE = '\u00A0'; // Non-breaking space
+ private static final char LINEBREAK = '\u2028';
+ private static final char ZERO_WIDTH_SPACE = '\u200B';
+ // byte order mark
+ private static final char ZERO_WIDTH_NOBREAK_SPACE = '\uFEFF';
+
+ /** Start index of first character in this parent Area */
+ private short iAreaStart = 0;
+ /** Start index of next TextArea */
+ private short iNextStart = 0;
+ /** Size since last makeArea call, except for last break */
+ private MinOptMax ipdTotal;
+ /** Size including last break possibility returned */
+ // private MinOptMax nextIPD = new MinOptMax(0);
+ /** size of a space character (U+0020) glyph in current font */
+ private int spaceCharIPD;
+ private MinOptMax wordSpaceIPD;
+ private MinOptMax letterSpaceIPD;
+ /** size of the hyphen character glyph in current font */
+ private int hyphIPD;
+ /** 1/2 of word-spacing value */
+ private SpaceVal halfWS;
+ /** Number of space characters after previous possible break position. */
+ private int iNbSpacesPending;
+ private Font fs;
+
+ private boolean bChanged = false;
+ private int iReturnedIndex = 0;
+ private short iThisStart = 0;
+ 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 = EN_BASELINE;
+
+ /**
+ * Create a Text layout manager.
+ *
+ * @param node The FOText object to be rendered
+ */
+ public TextLayoutManager(FOText node) {
+ super();
+ foText = node;
+
+ textArray = new char[node.endIndex - node.startIndex];
+ System.arraycopy(node.ca, node.startIndex, textArray, 0,
+ node.endIndex - node.startIndex);
+
+ vecAreaInfo = new java.util.ArrayList();
+
+ fs = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo());
+
+ // With CID fonts, space isn't neccesary currentFontState.width(32)
+ spaceCharIPD = fs.getCharWidth(' ');
+ // Use hyphenationChar property
+ hyphIPD = fs.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter);
+ // Make half-space: <space> on either side of a word-space)
+ SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
+ SpaceVal ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, fs);
+ halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5),
+ ws.isConditional(), ws.isForcing(), ws.getPrecedence());
+
+ // letter space applies only to consecutive non-space characters,
+ // while word space applies to space characters;
+ // i.e. the spaces in the string "A SIMPLE TEST" are:
+ // A<<ws>>S<ls>I<ls>M<ls>P<ls>L<ls>E<<ws>>T<ls>E<ls>S<ls>T
+ // there is no letter space after the last character of a word,
+ // nor after a space character
+
+ // set letter space and word space dimension;
+ // the default value "normal" was converted into a MinOptMax value
+ // 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 EN_MIDDLE : middle = textHeight / 2 ;
+ break;
+ case EN_TOP : // fall through
+ case EN_BOTTOM : total = textHeight;
+ break;
+ case EN_BASELINE: // fall through
+ default : lead = fs.getAscender();
+ total = textHeight;
+ break;
+ }
+ }
+
+ /**
+ * Reset position for returning next BreakPossibility.
+ *
+ * @param prevPos the position to reset to
+ */
+ public void resetPosition(Position prevPos) {
+ if (prevPos != null) {
+ // ASSERT (prevPos.getLM() == this)
+ if (prevPos.getLM() != this) {
+ log.error("TextLayoutManager.resetPosition: "
+ + "LM mismatch!!!");
+ }
+ LeafPosition tbp = (LeafPosition) prevPos;
+ AreaInfo ai =
+ (AreaInfo) vecAreaInfo.get(tbp.getLeafPos());
+ if (ai.iBreakIndex != iNextStart) {
+ iNextStart = ai.iBreakIndex;
+ vecAreaInfo.ensureCapacity(tbp.getLeafPos() + 1);
+ // TODO: reset or recalculate total IPD = sum of all word IPD
+ // up to the break position
+ ipdTotal = ai.ipdArea;
+ setFinished(false);
+ }
+ } else {
+ // Reset to beginning!
+ vecAreaInfo.clear();
+ iNextStart = 0;
+ setFinished(false);
+ }
+ }
+
+ // TODO: see if we can use normal getNextBreakPoss for this with
+ // extra hyphenation information in LayoutContext
+ private boolean getHyphenIPD(HyphContext hc, MinOptMax hyphIPD) {
+ // Skip leading word-space before calculating count?
+ boolean bCanHyphenate = true;
+ int iStopIndex = iNextStart + hc.getNextHyphPoint();
+
+ if (textArray.length < iStopIndex) {
+ iStopIndex = textArray.length;
+ bCanHyphenate = false;
+ }
+ hc.updateOffset(iStopIndex - iNextStart);
+
+ for (; iNextStart < iStopIndex; iNextStart++) {
+ char c = textArray[iNextStart];
+ hyphIPD.opt += fs.getCharWidth(c);
+ // letter-space?
+ }
+ // Need to include hyphen size too, but don't count it in the
+ // stored running total, since it would be double counted
+ // with later hyphenation points
+ return bCanHyphenate;
+ }
+
+ /**
+ * Generate and add areas to parent area.
+ * This can either generate an area for each TextArea and each space, or
+ * an area containing all text with a parameter controlling the size of
+ * the word space. The latter is most efficient for PDF generation.
+ * Set size of each area.
+ * @param posIter Iterator over Position information returned
+ * by this LayoutManager.
+ * @param context LayoutContext for adjustments
+ */
+ public void addAreas(PositionIterator posIter, LayoutContext context) {
+
+ // Add word areas
+ AreaInfo ai = null;
+ int iStart = -1;
+ int iWScount = 0;
+ int iLScount = 0;
+ MinOptMax realWidth = new MinOptMax(0);
+
+ /* On first area created, add any leading space.
+ * Calculate word-space stretch value.
+ */
+ while (posIter.hasNext()) {
+ LeafPosition tbpNext = (LeafPosition) posIter.next();
+ //
+ if (tbpNext.getLeafPos() != -1) {
+ ai = (AreaInfo) vecAreaInfo.get(tbpNext.getLeafPos());
+ if (iStart == -1) {
+ iStart = ai.iStartIndex;
+ }
+ iWScount += ai.iWScount;
+ iLScount += ai.iLScount;
+ realWidth.add(ai.ipdArea);
+ }
+ }
+ if (ai == null) {
+ return;
+ }
+
+ // Make an area containing all characters between start and end.
+ InlineArea word = null;
+ int adjust = 0;
+
+ // ignore newline character
+ if (textArray[ai.iBreakIndex - 1] == NEWLINE) {
+ adjust = 1;
+ }
+ String str = new String(textArray, iStart,
+ ai.iBreakIndex - iStart - adjust);
+
+ // add hyphenation character if the last word is hyphenated
+ if (context.isLastArea() && ai.bHyphenated) {
+ str += foText.getCommonHyphenation().hyphenationCharacter;
+ realWidth.add(new MinOptMax(hyphIPD));
+ }
+
+ // Calculate adjustments
+ int iDifference = 0;
+ int iTotalAdjust = 0;
+ int iWordSpaceDim = wordSpaceIPD.opt;
+ int iLetterSpaceDim = letterSpaceIPD.opt;
+ double dIPDAdjust = context.getIPDAdjust();
+ double dSpaceAdjust = context.getSpaceAdjust(); // not used
+
+ // calculate total difference between real and available width
+ if (dIPDAdjust > 0.0) {
+ iDifference = (int) ((double) (realWidth.max - realWidth.opt)
+ * dIPDAdjust);
+ } else {
+ iDifference = (int) ((double) (realWidth.opt - realWidth.min)
+ * dIPDAdjust);
+ }
+
+ // set letter space adjustment
+ if (dIPDAdjust > 0.0) {
+ iLetterSpaceDim
+ += (int) ((double) (letterSpaceIPD.max - letterSpaceIPD.opt)
+ * dIPDAdjust);
+ } else {
+ iLetterSpaceDim
+ += (int) ((double) (letterSpaceIPD.opt - letterSpaceIPD.min)
+ * dIPDAdjust);
+ }
+ iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount;
+
+ // set word space adjustment
+ //
+ if (iWScount > 0) {
+ iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount);
+ } else {
+ // there are no word spaces in this area
+ }
+ iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
+
+ TextArea t = createTextArea(str, realWidth.opt + iTotalAdjust,
+ context);
+
+ // iWordSpaceDim is computed in relation to wordSpaceIPD.opt
+ // but the renderer needs to know the adjustment in relation
+ // to the size of the space character in the current font;
+ // moreover, the pdf renderer adds the character spacing even to
+ // the last character of a word and to space characters: in order
+ // to avoid this, we must subtract the letter space width twice;
+ // the renderer will compute the space width as:
+ // space width =
+ // = "normal" space width + letterSpaceAdjust + wordSpaceAdjust
+ // = spaceCharIPD + letterSpaceAdjust +
+ // + (iWordSpaceDim - spaceCharIPD - 2 * letterSpaceAdjust)
+ // = iWordSpaceDim - letterSpaceAdjust
+ t.setTextLetterSpaceAdjust(iLetterSpaceDim);
+ t.setTextWordSpaceAdjust(iWordSpaceDim - spaceCharIPD
+ - 2 * t.getTextLetterSpaceAdjust());
+
+ word = t;
+ if (word != null) {
+ parentLM.addChildArea(word);
+ }
+ }
+
+ /**
+ * Create an inline word area.
+ * This creates a TextArea and sets up the various attributes.
+ *
+ * @param str the string for the TextArea
+ * @param width the width that the TextArea uses
+ * @param base the baseline position
+ * @return the new word area
+ */
+ protected TextArea createTextArea(String str, int width, LayoutContext context) {
+ TextArea textArea = new TextArea();
+ textArea.setIPD(width);
+ textArea.setBPD(fs.getAscender() - fs.getDescender());
+ int bpd = textArea.getBPD();
+ switch (verticalAlignment) {
+ case EN_MIDDLE:
+ textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
+ break;
+ case EN_TOP:
+ textArea.setOffset(context.getTopBaseline() + fs.getAscender());
+ break;
+ case EN_BOTTOM:
+ textArea.setOffset(context.getBottomBaseline() - bpd + fs.getAscender());
+ break;
+ case EN_BASELINE:
+ default:
+ textArea.setOffset(context.getBaseline());
+ break;
+ }
+
+ textArea.setTextArea(str);
+ textArea.addTrait(Trait.FONT_NAME, fs.getFontName());
+ textArea.addTrait(Trait.FONT_SIZE, new Integer(fs.getFontSize()));
+ textArea.addTrait(Trait.COLOR, foText.getColor());
+
+ TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
+
+ 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
+ || 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 EN_CENTER :
+ vecAreaInfo.add
+ (new AreaInfo(iNextStart, (short) (iNextStart + 1),
+ (short) 1, (short) 0,
+ wordSpaceIPD, false));
+ returnList.add
+ (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 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthInlineBox(0, 0, 0, 0,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ new LeafPosition(this, -1), true));
+ iNextStart ++;
+ break;
+
+ case EN_START : // fall through
+ case EN_END :
+ vecAreaInfo.add
+ (new AreaInfo(iNextStart, (short) (iNextStart + 1),
+ (short) 1, (short) 0,
+ wordSpaceIPD, false));
+ returnList.add
+ (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 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,
+ - 3 * wordSpaceIPD.opt, 0,
+ new LeafPosition(this, -1), true));
+ iNextStart ++;
+ break;
+
+ case EN_JUSTIFY:
+ vecAreaInfo.add
+ (new AreaInfo(iNextStart, (short) (iNextStart + 1),
+ (short) 1, (short) 0,
+ wordSpaceIPD, false));
+ returnList.add
+ (new KnuthGlue(wordSpaceIPD.opt,
+ wordSpaceIPD.max - wordSpaceIPD.opt,
+ wordSpaceIPD.opt - wordSpaceIPD.min,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ iNextStart ++;
+ break;
+
+ default:
+ vecAreaInfo.add
+ (new AreaInfo(iNextStart, (short) (iNextStart + 1),
+ (short) 1, (short) 0,
+ wordSpaceIPD, false));
+ returnList.add
+ (new KnuthGlue(wordSpaceIPD.opt,
+ wordSpaceIPD.max - wordSpaceIPD.opt, 0,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ iNextStart ++;
+ }
+ } else if (textArray[iNextStart] == NBSPACE) {
+ // non breaking space
+ vecAreaInfo.add
+ (new AreaInfo(iNextStart, (short) (iNextStart + 1),
+ (short) 1, (short) 0,
+ wordSpaceIPD, false));
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ returnList.add
+ (new KnuthGlue(wordSpaceIPD.opt,
+ wordSpaceIPD.max - wordSpaceIPD.opt,
+ wordSpaceIPD.opt - wordSpaceIPD.min,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ iNextStart ++;
+ } else if (textArray[iNextStart] == NEWLINE) {
+ // linefeed; this can happen when linefeed-treatment="preserve"
+ // add a penalty item to the list and return
+ returnList.add
+ (new KnuthPenalty(0, -KnuthElement.INFINITE,
+ false, null, false));
+ iNextStart ++;
+ return returnList;
+ } else {
+ // the beginning of a word
+ iThisStart = iNextStart;
+ iTempStart = iNextStart;
+ MinOptMax wordIPD = new MinOptMax(0);
+ for (; iTempStart < textArray.length
+ && textArray[iTempStart] != SPACE
+ && textArray[iTempStart] != NBSPACE
+ && textArray[iTempStart] != NEWLINE
+ && !(iTempStart > iNextStart
+ && alignment == EN_JUSTIFY
+ && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
+ iTempStart++) {
+ wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
+ }
+ int iLetterSpaces = iTempStart - iThisStart - 1;
+ wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
+ vecAreaInfo.add
+ (new AreaInfo(iThisStart, iTempStart, (short) 0,
+ (short) iLetterSpaces,
+ wordIPD, false));
+ if (letterSpaceIPD.min == letterSpaceIPD.max) {
+ // constant letter space; simply return a box
+ // whose width includes letter spaces
+ returnList.add
+ (new KnuthInlineBox(wordIPD.opt, lead, total, middle,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ } else {
+ // adjustable letter space;
+ // some other KnuthElements are needed
+ returnList.add
+ (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt,
+ lead, total, middle,
+ new LeafPosition(this, vecAreaInfo.size() - 1), false));
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt,
+ iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt),
+ iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min),
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthInlineBox(0, lead, total, middle,
+ new LeafPosition(this, -1), true));
+ }
+ // if the last character is '-' or '/', it could be used as a line end;
+ // add a flagged penalty element and glue element representing a suppressible
+ // letter space if the next character is not a space
+ if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
+ && iTempStart < textArray.length
+ && textArray[iTempStart] != SPACE
+ && textArray[iTempStart] != NBSPACE) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ returnList.add
+ (new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ // update the information in the AreaInfo, adding one more letter space
+ AreaInfo ai = (AreaInfo) vecAreaInfo.get(vecAreaInfo.size() - 1);
+ ai.iLScount ++;
+ }
+ iNextStart = iTempStart;
+ }
+ } // end of while
+ setFinished(true);
+ if (returnList.size() > 0) {
+ return returnList;
+ } else {
+ return null;
+ }
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ // old list contains only a box, or the sequence: box penalty glue box;
+ // look at the Position stored in the first element in oldList
+ // which is always a box
+ ListIterator oldListIterator = oldList.listIterator();
+ LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition();
+ AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
+ ai.iLScount ++;
+ ai.ipdArea.add(letterSpaceIPD);
+ if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
+ // the last character could be used as a line break
+ // append new elements to oldList
+ oldListIterator = oldList.listIterator(oldList.size());
+ oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
+ // constant letter space: replace the box
+ oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle, pos, false));
+ } else {
+ // adjustable letter space: replace the glue
+ oldListIterator.next(); // this would return the penalty element
+ oldListIterator.next(); // this would return the glue element
+ oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
+ ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
+ ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
+ new LeafPosition(this, -1), true));
+ }
+ return oldList;
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ AreaInfo ai
+ = (AreaInfo) vecAreaInfo.get(((LeafPosition) pos).getLeafPos());
+ int iStartIndex = ai.iStartIndex;
+ int iStopIndex;
+ boolean bNothingChanged = true;
+
+ while (iStartIndex < ai.iBreakIndex) {
+ MinOptMax newIPD = new MinOptMax(0);
+ boolean bHyphenFollows;
+
+ if (hc.hasMoreHyphPoints()
+ && (iStopIndex = iStartIndex + hc.getNextHyphPoint())
+ <= ai.iBreakIndex) {
+ // iStopIndex is the index of the first character
+ // after a hyphenation point
+ bHyphenFollows = true;
+ } else {
+ // there are no more hyphenation points,
+ // or the next one is after ai.iBreakIndex
+ bHyphenFollows = false;
+ iStopIndex = ai.iBreakIndex;
+ }
+
+ hc.updateOffset(iStopIndex - iStartIndex);
+
+ for (int i = iStartIndex; i < iStopIndex; i++) {
+ char c = textArray[i];
+ newIPD.add(new MinOptMax(fs.getCharWidth(c)));
+ }
+ // add letter spaces
+ boolean bIsWordEnd
+ = iStopIndex == ai.iBreakIndex
+ && ai.iLScount < (ai.iBreakIndex - ai.iStartIndex);
+ newIPD.add(MinOptMax.multiply(letterSpaceIPD,
+ (bIsWordEnd
+ ? (iStopIndex - iStartIndex - 1)
+ : (iStopIndex - iStartIndex))));
+
+ if (!(bNothingChanged
+ && iStopIndex == ai.iBreakIndex
+ && bHyphenFollows == false)) {
+ // the new AreaInfo object is not equal to the old one
+ if (changeList == null) {
+ changeList = new LinkedList();
+ }
+ changeList.add
+ (new PendingChange
+ (new AreaInfo((short) iStartIndex, (short) iStopIndex,
+ (short) 0,
+ (short) (bIsWordEnd
+ ? (iStopIndex - iStartIndex - 1)
+ : (iStopIndex - iStartIndex)),
+ newIPD, bHyphenFollows),
+ ((LeafPosition) pos).getLeafPos()));
+ bNothingChanged = false;
+ }
+ iStartIndex = iStopIndex;
+ }
+ if (!bChanged && !bNothingChanged) {
+ bChanged = true;
+ }
+ }
+
+ public boolean applyChanges(List oldList) {
+ setFinished(false);
+
+ if (changeList != null) {
+ int iAddedAI = 0;
+ int iRemovedAI = 0;
+ int iOldIndex = -1;
+ PendingChange currChange = null;
+ ListIterator changeListIterator = changeList.listIterator();
+ while (changeListIterator.hasNext()) {
+ currChange = (PendingChange) changeListIterator.next();
+ if (currChange.index != iOldIndex) {
+ iRemovedAI ++;
+ iAddedAI ++;
+ iOldIndex = currChange.index;
+ vecAreaInfo.remove(currChange.index + iAddedAI - iRemovedAI);
+ vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
+ currChange.ai);
+ } else {
+ iAddedAI ++;
+ vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
+ currChange.ai);
+ }
+ }
+ changeList.clear();
+ }
+
+ iReturnedIndex = 0;
+ return bChanged;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList,
+ /*int flaggedPenalty,*/
+ int alignment) {
+ if (isFinished()) {
+ return null;
+ }
+
+ LinkedList returnList = new LinkedList();
+
+ while (iReturnedIndex < vecAreaInfo.size()) {
+ AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
+ if (ai.iWScount == 0) {
+ // ai refers either to a word or a word fragment
+
+ // if the last character is '-' or '/' and the next character is not a space
+ // one of the letter spaces must be represented using a penalty and a glue,
+ // and its width must be subtracted
+ if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+ && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+ ai.ipdArea.add(new MinOptMax(-letterSpaceIPD.min, -letterSpaceIPD.opt, -letterSpaceIPD.max));
+ }
+ if (letterSpaceIPD.min == letterSpaceIPD.max) {
+ returnList.add
+ (new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
+ new LeafPosition(this, iReturnedIndex), false));
+ } else {
+ returnList.add
+ (new KnuthInlineBox(ai.ipdArea.opt
+ - ai.iLScount * letterSpaceIPD.opt,
+ lead, total, middle,
+ new LeafPosition(this, iReturnedIndex), false));
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
+ ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
+ ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthInlineBox(0, 0, 0, 0,
+ new LeafPosition(this, -1), true));
+ }
+ if (ai.bHyphenated) {
+ returnList.add
+ (new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ }
+ // if the last character is '-' or '/', it could be used as a line end;
+ // add a flagged penalty element and a glue element representing a suppressible
+ // letter space if the next character is not a space
+ if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+ && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ returnList.add
+ (new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ }
+ iReturnedIndex ++;
+ } else {
+ // ai refers to a space
+ if (textArray[ai.iStartIndex] == NBSPACE) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1),
+ false));
+ }
+ switch (alignment) {
+ case EN_CENTER :
+ returnList.add
+ (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 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthInlineBox(0, 0, 0, 0,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), true));
+ returnList.add
+ (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ new LeafPosition(this, -1), true));
+ iReturnedIndex ++;
+ break;
+ case EN_START : // fall through
+ case EN_END :
+ returnList.add
+ (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,
+ - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ new LeafPosition(this, -1), true));
+ iReturnedIndex ++;
+ break;
+ case EN_JUSTIFY:
+ returnList.add
+ (new KnuthGlue(wordSpaceIPD.opt,
+ wordSpaceIPD.max - wordSpaceIPD.opt,
+ wordSpaceIPD.opt - wordSpaceIPD.min,
+ new LeafPosition(this, iReturnedIndex), false));
+ iReturnedIndex ++;
+ break;
+
+ default:
+ returnList.add
+ (new KnuthGlue(wordSpaceIPD.opt,
+ wordSpaceIPD.max - wordSpaceIPD.opt, 0,
+ new LeafPosition(this, iReturnedIndex), false));
+ iReturnedIndex ++;
+ }
+ }
+ } // end of while
+ setFinished(true);
+ return returnList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ int iLeafValue = ((LeafPosition) pos).getLeafPos();
+ if (iLeafValue != -1) {
+ AreaInfo ai = (AreaInfo) vecAreaInfo.get(iLeafValue);
+ sbChars.append(new String(textArray, ai.iStartIndex,
+ ai.iBreakIndex - ai.iStartIndex));
+ }
+ }
+}
+
--- /dev/null
+/*\r
+ * Copyright 2005 The Apache Software Foundation.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.layoutmgr.inline;\r
+\r
+import org.apache.fop.area.inline.InlineArea;\r
+import org.apache.fop.fo.flow.Wrapper;\r
+import org.apache.fop.layoutmgr.LayoutContext;\r
+\r
+/**\r
+ * This is the layout manager for the fo:wrapper formatting object.\r
+ */\r
+public class WrapperLayoutManager extends LeafNodeLayoutManager {\r
+ \r
+ private Wrapper fobj;\r
+\r
+ /**\r
+ * Creates a new LM for fo:wrapper.\r
+ * @param node the fo:wrapper\r
+ */\r
+ public WrapperLayoutManager(Wrapper node) {\r
+ super(node);\r
+ fobj = node;\r
+ }\r
+\r
+ /** @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager */\r
+ public InlineArea get(LayoutContext context) {\r
+ //Create a zero-width, zero-height dummy area so this node can \r
+ //participate in the ID handling. Otherwise, addId() wouldn't \r
+ //be called.\r
+ InlineArea area = new InlineArea();\r
+ return area;\r
+ }\r
+ \r
+ /** @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager#addId() */\r
+ protected void addId() {\r
+ getPSLM().addIDToPage(fobj.getId());\r
+ }\r
+ \r
+}\r