You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by ar...@apache.org on 2001/02/11 06:37:22 UTC
cvs commit: xml-fop/jpfop-0.17.0/src/org/apache/fop/layout LineArea.java
arved 01/02/10 21:37:22
Added: jpfop-0.17.0/src/org/apache/fop/layout LineArea.java
Log:
Modified JPFOP line area class
Revision Changes Path
1.1 xml-fop/jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java
Index: LineArea.java
===================================================================
/*-- $Id: LineArea.java,v 1.1 2001/02/11 05:37:21 arved Exp $ --
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "FOP" and "Apache Software Foundation" must not be used to
endorse or promote products derived from this software without prior
written permission. For written permission, please contact
apache@apache.org.
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation and was originally created by
James Tauber <jt...@jtauber.com>. For more information on the Apache
Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.fop.layout;
//fop
import org.apache.fop.render.Renderer;
import org.apache.fop.messaging.MessageHandler;
import org.apache.fop.layout.inline.*;
import org.apache.fop.datatypes.IDNode;
import org.apache.fop.fo.properties.WrapOption;
import org.apache.fop.fo.properties.WhiteSpaceCollapse;
import org.apache.fop.fo.properties.TextAlign;
import org.apache.fop.fo.properties.TextAlignLast;
import org.apache.fop.fo.properties.LeaderPattern;
import org.apache.fop.fo.properties.Hyphenate;
import org.apache.fop.fo.properties.CountryMaker;
import org.apache.fop.fo.properties.LanguageMaker;
import org.apache.fop.fo.properties.LeaderAlignment;
import org.apache.fop.fo.properties.VerticalAlign;
import org.apache.fop.layout.hyphenation.Hyphenation;
import org.apache.fop.layout.hyphenation.Hyphenator;
//java
import java.util.Vector;
import java.util.Enumeration;
import java.awt.Rectangle;
public class LineArea extends Area {
protected int lineHeight;
protected int halfLeading;
protected int nominalFontSize;
protected int nominalGlyphHeight;
protected int allocationHeight;
protected int startIndent;
protected int endIndent;
private int placementOffset;
private FontState currentFontState; // not the nominal, which is
// in this.fontState
private float red, green, blue;
private int wrapOption;
private int whiteSpaceCollapse;
int vAlign;
/*hyphenation*/
protected int hyphenate;
protected char hyphenationChar;
protected int hyphenationPushCharacterCount;
protected int hyphenationRemainCharacterCount;
protected String language;
protected String country;
/* the width of text that has definitely made it into the line
area */
protected int finalWidth = 0;
/* the position to shift a link rectangle in order to compensate for links embedded within a word*/
protected int embeddedLinkStart = 0;
/* the width of the current word so far */
// protected int wordWidth = 0;
/* values that prev (below) may take */
protected static final int NOTHING = 0;
protected static final int WHITESPACE = 1;
protected static final int TEXT = 2;
/* the character type of the previous character */
protected int prev = NOTHING;
/* the position in data[] of the start of the current word */
// protected int wordStart;
/* the length (in characters) of the current word */
// protected int wordLength = 0;
/* width of spaces before current word */
protected int spaceWidth = 0;
/* the inline areas that have not yet been added to the line
because subsequent characters to come (in a different addText)
may be part of the same word */
protected Vector pendingAreas = new Vector();
/* the width of the pendingAreas */
protected int pendingWidth = 0;
/* text-decoration of the previous text */
protected boolean prevUlState = false;
protected boolean prevOlState = false;
protected boolean prevLTState = false;
public LineArea(FontState fontState, int lineHeight,
int halfLeading, int allocationWidth, int startIndent,
int endIndent, LineArea prevLineArea) {
super(fontState);
this.currentFontState = fontState;
this.lineHeight = lineHeight;
this.nominalFontSize = fontState.getFontSize();
this.nominalGlyphHeight =
fontState.getAscender() - fontState.getDescender();
this.placementOffset = fontState.getAscender();
this.contentRectangleWidth =
allocationWidth - startIndent - endIndent;
this.fontState = fontState;
this.allocationHeight = this.nominalGlyphHeight;
this.halfLeading = this.lineHeight - this.allocationHeight;
this.startIndent = startIndent;
this.endIndent = endIndent;
if (prevLineArea != null) {
Enumeration e = prevLineArea.pendingAreas.elements();
while (e.hasMoreElements()) {
pendingAreas.addElement(e.nextElement());
}
pendingWidth = prevLineArea.getPendingWidth();
}
}
public void render(Renderer renderer) {
renderer.renderLineArea(this);
}
public int addPageNumberCitation(String refid, LinkSet ls) {
/* We should add code here to handle the case where the page number doesn't fit on the current line
*/
//Space must be alloted to the page number, so currently we give it 3 spaces
int width = currentFontState.width(32) * 3;
PageNumberInlineArea pia =
new PageNumberInlineArea(currentFontState, this.red,
this.green, this.blue, refid, width);
pia.setYOffset(placementOffset);
pendingAreas.addElement(pia);
pendingWidth += width;
prev = TEXT;
return -1;
}
/**
* adds text to line area
*
* @return int character position
*/
public int addText(char odata[], int start, int end, LinkSet ls,
TextState textState) {
// this prevents an array index out of bounds
// which occurs when some text is laid out again.
if(start == -1) return -1;
boolean overrun = false;
//ForJAPANIZE
boolean isCJKChar = false;
int wordStart = start;
int wordLength = 0;
int wordWidth = 0;
char[] data = new char[odata.length];
for (int count = 0; count < odata.length; count++) {
data[count] = odata[count];
}
/* iterate over each character */
for (int i = start; i < end; i++) {
int charWidth;
/* get the character */
char c = data[i];
if (c > 127) {
/* this class shouldn't be hard coded */
char d = org.apache.fop.render.pdf.CodePointMapping.map[c];
if (d != 0) {
c = data[i] = d;
} else {
// ForJAPANIZE
// MessageHandler.error("ch" + (int) c + "?");
// c = data[i] = '#';
isCJKChar = true;
}
}
charWidth = currentFontState.width(c);
if ((c == ' ') || (c == '\n') || (c == '\r') ||
(c == '\t')) { // whitespace
if (prev == WHITESPACE) {
// if current & previous are WHITESPACE
if (this.whiteSpaceCollapse ==
WhiteSpaceCollapse.FALSE) {
if (c == ' ') {
spaceWidth += currentFontState.width(32);
} else if (c == '\n') {
// force line break
return i;
} else if (c == '\t') {
spaceWidth += 8 * currentFontState.width(32);
}
} // else ignore it
} else if (prev == TEXT) {
// if current is WHITESPACE and previous TEXT
// the current word made it, so
// add the space before the current word (if there
// was some)
if (spaceWidth > 0) {
InlineSpace is = new InlineSpace(spaceWidth);
if (prevUlState) {
is.setUnderlined(textState.getUnderlined());
}
if (prevOlState) {
is.setOverlined(textState.getOverlined());
}
if (prevLTState) {
is.setLineThrough(textState.getLineThrough());
}
addChild(is);
//ForJAPANIZE
isCJKChar = false;
finalWidth += spaceWidth;
spaceWidth = 0;
}
// add any pending areas
Enumeration e = pendingAreas.elements();
while (e.hasMoreElements()) {
Box box = (Box) e.nextElement();
if (box instanceof InlineArea) {
if (ls != null) {
Rectangle lr = new Rectangle(finalWidth, 0,
((InlineArea) box).
getContentWidth(),
fontState.getFontSize());
ls.addRect(lr, this);
}
}
addChild(box);
//ForJAPANIZE
isCJKChar = false;
}
finalWidth += pendingWidth;
// reset pending areas array
pendingWidth = 0;
pendingAreas = new Vector();
// add the current word
if (wordLength > 0) {
WordArea ia = new WordArea(currentFontState,
this.red, this.green, this.blue,
new String(data, wordStart,
wordLength), wordWidth);
ia.setYOffset(placementOffset);
ia.setUnderlined(textState.getUnderlined());
prevUlState = textState.getUnderlined();
ia.setOverlined(textState.getOverlined());
prevOlState = textState.getOverlined();
ia.setLineThrough(textState.getLineThrough());
prevLTState = textState.getLineThrough();
ia.setVerticalAlign(vAlign);
addChild(ia);
//ForJAPANIZE
isCJKChar = false;
if (ls != null) {
Rectangle lr = new Rectangle(finalWidth, 0,
ia.getContentWidth(),
fontState.getFontSize());
ls.addRect(lr, this);
}
finalWidth += wordWidth;
// reset word width
wordWidth = 0;
}
// deal with this new whitespace following the
// word we just added
prev = WHITESPACE;
embeddedLinkStart = 0; //reset embeddedLinkStart since a space was encountered
spaceWidth = currentFontState.width(32);
/*
here is the place for space-treatment value 'ignore':
if (this.spaceTreatment ==
SpaceTreatment.IGNORE) {
// do nothing
} else {
spaceWidth = currentFontState.width(32);
}
*/
if (this.whiteSpaceCollapse ==
WhiteSpaceCollapse.FALSE) {
if (c == '\n') {
// force a line break
return i;
} else if (c == '\t') {
spaceWidth = currentFontState.width(32);
}
}
} else {
// if current is WHITESPACE and no previous
if (this.whiteSpaceCollapse ==
WhiteSpaceCollapse.FALSE) {
prev = WHITESPACE;
spaceWidth = currentFontState.width(32);
} else {
// skip over it
start++;
}
}
} else { // current is TEXT
if (prev == WHITESPACE) {
// if current is TEXT and previous WHITESPACE
wordWidth = charWidth;
if ((finalWidth + spaceWidth + wordWidth) >
this.getContentWidth()) {
if (overrun)
MessageHandler.error(">");
if (this.wrapOption == WrapOption.WRAP)
return i;
}
prev = TEXT;
wordStart = i;
wordLength = 1;
} else if (prev == TEXT) {
wordLength++;
wordWidth += charWidth;
} else { // nothing previous
prev = TEXT;
wordStart = i;
wordLength = 1;
wordWidth = charWidth;
}
if ((finalWidth + spaceWidth + pendingWidth +
wordWidth) > this.getContentWidth()) {
//ForJAPANIZE
if ( isCJKChar && this.wrapOption == WrapOption.WRAP &&
charWidth <= this.getContentWidth() ) {
// The word does not separated WHITSPACE in CJK language .
// So, if the word is overran, it is nessesaly to addChild .
WordArea ia = new WordArea(currentFontState,
this.red, this.green, this.blue,
new String(data, wordStart,wordLength-1), wordWidth-charWidth);
ia.setUnderlined(textState.getUnderlined());
addChild(ia);
isCJKChar = false;
if (ls != null) {
Rectangle lr = new Rectangle(finalWidth, 0,
ia.getContentWidth(),fontState.getFontSize());
ls.addRect(lr, this);
}
finalWidth += wordWidth;
return i;
}
// BREAK MID WORD
if (wordStart == start) { // if couldn't even fit
// first word
overrun = true;
// if not at start of line, return word start
// to try again on a new line
if (finalWidth > 0) {
return wordStart;
}
} else if (this.wrapOption == WrapOption.WRAP) {
if (this.hyphenate == Hyphenate.TRUE) {
return this.doHyphenation(data,i,wordStart,this.getContentWidth() - (finalWidth + spaceWidth + pendingWidth));
} else {
return wordStart;
}
}
}
}
} // end of iteration over text
if (prev == TEXT) {
if (spaceWidth > 0) {
InlineSpace pis = new InlineSpace(spaceWidth);
if (prevUlState) {
pis.setUnderlined(textState.getUnderlined());
}
if (prevOlState) {
pis.setOverlined(textState.getOverlined());
}
if (prevLTState) {
pis.setLineThrough(textState.getLineThrough());
}
pendingAreas.addElement(pis);
pendingWidth += spaceWidth;
spaceWidth = 0;
}
WordArea pia = new WordArea(currentFontState, this.red,
this.green, this.blue,
new String(data, wordStart, wordLength), wordWidth);
pia.setYOffset(placementOffset);
pia.setUnderlined(textState.getUnderlined());
prevUlState = textState.getUnderlined();
pia.setOverlined(textState.getOverlined());
prevOlState = textState.getOverlined();
pia.setLineThrough(textState.getLineThrough());
prevLTState = textState.getLineThrough();
pia.setVerticalAlign(vAlign);
if (ls != null) {
Rectangle lr = new Rectangle(finalWidth + spaceWidth +
embeddedLinkStart, spaceWidth,
pia.getContentWidth(), fontState.getFontSize());
ls.addRect(lr, this);
}
embeddedLinkStart += wordWidth;
pendingAreas.addElement(pia);
pendingWidth += wordWidth;
wordWidth = 0;
}
if (overrun)
MessageHandler.error(">");
return -1;
}
/**
* adds a Leader; actually the method receives the leader properties
* and creates a leader area or an inline area which is appended to
* the children of the containing line area. <br>
* leader pattern use-content is not implemented.
*/
public void addLeader(int leaderPattern, int leaderLengthMinimum,
int leaderLengthOptimum, int leaderLengthMaximum,
int ruleStyle, int ruleThickness, int leaderPatternWidth,
int leaderAlignment) {
WordArea leaderPatternArea;
int leaderLength = 0;
int remainingWidth =
this.getContentWidth() - this.getCurrentXPosition();
/** checks whether leaderLenghtOptimum fits into rest of line;
* should never overflow, as it has been checked already in BlockArea
* first check: use remaining width if it smaller than optimum oder maximum
* */
if ((remainingWidth <= leaderLengthOptimum) || (remainingWidth <= leaderLengthMaximum)) {
leaderLength = remainingWidth;
} else if ((remainingWidth > leaderLengthOptimum) && ( remainingWidth > leaderLengthMaximum)) {
leaderLength = leaderLengthMaximum;
} else if ((leaderLengthOptimum > leaderLengthMaximum) && (leaderLengthOptimum < remainingWidth)) {
leaderLength = leaderLengthOptimum;
}
switch (leaderPattern) {
case LeaderPattern.SPACE:
//whitespace setting must be false for this
int whiteSpaceSetting = this.whiteSpaceCollapse;
this.changeWhiteSpaceCollapse(WhiteSpaceCollapse.FALSE);
pendingAreas.addElement(
this.buildSimpleLeader(32, leaderLength));
this.changeWhiteSpaceCollapse(whiteSpaceSetting);
break;
case LeaderPattern.RULE:
LeaderArea leaderArea =
new LeaderArea(fontState, red, green, blue, "",
leaderLength, leaderPattern, ruleThickness,
ruleStyle);
leaderArea.setYOffset(placementOffset);
pendingAreas.addElement(leaderArea);
break;
case LeaderPattern.DOTS:
//if the width of a dot is larger than leader-pattern-width
//ignore this property
if (leaderPatternWidth < this.currentFontState.width(46)) {
leaderPatternWidth = 0;
}
//if value of leader-pattern-width is 'use-font-metrics' (0)
if (leaderPatternWidth == 0) {
pendingAreas.addElement(
this.buildSimpleLeader(46, leaderLength));
} else {
//if leader-alignment is used, calculate space to insert before leader
//so that all dots will be parallel.
if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
int spaceBeforeLeader = this.getLeaderAlignIndent(
leaderLength, leaderPatternWidth);
//appending indent space leader-alignment
//setting InlineSpace to false, so it is not used in line justification
if (spaceBeforeLeader != 0) {
pendingAreas.addElement(
new InlineSpace(spaceBeforeLeader,
false));
pendingWidth += spaceBeforeLeader;
//shorten leaderLength, otherwise - in case of
//leaderLength=remaining length - it will cut off the end of
//leaderlength
leaderLength -= spaceBeforeLeader;
}
}
// calculate the space to insert between the dots and create a
//inline area with this width
InlineSpace spaceBetweenDots =
new InlineSpace(leaderPatternWidth -
this.currentFontState.width(46), false);
leaderPatternArea =
new WordArea(currentFontState, this.red,
this.green, this.blue, new String ("."),
this.currentFontState.width(46));
leaderPatternArea.setYOffset(placementOffset);
int dotsFactor = (int) Math.floor (
((double) leaderLength) /
((double) leaderPatternWidth));
//add combination of dot + space to fill leader
//is there a way to do this in a more effective way?
for (int i = 0; i < dotsFactor; i++) {
pendingAreas.addElement(leaderPatternArea);
pendingAreas.addElement(spaceBetweenDots);
}
//append at the end some space to fill up to leader length
pendingAreas.addElement( new InlineSpace(leaderLength -
dotsFactor * leaderPatternWidth));
}
break;
//leader pattern use-content not implemented.
case LeaderPattern.USECONTENT:
MessageHandler.errorln(
"leader-pattern=\"use-content\" not " + "supported by this version of Fop");
return;
}
//adds leader length to length of pending inline areas
pendingWidth += leaderLength;
//sets prev to TEXT and makes so sure, that also blocks only
//containing leaders are processed
prev = TEXT;
}
/**
* adds pending inline areas to the line area
* normally done, when the line area is filled and
* added as child to the parent block area
*/
public void addPending() {
if (spaceWidth > 0) {
addChild(new InlineSpace(spaceWidth));
finalWidth += spaceWidth;
spaceWidth = 0;
}
Enumeration e = pendingAreas.elements();
while (e.hasMoreElements()) {
Box box = (Box) e.nextElement();
addChild(box);
}
finalWidth += pendingWidth;
// reset pending areas array
pendingWidth = 0;
pendingAreas = new Vector();
}
/**
* aligns line area
*
*/
public void align(int type) {
int padding = 0;
switch (type) {
case TextAlign.START: // left
padding = this.getContentWidth() - finalWidth;
endIndent += padding;
break;
case TextAlign.END: // right
padding = this.getContentWidth() - finalWidth;
startIndent += padding;
break;
case TextAlign.CENTER: // center
padding = (this.getContentWidth() - finalWidth) / 2;
startIndent += padding;
endIndent += padding;
break;
case TextAlign.JUSTIFY: // justify
Vector spaceList = new Vector();
int spaceCount = 0;
Enumeration e = children.elements();
while (e.hasMoreElements()) {
Box b = (Box) e.nextElement();
if (b instanceof InlineSpace) {
InlineSpace space = (InlineSpace) b;
if (space.getResizeable()) {
spaceList.addElement(space);
spaceCount++;
}
}
}
if (spaceCount > 0) {
padding = (this.getContentWidth() - finalWidth) /
spaceCount;
} else { // no spaces
padding = 0;
}
Enumeration f = spaceList.elements();
while (f.hasMoreElements()) {
InlineSpace space2 = (InlineSpace) f.nextElement();
int i = space2.getSize();
space2.setSize(i + padding);
}
}
}
/**
* Balance (vertically) the inline areas within this line.
*/
public void verticalAlign()
{
int superHeight = -this.placementOffset;
int maxHeight = this.allocationHeight;
Enumeration e = children.elements();
while (e.hasMoreElements()) {
Box b = (Box) e.nextElement();
if(b instanceof InlineArea) {
InlineArea ia = (InlineArea)b;
if(ia instanceof WordArea) {
ia.setYOffset(placementOffset);
}
if(ia.getHeight() > maxHeight) {
maxHeight = ia.getHeight();
}
int vert = ia.getVerticalAlign();
if(vert == VerticalAlign.SUPER) {
int fh = fontState.getAscender();
ia.setYOffset((int)(placementOffset - (fh / 3.0)));
} else if(vert == VerticalAlign.SUB) {
int fh = fontState.getAscender();
ia.setYOffset((int)(placementOffset + (fh / 3.0)));
}
} else {
}
}
// adjust the height of this line to the
// resulting alignment height.
this.allocationHeight = maxHeight;
}
public void changeColor(float red, float green, float blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
public void changeFont(FontState fontState) {
this.currentFontState = fontState;
}
public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) {
this.whiteSpaceCollapse = whiteSpaceCollapse;
}
public void changeWrapOption(int wrapOption) {
this.wrapOption = wrapOption;
}
public void changeVerticalAlign(int vAlign) {
this.vAlign = vAlign;
}
public int getEndIndent() {
return endIndent;
}
public int getHeight() {
return this.allocationHeight;
}
public int getPlacementOffset() {
return this.placementOffset;
}
public int getStartIndent() {
return startIndent;
}
public boolean isEmpty() {
return !(pendingAreas.size() > 0 || children.size() > 0);
// return (prev == NOTHING);
}
public Vector getPendingAreas() {
return pendingAreas;
}
public int getPendingWidth() {
return pendingWidth;
}
public void setPendingAreas(Vector areas) {
pendingAreas = areas;
}
public void setPendingWidth(int width) {
pendingWidth = width;
}
/**
* sets hyphenation related traits: language, country, hyphenate, hyphenation-character
* and minimum number of character to remain one the previous line and to be on the
* next line.
*/
public void changeHyphenation(String language, String country,
int hyphenate, char hyphenationChar,
int hyphenationPushCharacterCount,
int hyphenationRemainCharacterCount) {
this.language = language;
this.country = country;
this.hyphenate = hyphenate;
this.hyphenationChar = hyphenationChar;
this.hyphenationPushCharacterCount = hyphenationPushCharacterCount;
this.hyphenationRemainCharacterCount =
hyphenationRemainCharacterCount;
}
/**
* creates a leader as String out of the given char and the leader length
* and wraps it in an InlineArea which is returned
*/
private InlineArea buildSimpleLeader(int charNumber, int leaderLength) {
int factor = (int) Math.floor (leaderLength /
this.currentFontState.width(charNumber));
char [] leaderChars = new char [factor];
char fillChar = (char) charNumber;
for (int i = 0; i < factor; i ++) {
leaderChars[i] = fillChar;
}
WordArea leaderPatternArea =
new WordArea(currentFontState, this.red, this.green,
this.blue, new String (leaderChars), leaderLength);
leaderPatternArea.setYOffset(placementOffset);
return leaderPatternArea;
}
/**
* calculates the width of space which has to be inserted before the
* start of the leader, so that all leader characters are aligned.
* is used if property leader-align is set. At the moment only the value
* for leader-align="reference-area" is supported.
*
*/
private int getLeaderAlignIndent (int leaderLength,
int leaderPatternWidth) {
//calculate position of used space in line area
double position = getCurrentXPosition();
//calculate factor of next leader pattern cycle
double nextRepeatedLeaderPatternCycle =
Math.ceil(position / leaderPatternWidth);
//calculate difference between start of next leader
//pattern cycle and already used space
double difference = (leaderPatternWidth *
nextRepeatedLeaderPatternCycle) - position;
return (int) difference;
}
/**
* calculates the used space in this line area
*/
private int getCurrentXPosition() {
return finalWidth + spaceWidth + startIndent + pendingWidth;
}
/**
* extracts a complete word from the character data
*/
private String getHyphenationWord (char [] characters, int wordStart) {
boolean wordendFound = false;
int counter = 0;
char [] newWord = new char [100]; //create a buffer
while ((!wordendFound) && ((wordStart + counter) < characters.length)) {
char tk = characters[wordStart+counter];
if (Character.isLetter(tk)) {
newWord[counter] = tk;
counter++;
} else {
wordendFound = true;
}
}
return new String (newWord,0,counter);
}
/** extracts word for hyphenation and calls hyphenation package,
* handles cases of inword punctuation and quotation marks at the beginning
* of words, but not in a internationalized way
*/
public int doHyphenation (char [] characters, int position, int wordStart, int remainingWidth) {
//check whether the language property has been set
if (this.language.equalsIgnoreCase("none")) {
MessageHandler.errorln("if property 'hyphenate' is used, a language must be specified");
return wordStart;
}
/** remaining part string of hyphenation */
StringBuffer remainingString = new StringBuffer();
/** for words with some inword punctuation like / or - */
StringBuffer preString = null;
/** char before the word, probably whitespace */
char startChar = ' ' ;//characters[wordStart-1];
/** in word punctuation character */
char inwordPunctuation;
/** the complete word handed to the hyphenator */
String wordToHyphenate;
//width of hyphenation character
int hyphCharWidth = this.currentFontState.width(this.hyphenationChar);
remainingWidth -= hyphCharWidth;
//handles ' or " at the beginning of the word
if (characters[wordStart] == '"' || characters[wordStart] == '\'' ) {
remainingString.append(characters[wordStart]);
//extracts whole word from string
wordToHyphenate = getHyphenationWord(characters,wordStart+1);
} else {
wordToHyphenate = getHyphenationWord(characters,wordStart);
}
//if the extracted word is smaller than the remaining width
//we have a non letter character inside the word. at the moment
//we will only handle hard hyphens and slashes
if (getWordWidth(wordToHyphenate)< remainingWidth) {
inwordPunctuation = characters[wordStart+wordToHyphenate.length()];
if (inwordPunctuation == '-' ||
inwordPunctuation == '/' ) {
preString = new StringBuffer(wordToHyphenate);
preString = preString.append(inwordPunctuation);
wordToHyphenate = getHyphenationWord(characters,wordStart+wordToHyphenate.length()+1);
remainingWidth -= (getWordWidth(wordToHyphenate) + this.currentFontState.width(inwordPunctuation));
}
}
//are there any hyphenation points
Hyphenation hyph = Hyphenator.hyphenate(language,country,wordToHyphenate,hyphenationRemainCharacterCount,hyphenationPushCharacterCount);
//no hyphenation points and no inword non letter character
if (hyph == null && preString == null) {
if (remainingString.length() > 0) {
return wordStart-1;
} else {
return wordStart;
}
//no hyphenation points, but a inword non-letter character
} else if (hyph == null && preString != null){
remainingString.append(preString);
this.addWord(startChar,remainingString);
return wordStart + remainingString.length();
//hyphenation points and no inword non-letter character
} else if (hyph != null && preString == null) {
int index = getFinalHyphenationPoint(hyph,remainingWidth);
if (index != -1) {
remainingString.append(hyph.getPreHyphenText(index));
remainingString.append(this.hyphenationChar);
this.addWord(startChar,remainingString);
return wordStart + remainingString.length()-1;
}
//hyphenation points and a inword non letter character
} else if (hyph != null && preString != null) {
int index = getFinalHyphenationPoint(hyph,remainingWidth);
if (index != -1) {
remainingString.append(preString.append(hyph.getPreHyphenText(index)));
remainingString.append(this.hyphenationChar);
this.addWord(startChar,remainingString);
return wordStart + remainingString.length()-1;
} else {
remainingString.append(preString) ;
this.addWord(startChar,remainingString);
return wordStart + remainingString.length();
}
}
return wordStart;
}
/** calculates the wordWidth using the actual fontstate*/
private int getWordWidth (String word) {
int wordLength = word.length();
int width = 0;
char [] characters = new char [wordLength];
word.getChars(0,wordLength,characters,0);
for (int i = 0; i < wordLength; i++) {
width += this.currentFontState.width(characters[i]);
}
return width;
}
public int getRemainingWidth()
{
return this.getContentWidth() - this.getCurrentXPosition();
}
public void setLinkSet(LinkSet ls)
{
}
public void addInlineArea(Area box)
{
addPending();
addChild(box);
prev = TEXT;
finalWidth += box.getContentWidth();
}
public void addInlineSpace(InlineSpace is, int spaceWidth)
{
addChild(is);
finalWidth += spaceWidth;
// spaceWidth = 0;
}
/** adds a single character to the line area tree*/
public int addCharacter (char data, LinkSet ls, boolean ul) {
WordArea ia = null;
int remainingWidth =
this.getContentWidth() - this.getCurrentXPosition();
int width = this.currentFontState.width(data);
//if it doesn't fit, return
if (width > remainingWidth) {
return org.apache.fop.fo.flow.Character.DOESNOT_FIT;
} else {
//if whitespace-collapse == true, discard character
if (Character.isSpaceChar(data) && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) {
return org.apache.fop.fo.flow.Character.OK;
}
//create new InlineArea
ia = new WordArea(currentFontState,
this.red, this.green, this.blue,
new Character(data).toString(),width);
ia.setYOffset(placementOffset);
ia.setUnderlined(ul);
pendingAreas.addElement(ia);
if (Character.isSpaceChar(data)) {
this.spaceWidth =+ width;
prev = LineArea.WHITESPACE;
} else {
pendingWidth += width;
prev = LineArea.TEXT;
}
return org.apache.fop.fo.flow.Character.OK;
}
}
/** adds a InlineArea containing the String startChar+wordBuf to the line area children. */
private void addWord (char startChar, StringBuffer wordBuf) {
String word = wordBuf.toString();
WordArea hia;
int startCharWidth = this.currentFontState.width(startChar);
if (startChar == ' ') {
this.addChild(new InlineSpace(startCharWidth));
} else {
hia = new WordArea(currentFontState,
this.red, this.green, this.blue,
new Character(startChar).toString(),1);
hia.setYOffset(placementOffset);
this.addChild(hia);
}
int wordWidth = this.getWordWidth(word);
hia = new WordArea(currentFontState,
this.red, this.green, this.blue,
word,word.length());
hia.setYOffset(placementOffset);
this.addChild(hia);
//calculate the space needed
finalWidth += startCharWidth + wordWidth ;
}
/** extracts from a hyphenated word the best (most greedy) fit */
private int getFinalHyphenationPoint(Hyphenation hyph, int remainingWidth) {
int [] hyphenationPoints = hyph.getHyphenationPoints();
int numberOfHyphenationPoints = hyphenationPoints.length;
int index = -1;
String wordBegin = "";
int wordBeginWidth = 0;
for (int i = 0;i < numberOfHyphenationPoints; i++){
wordBegin = hyph.getPreHyphenText(i);
if (this.getWordWidth(wordBegin) > remainingWidth){
break;
}
index = i;
}
return index;
}
}