Browse Source

Correct handling of letter spaces, in combination with different text alignments and optional breaks ("-" and "/" characters in the text)

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@291913 13f79535-47bb-0310-9956-ffa450edef68
pull/31/head
Luca Furini 19 years ago
parent
commit
b2d394dab8

+ 12
- 13
src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java View File

* inline break positions. * inline break positions.
*/ */
private static class LineBreakPosition extends LeafPosition { 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;
private int iParIndex; // index of the Paragraph this Position refers to
private int availableShrink;
private int availableStretch;
private int difference;
private double dAdjust; // Percentage to adjust (stretch or shrink)
private double ipdAdjust; // Percentage to adjust (stretch or shrink)
private int startIndent;
private int lineHeight;
private int lineWidth;
private int baseline;
private int topShift;
private int bottomShift;


LineBreakPosition(LayoutManager lm, int index, int iBreakIndex, LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
int shrink, int stretch, int diff, int shrink, int stretch, int diff,

+ 80
- 59
src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java View File

} }
if (ai == null) { if (ai == null) {
return; return;
} else if (ai.iLScount == ai.iBreakIndex - ai.iStartIndex
&& context.isLastArea()) {
// the line ends at a character like "/" or "-";
// remove the letter space after the last character
realWidth.add(MinOptMax.multiply(letterSpaceIPD, -1));
iLScount --;
} }


// Make an area containing all characters between start and end. // Make an area containing all characters between start and end.
// there are no word spaces in this area // there are no word spaces in this area
} }
iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount; iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
if (iTotalAdjust != iDifference) {
// the applied adjustment is greater or smaller than the needed one
log.trace("TextLM.addAreas: error in word / letter space adjustment = "
+ (iTotalAdjust - iDifference));
// set iTotalAdjust = iDifference, so that the width of the TextArea
// will counterbalance the error and the other inline areas will be
// placed correctly
iTotalAdjust = iDifference;
}


TextArea t = createTextArea(str, realWidth, iTotalAdjust, context, TextArea t = createTextArea(str, realWidth, iTotalAdjust, context,
wordSpaceIPD.opt - spaceCharIPD); wordSpaceIPD.opt - spaceCharIPD);
&& textArray[iTempStart] != NBSPACE && textArray[iTempStart] != NBSPACE
&& textArray[iTempStart] != NEWLINE && textArray[iTempStart] != NEWLINE
&& !(iTempStart > iNextStart && !(iTempStart > iNextStart
&& alignment == EN_JUSTIFY
&& BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0); && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
iTempStart++) { iTempStart++) {
wordIPD.add(fs.getCharWidth(textArray[iTempStart])); wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
} }
int iLetterSpaces = iTempStart - iThisStart - 1; int iLetterSpaces = iTempStart - iThisStart - 1;
// if the last character is '-' or '/' and the next one
// is not a space, it could be used as a line end;
// add one more letter space, in case other text follows
if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
&& iTempStart < textArray.length
&& textArray[iTempStart] != SPACE
&& textArray[iTempStart] != NBSPACE) {
iLetterSpaces ++;
}
wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces)); wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));


// create the AreaInfo object // create the AreaInfo object
ai = new AreaInfo(iThisStart, iTempStart, (short) 0, ai = new AreaInfo(iThisStart, iTempStart, (short) 0,
(short) iLetterSpaces, (short) iLetterSpaces,
wordIPD, false); wordIPD, false);
// if the last character is '-' or '/' and other characters
// follows, it could be used as a line end; update the
// information in the AreaInfo, adding one more letter space
if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
&& iTempStart < textArray.length
&& textArray[iTempStart] != SPACE
&& textArray[iTempStart] != NBSPACE) {
ai.iLScount ++;
}
vecAreaInfo.add(ai); vecAreaInfo.add(ai);


// create the elements // create the elements
AreaInfo ai, int leafValue, MinOptMax letterSpaceWidth) { AreaInfo ai, int leafValue, MinOptMax letterSpaceWidth) {
LinkedList wordElements = new LinkedList(); LinkedList wordElements = new LinkedList();
LeafPosition mainPosition = new LeafPosition(this, leafValue); LeafPosition mainPosition = new LeafPosition(this, leafValue);
int unsuppressibleLetterSpaces = ai.iLScount;


// if the last character of the word fragment is '-' or '/' and
// the next one is not a space, the fragment could end a line;
// in this case, it loses one of its letter spaces;
if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
&& ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
unsuppressibleLetterSpaces --;
}
// if the last character of the word fragment is '-' or '/',
// the fragment could end a line; in this case, it loses one
// of its letter spaces;
boolean bSuppressibleLetterSpace =
ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)
&& BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0;


