in fop-core/src/main/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java [761:917]
public List getNextKnuthElements(final LayoutContext context, final int alignment) {
lineStartBAP = context.getLineStartBorderAndPaddingWidth();
lineEndBAP = context.getLineEndBorderAndPaddingWidth();
alignmentContext = context.getAlignmentContext();
final List returnList = new LinkedList();
KnuthSequence sequence = new InlineKnuthSequence();
GlyphMapping mapping = null;
GlyphMapping prevMapping = null;
returnList.add(sequence);
if (LOG.isDebugEnabled()) {
LOG.debug("GK: [" + nextStart + "," + foText.length() + "]");
}
LineBreakStatus lineBreakStatus = new LineBreakStatus();
thisStart = nextStart;
boolean inWord = false;
boolean inWhitespace = false;
char ch = 0;
int level = -1;
int prevLevel = -1;
boolean retainControls = false;
Font lastFont = null;
int lastFontPos = -1;
while (nextStart < foText.length()) {
ch = foText.charAt(nextStart);
level = foText.bidiLevelAt(nextStart);
boolean breakOpportunity = false;
byte breakAction = keepTogether
? LineBreakStatus.PROHIBITED_BREAK
: lineBreakStatus.nextChar(ch);
switch (breakAction) {
case LineBreakStatus.COMBINING_PROHIBITED_BREAK:
case LineBreakStatus.PROHIBITED_BREAK:
break;
case LineBreakStatus.EXPLICIT_BREAK:
break;
case LineBreakStatus.COMBINING_INDIRECT_BREAK:
case LineBreakStatus.DIRECT_BREAK:
case LineBreakStatus.INDIRECT_BREAK:
breakOpportunity = true;
break;
default:
TextLayoutManager.LOG.error("Unexpected breakAction: " + breakAction);
}
if (LOG.isDebugEnabled()) {
LOG.debug("GK: {"
+ " index = " + nextStart
+ ", char = " + CharUtilities.charToNCRef(ch)
+ ", level = " + level
+ ", levelPrev = " + prevLevel
+ ", inWord = " + inWord
+ ", inSpace = " + inWhitespace
+ "}");
}
if (inWord) {
boolean processWord = breakOpportunity
|| GlyphMapping.isSpace(ch)
|| CharUtilities.isExplicitBreak(ch)
|| ((prevLevel != -1) && (level != prevLevel));
if (!processWord && foText.getCommonFont().getFontSelectionStrategy() == EN_CHARACTER_BY_CHARACTER) {
if (lastFont == null || lastFontPos != nextStart - 1) {
lastFont = FontSelector.selectFontForCharactersInText(
foText, nextStart - 1, nextStart, foText, this);
}
Font font = FontSelector.selectFontForCharactersInText(
foText, nextStart, nextStart + 1, foText, this);
processWord = font != lastFont;
lastFont = font;
lastFontPos = nextStart;
}
if (processWord) {
// this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN
prevMapping = processWord(alignment, sequence, prevMapping, ch,
breakOpportunity, true, prevLevel, retainControls);
}
} else if (inWhitespace) {
if (ch != CharUtilities.SPACE || breakOpportunity) {
prevMapping = processWhitespace(alignment, sequence,
breakOpportunity, prevLevel);
}
} else {
if (mapping != null) {
prevMapping = mapping;
processLeftoverGlyphMapping(alignment, sequence, mapping,
ch == CharUtilities.SPACE || breakOpportunity);
mapping = null;
}
if (breakAction == LineBreakStatus.EXPLICIT_BREAK) {
sequence = processLinebreak(returnList, sequence);
}
}
if (ch == CharUtilities.SPACE
&& foText.getWhitespaceTreatment() == Constants.EN_PRESERVE
|| ch == CharUtilities.NBSPACE) {
final Font font = FontSelector.selectFontForCharacterInText(ch,
this.foText, this);
font.mapChar(ch);
// preserved space or non-breaking space:
// create the GlyphMapping object
MinOptMax areaIPD;
if (prevMapping != null && prevMapping.isSpace) {
areaIPD = wordSpaceIPD.minus(letterSpaceIPD);
} else {
areaIPD = wordSpaceIPD;
}
mapping = new GlyphMapping(nextStart, nextStart + 1, 1, 0, areaIPD, false, true,
breakOpportunity, spaceFont, level, null);
thisStart = nextStart + 1;
} else if (CharUtilities.isFixedWidthSpace(ch) || CharUtilities.isZeroWidthSpace(ch)) {
// create the GlyphMapping object
Font font = FontSelector.selectFontForCharacterInText(ch, foText, this);
MinOptMax ipd = MinOptMax.getInstance(font.getCharWidth(ch));
mapping = new GlyphMapping(nextStart, nextStart + 1, 0, 0, ipd, false, true,
breakOpportunity, font, level, null);
thisStart = nextStart + 1;
} else if (CharUtilities.isExplicitBreak(ch)) {
//mandatory break-character: only advance index
thisStart = nextStart + 1;
}
inWord = !GlyphMapping.isSpace(ch) && !CharUtilities.isExplicitBreak(ch);
inWhitespace = ch == CharUtilities.SPACE
&& foText.getWhitespaceTreatment() != Constants.EN_PRESERVE;
prevLevel = level;
nextStart++;
}
// Process any last elements
if (inWord) {
processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel, retainControls);
} else if (inWhitespace) {
processWhitespace(alignment, sequence, !keepTogether, prevLevel);
} else if (mapping != null) {
processLeftoverGlyphMapping(alignment, sequence, mapping,
ch == CharUtilities.ZERO_WIDTH_SPACE);
} else if (CharUtilities.isExplicitBreak(ch)) {
this.processLinebreak(returnList, sequence);
}
if (((List) ListUtil.getLast(returnList)).isEmpty()) {
//Remove an empty sequence because of a trailing newline
ListUtil.removeLast(returnList);
}
setFinished(true);
if (returnList.isEmpty()) {
return null;
} else {
return returnList;
}
}