package de.uniulm.mathematik.typo.hyphen;
import java.lang.*;
import java.util.*;
import de.uniulm.mathematik.typo.items.*;
import de.uniulm.mathematik.typo.fonts.*;
/**
* An instance of this class is associated with an hyphenator object
* and permits to insert hyphenation items into horizontal sequences.
* If a hyphenation item is inserted at a position between two
* letters with a kerning item between them, the position in front
* of the kerning item is chosen. If the a horizontal sequence is
* split at such a point, the leading kerning item of the right
* sequence is to be removed. (This is implicitly done by the
* HorizontalSequence class.)
*/
public class SequenceWeightedHyphenator {
private WeightedHyphenator hyph;
/**
* Construct a sequence hyphenator for the given hyphenator object.
* @param h hyphenator object that delivers possible hyphenation
* positions for a given word.
*/
public SequenceWeightedHyphenator(WeightedHyphenator h) {
assert h != null;
hyph = h;
}
private int convertToPenalty(int weight) {
int diff = hyph.getMaxWeight() - weight;
return diff * diff * diff + 1;
}
/**
* Return a hyphenation sequence which is a copy of hseq
* with inserted hyphenation items at possible hyphenation points.
* @param hseq hyphenation sequence that represents a word,
* i.e. a sequence of glyph items representing letters
* with possible kerning items; non-letters are
* skipped but just the first sequence of letters
* is considered for hyphenation
* @return the given horizontal sequence is returned, if no
* hyphenation points have been found; otherwise a
* copy of hseq is returned with inserted hyphenation
* items
*/
public HorizontalSequence hyphenateWord(HorizontalSequence hseq) {
// extract a String representing the word out of hseq
ArrayList chpos = new ArrayList();
StringBuffer buf = new StringBuffer();
boolean inWord = false;
boolean wordFinished = false;
boolean behindKerning = false;
int pos = 0;
for (Item item: hseq) {
if (item.isKerning()) {
behindKerning = true;
} else {
int ch = item.getChar();
if (ch >= 0 && item.getFont() != null) {
if (Character.isLetter(ch)) {
if (!wordFinished) {
inWord = true;
buf.appendCodePoint(ch);
// break before the kerning box, if any; this makes sure
// that the kerning box gets removed if we break at this
// point (note that HorizontalSequence ignores a leading
// kerning box)
int cpos = pos;
if (behindKerning) {
--cpos;
}
chpos.add(new Integer(cpos));
}
} else if (inWord) {
inWord = false; wordFinished = true;
}
} else if (item.isPenalty()) {
// return hseq unchanged, if any hyphenations are already present
return hseq;
}
behindKerning = false;
}
++pos;
}
// return hseq unchanged if it does not contain any glyph boxes
if (buf.length() == 0) {
return hseq;
}
// hyphenate the extracted word and
// return hseq unchanged if no possible hyphenation was found
WeightedHyphenationRule rule = hyph.hyphenateWithWeights(buf.toString());
if (rule == null) {
return hseq;
}
// prepare iteration through hyphenation points
Iterator it = rule.iterator();
WeightedHyphenationPoint hpoint = null;
if (it.hasNext()) {
hpoint = it.next();
}
// create new hseq with inserted hyphenations
HorizontalSequence newseq = new SimpleHorizontalSequence();
pos = 0; int posindex = 0;
FontMetrics fm = null; int fontsize = 0;
for (Item item: hseq) {
if (item.getFont() != null) {
fm = item.getFont(); fontsize = item.getFontSize();
}
if (posindex < chpos.size() &&
pos == chpos.get(new Integer(posindex)).intValue()) {
// possible hyphenation position
if (hpoint != null && hpoint.getPosition() == posindex) {
// create hyphen in the font of the following item
Item hyphen = new NormalizedGlyphBox(fm, fontsize, '-');
// add penalty item representing the possible breakpoint
int penalty = convertToPenalty(hpoint.getWeight());
newseq.add(new Hyphenation(penalty, hyphen));
// advance to next hyphenation point
if (it.hasNext()) {
hpoint = it.next();
} else {
hpoint = null;
}
}
++posindex;
}
newseq.add(item);
++pos;
}
return newseq;
}
/**
* Return a copy of the given hyphenation sequence with inserted
* hyphenation items at all possible hyphenation positions. The
* sequence may represent an entire paragraph which is split into
* individual words which are each individually passed to the
* hyphenateWord method.
*/
public HorizontalSequence hyphenate(HorizontalSequence hseq) {
HorizontalSequence newseq = new SimpleHorizontalSequence();
for (HorizontalSequence word: hseq.getWords()) {
Item bp = word.getFollowingBreakpoint();
word = hyphenateWord(word);
newseq.add(word);
if (bp != null) {
newseq.add(bp);
}
}
return newseq;
}
}