if (letterSpaceWidth.min == letterSpaceWidth.max) { if (letterSpaceWidth.min == letterSpaceWidth.max) {
// constant letter spacing // constant letter spacing
wordElements.add wordElements.add
(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
mainPosition, false));
(new KnuthInlineBox(
bSuppressibleLetterSpace ?
ai.ipdArea.opt - letterSpaceWidth.opt :
ai.ipdArea.opt,
lead, total, middle,
mainPosition, false));
} else { } else {
// adjustable letter spacing // adjustable letter spacing
int unsuppressibleLetterSpaces = bSuppressibleLetterSpace ?
ai.iLScount - 1 :
ai.iLScount;
wordElements.add wordElements.add
(new KnuthInlineBox(ai.ipdArea.opt
- ai.iLScount * letterSpaceWidth.opt,
lead, total, middle,
mainPosition, false));
(new KnuthInlineBox(ai.ipdArea.opt
- ai.iLScount * letterSpaceWidth.opt,
lead, total, middle,
mainPosition, false));
wordElements.add wordElements.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
wordElements.add wordElements.add
(new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt,
unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt),
unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min),
new LeafPosition(this, -1), true));
(new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt,
unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt),
unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min),
new LeafPosition(this, -1), true));
wordElements.add wordElements.add
(new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
(new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
} }
// extra-elements if the word fragment is the end of a syllable,
// or it ends with a character that can be used as a line break
if (ai.bHyphenated) { if (ai.bHyphenated) {
wordElements.addAll(createElementsForAHyphen(alignment));
// the word fragment ends at the end of a syllable:
// if a break occurs the content width increases,
// otherwise nothing happens
wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, new MinOptMax(0)));
} else if (bSuppressibleLetterSpace) {
// the word framgent ends with a character that acts as a hyphen
// if a break occurs the width does not increase,
// otherwise there is one more letter space
wordElements.addAll(createElementsForAHyphen(alignment, 0, letterSpaceWidth));
} }
// add a flagged penalty element and a glue element representing a suppressible
// letter space if the next character is not a space
if (ai.iLScount - unsuppressibleLetterSpaces == 1) {
//TODO: this is correct only if text is justified
wordElements.add
(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
new LeafPosition(this, -1), false));
wordElements.add
(new KnuthGlue(letterSpaceWidth.opt,
letterSpaceWidth.max - letterSpaceWidth.opt,
letterSpaceWidth.opt - letterSpaceWidth.min,
new LeafPosition(this, -1), false));
}



return wordElements; return wordElements;
} }


private LinkedList createElementsForAHyphen(int alignment) {
private LinkedList createElementsForAHyphen(int alignment,
int widthIfBreakOccurs, MinOptMax widthIfNoBreakOccurs) {
LinkedList hyphenElements = new LinkedList(); LinkedList hyphenElements = new LinkedList();
switch (alignment) { switch (alignment) {
case EN_CENTER : case EN_CENTER :
// centered text: // centered text:
// if the second element is chosen as a line break these elements
// add a constant amount of stretch at the end of a line and at the
// beginning of the next one, otherwise they don't add any stretch
hyphenElements.add hyphenElements.add
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
hyphenElements.add hyphenElements.add
(new KnuthPenalty(hyphIPD,
(new KnuthPenalty(widthIfBreakOccurs,
KnuthPenalty.FLAGGED_PENALTY, true, KnuthPenalty.FLAGGED_PENALTY, true,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
hyphenElements.add hyphenElements.add
(new KnuthGlue(0,
(new KnuthGlue(widthIfNoBreakOccurs.opt,
- 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
hyphenElements.add hyphenElements.add
case EN_START : // fall through case EN_START : // fall through
case EN_END : case EN_END :
// left- or right-aligned text: // left- or right-aligned text:
// if the second element is chosen as a line break these elements
// add a constant amount of stretch at the end of a line, otherwise
// they don't add any stretch
hyphenElements.add hyphenElements.add
(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
hyphenElements.add hyphenElements.add
(new KnuthPenalty(hyphIPD,
(new KnuthPenalty(widthIfBreakOccurs,
KnuthPenalty.FLAGGED_PENALTY, true, KnuthPenalty.FLAGGED_PENALTY, true,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
hyphenElements.add hyphenElements.add
(new KnuthGlue(0,
(new KnuthGlue(widthIfNoBreakOccurs.opt,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
break; break;
// justified text, or last line justified: // justified text, or last line justified:
// just a flagged penalty // just a flagged penalty
hyphenElements.add hyphenElements.add
(new KnuthPenalty(hyphIPD,
(new KnuthPenalty(widthIfBreakOccurs,
KnuthPenalty.FLAGGED_PENALTY, true, KnuthPenalty.FLAGGED_PENALTY, true,
new LeafPosition(this, -1), false)); new LeafPosition(this, -1), false));
// extra elements representing a letter space that is suppressed
// if a break occurs
if (widthIfNoBreakOccurs.min != 0
|| widthIfNoBreakOccurs.max != 0) {
hyphenElements.add
(new KnuthGlue(widthIfNoBreakOccurs.opt,
widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
new LeafPosition(this, -1), false));
}
} }
return hyphenElements; return hyphenElements;

Loading…
Cancel
Save