Browse Source

Added missing support for Next Line (NEL, 0x0085) and Line Separator (LS, 0x2028) characters.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-0_95@698151 13f79535-47bb-0310-9956-ffa450edef68
fop-0_95
Jeremias Maerki 15 years ago
parent
commit
3ad16136d5

+ 57
- 58
src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java View File

@@ -5,9 +5,9 @@
* The ASF licenses this file to You 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.
@@ -26,6 +26,7 @@ import java.util.ListIterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.area.Trait;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.fo.Constants;
@@ -81,7 +82,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
this.isSpace = isSpace;
this.breakOppAfter = breakOppAfter;
}
public String toString() {
return "[ lscnt=" + iLScount
+ ", wscnt=" + iWScount
@@ -120,7 +121,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {

/** Used to reduce instantiation of MinOptMax with zero length. Do not modify! */
private static final MinOptMax ZERO_MINOPTMAX = new MinOptMax(0);
private FOText foText;
private char[] textArray;
/**
@@ -130,8 +131,6 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
*/
private MinOptMax[] letterAdjustArray; //size = textArray.length + 1

private static final char NEWLINE = '\n';

private Font font = null;
/** Start index of first character in this parent Area */
private short iAreaStart = 0;
@@ -152,7 +151,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
/** 1/2 of word-spacing value */
private SpaceVal halfWS;
/** 1/2 of letter-spacing value */
private SpaceVal halfLS;
private SpaceVal halfLS;
/** Number of space characters after previous possible break position. */
private int iNbSpacesPending;

@@ -166,7 +165,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {

private int lineStartBAP = 0;
private int lineEndBAP = 0;
private boolean keepTogether;

/**
@@ -177,7 +176,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
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);
@@ -185,22 +184,22 @@ public class TextLayoutManager extends LeafNodeLayoutManager {

vecAreaInfo = new java.util.ArrayList();
}
/** {@inheritDoc} */
public void initialize() {
FontInfo fi = foText.getFOEventHandler().getFontInfo();
FontTriplet[] fontkeys = foText.getCommonFont().getFontState(fi);
font = fi.getFontInstance(fontkeys[0], foText.getCommonFont().fontSize.getValue(this));
// With CID fonts, space isn't neccesary currentFontState.width(32)
spaceCharIPD = font.getCharWidth(' ');
// Use hyphenationChar property
hyphIPD = foText.getCommonHyphenation().getHyphIPD(font);
SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
halfLS = new SpaceVal(MinOptMax.multiply(ls.getSpace(), 0.5),
ls.isConditional(), ls.isForcing(), ls.getPrecedence());
ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, font);
// Make half-space: <space> on either side of a word-space)
halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5),
@@ -219,7 +218,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
// in the SpaceVal.makeWordSpacing() method
letterSpaceIPD = ls.getSpace();
wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace());
keepTogether = foText.getKeepTogether().getWithinLine().getEnum() == Constants.EN_ALWAYS;

}
@@ -298,9 +297,9 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
realWidth.add(MinOptMax.multiply(letterSpaceIPD, -1));
iLScount--;
}
for (int i = ai.iStartIndex; i < ai.iBreakIndex; i++) {
MinOptMax ladj = letterAdjustArray[i + 1];
MinOptMax ladj = letterAdjustArray[i + 1];
if (ladj != null && ladj.isElastic()) {
iLScount++;
}
@@ -327,7 +326,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
iDifference = (int) ((double) (realWidth.opt - realWidth.min)
* dIPDAdjust);
}
// set letter space adjustment
if (dIPDAdjust > 0.0) {
iLetterSpaceDim
@@ -341,7 +340,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount;

// set word space adjustment
//
//
if (iWScount > 0) {
iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount);
} else {
@@ -370,7 +369,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
// 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 =
// space width =
// = "normal" space width + letterSpaceAdjust + wordSpaceAdjust
// = spaceCharIPD + letterSpaceAdjust +
// + (iWordSpaceDim - spaceCharIPD - 2 * letterSpaceAdjust)
@@ -395,7 +394,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
* @param context the layout context
* @param spaceDiff unused
* @param firstIndex the index of the first AreaInfo used for the TextArea
* @param lastIndex the index of the last AreaInfo used for the TextArea
* @param lastIndex the index of the last AreaInfo used for the TextArea
* @param isLastArea is this TextArea the last in a line?
* @return the new text area
*/
@@ -450,7 +449,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
// here ends a new word
// add a word to the TextArea
if (isLastArea
&& i == lastIndex
&& i == lastIndex
&& areaInfo.bHyphenated) {
len++;
}
@@ -475,7 +474,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
}
// String wordChars = new String(textArray, wordStartIndex, len);
if (isLastArea
&& i == lastIndex
&& i == lastIndex
&& areaInfo.bHyphenated) {
// add the hyphenation character
wordChars.append(foText.getCommonHyphenation().getHyphChar(font));
@@ -487,12 +486,12 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
}
TraitSetter.addFontTraits(textArea, font);
textArea.addTrait(Trait.COLOR, foText.getColor());
TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
return textArea;
}
private void addToLetterAdjust(int index, int width) {
if (letterAdjustArray[index] == null) {
letterAdjustArray[index] = new MinOptMax(width);
@@ -519,7 +518,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
|| CharUtilities.isNonBreakableSpace(ch)
|| CharUtilities.isFixedWidthSpace(ch);
}
/** {@inheritDoc} */
public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
lineStartBAP = context.getLineStartBorderAndPaddingWidth();
@@ -536,11 +535,11 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
iThisStart = iNextStart;
boolean inWord = false;
boolean inWhitespace = false;
char ch = 0;
char ch = 0;
while (iNextStart < textArray.length) {
ch = textArray[iNextStart];
ch = textArray[iNextStart];
boolean breakOpportunity = false;
byte breakAction = keepTogether? LineBreakStatus.PROHIBITED_BREAK : lbs.nextChar(ch);
byte breakAction = keepTogether ? LineBreakStatus.PROHIBITED_BREAK : lbs.nextChar(ch);
switch (breakAction) {
case LineBreakStatus.COMBINING_PROHIBITED_BREAK:
case LineBreakStatus.PROHIBITED_BREAK:
@@ -556,7 +555,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
log.error("Unexpected breakAction: " + breakAction);
}
if (inWord) {
if (breakOpportunity || isSpace(ch) || ch == NEWLINE) {
if (breakOpportunity || isSpace(ch) || CharUtilities.isLineBreakCharacter(ch)) {
//Word boundary found, process widths and kerning
int lastIndex = iNextStart;
while (lastIndex > 0 && textArray[lastIndex - 1] == CharUtilities.SOFT_HYPHEN) {
@@ -626,7 +625,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
ai = new AreaInfo(iThisStart, (short) (iNextStart),
(short) (iNextStart - iThisStart), (short) 0,
MinOptMax.multiply(wordSpaceIPD, iNextStart - iThisStart),
false, true, breakOpportunity);
false, true, breakOpportunity);
vecAreaInfo.add(ai);
prevAi = ai;

@@ -657,9 +656,9 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
returnList.add(sequence);
}
}
if ((ch == CharUtilities.SPACE
&& foText.getWhitespaceTreatment() == Constants.EN_PRESERVE)
if ((ch == CharUtilities.SPACE
&& foText.getWhitespaceTreatment() == Constants.EN_PRESERVE)
|| ch == CharUtilities.NBSPACE) {
// preserved space or non-breaking space:
// create the AreaInfo object
@@ -672,17 +671,17 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
MinOptMax ipd = new MinOptMax(font.getCharWidth(ch));
ai = new AreaInfo(iNextStart, (short) (iNextStart + 1),
(short) 0, (short) 0,
ipd, false, true, breakOpportunity);
ipd, false, true, breakOpportunity);
iThisStart = (short) (iNextStart + 1);
} else if (ch == NEWLINE) {
} else if (CharUtilities.isLineBreakCharacter(ch)) {
// linefeed; this can happen when linefeed-treatment="preserve"
iThisStart = (short) (iNextStart + 1);
}
inWord = !isSpace(ch) && ch != NEWLINE;
inWord = !isSpace(ch) && !CharUtilities.isLineBreakCharacter(ch);
inWhitespace = ch == CharUtilities.SPACE && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE;
iNextStart++;
} // end of while
// Process any last elements
if (inWord) {
int lastIndex = iNextStart;
@@ -734,7 +733,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
ai = new AreaInfo(iThisStart, (short) (iNextStart),
(short) (iNextStart - iThisStart), (short) 0,
MinOptMax.multiply(wordSpaceIPD, iNextStart - iThisStart),
false, true, true);
false, true, true);
vecAreaInfo.add(ai);

// create the elements
@@ -747,7 +746,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
sequence.addAll
(createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
ai = null;
} else if (ch == NEWLINE) {
} else if (CharUtilities.isLineBreakCharacter(ch)) {
if (lineEndBAP != 0) {
sequence.add
(new KnuthGlue(lineEndBAP, 0, 0,
@@ -894,7 +893,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
: (iStopIndex - iStartIndex))));

if (!(bNothingChanged
&& iStopIndex == ai.iBreakIndex
&& iStopIndex == ai.iBreakIndex
&& bHyphenFollows == false)) {
// the new AreaInfo object is not equal to the old one
if (changeList == null) {
@@ -991,7 +990,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
AreaInfo ai, int leafValue) {
LinkedList spaceElements = new LinkedList();
LeafPosition mainPosition = new LeafPosition(this, leafValue);
if (!ai.breakOppAfter) {
// a non-breaking space
if (alignment == EN_JUSTIFY) {
@@ -1001,7 +1000,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
notifyPos(new LeafPosition(this, -1)), true));
spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
false, new LeafPosition(this, -1), false));
spaceElements.add(new KnuthGlue(ai.ipdArea.opt, ai.ipdArea.max - ai.ipdArea.opt,
spaceElements.add(new KnuthGlue(ai.ipdArea.opt, ai.ipdArea.max - ai.ipdArea.opt,
ai.ipdArea.opt - ai.ipdArea.min, mainPosition, false));
} else {
// the space does not need to stretch or shrink, and must be
@@ -1016,7 +1015,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
switch (alignment) {
case EN_CENTER:
// centered text:
// if the second element is chosen as a line break these elements
// 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
spaceElements.add(new KnuthGlue(lineEndBAP,
@@ -1043,7 +1042,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
case EN_START: // fall through
case EN_END:
// left- or right-aligned text:
// if the second element is chosen as a line break these elements
// 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
spaceElements.add(new KnuthGlue(lineEndBAP,
@@ -1102,13 +1101,13 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
this, -1), false));
spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0,
mainPosition, false));
}
}
} else {
// a (possible block) of breaking spaces
switch (alignment) {
case EN_CENTER:
// centered text:
// if the second element is chosen as a line break these elements
// 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
spaceElements.add(new KnuthGlue(lineEndBAP,
@@ -1134,7 +1133,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
case EN_START: // fall through
case EN_END:
// left- or right-aligned text:
// if the second element is chosen as a line break these elements
// 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
if (lineStartBAP != 0 || lineEndBAP != 0) {
@@ -1221,7 +1220,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
}
}
}
return spaceElements;
}

@@ -1239,14 +1238,14 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
// constant letter spacing
wordElements.add
(new KnuthInlineBox(
bSuppressibleLetterSpace
bSuppressibleLetterSpace
? ai.ipdArea.opt - letterSpaceWidth.opt
: ai.ipdArea.opt,
alignmentContext,
notifyPos(mainPosition), false));
} else {
// adjustable letter spacing
int unsuppressibleLetterSpaces
int unsuppressibleLetterSpaces
= bSuppressibleLetterSpace ? ai.iLScount - 1 : ai.iLScount;
wordElements.add
(new KnuthInlineBox(ai.ipdArea.opt
@@ -1265,7 +1264,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
(new KnuthInlineBox(0, null,
notifyPos(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) {
@@ -1275,7 +1274,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
widthIfNoBreakOccurs = letterAdjustArray[ai.iBreakIndex];
}
//if (ai.iBreakIndex)
// the word fragment ends at the end of a syllable:
// if a break occurs the content width increases,
// otherwise nothing happens
@@ -1297,7 +1296,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
widthIfNoBreakOccurs = ZERO_MINOPTMAX;
}
LinkedList hyphenElements = new LinkedList();
switch (alignment) {
case EN_CENTER :
// centered text:
@@ -1347,7 +1346,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
(new KnuthGlue(lineStartBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
break;
case EN_START : // fall through
case EN_END :
// left- or right-aligned text:
@@ -1405,7 +1404,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
new LeafPosition(this, -1), false));
}
break;
default:
// justified text, or last line justified:
// just a flagged penalty
@@ -1467,10 +1466,10 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
}
}
}
return hyphenElements;
}
}



+ 21
- 6
src/java/org/apache/fop/util/CharUtilities.java View File

@@ -5,9 +5,9 @@
* The ASF licenses this file to You 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.
@@ -123,7 +123,7 @@ public class CharUtilities {
* @return true if the character has a fixed-width
*/
public static boolean isFixedWidthSpace(char c) {
return (c >= '\u2000' && c <= '\u200B')
return (c >= '\u2000' && c <= '\u200B')
|| c == '\u3000';
// c == '\u2000' // en quad
// c == '\u2001' // em quad
@@ -167,7 +167,7 @@ public class CharUtilities {
(c == '\u0020' // normal space
|| c == NBSPACE); // no-break space
}
/**
* Determines if the character represents any kind of space.
* @param c character to check
@@ -177,7 +177,7 @@ public class CharUtilities {
boolean ret = (isBreakableSpace(c) || isNonBreakableSpace(c));
return ret;
}
/**
* Indicates whether a character is classified as "Alphabetic" by the Unicode standard.
* @param ch the character
@@ -201,6 +201,21 @@ public class CharUtilities {
return false;
}
}

/**
* Indicates whether the given character is a line break character as in the
* "BK" line breaking class (see UAX #14).
* @param ch the character
* @return true if the character is in the "BK" line breaking class
*/
public static boolean isLineBreakCharacter(char ch) {
return ch == '\n'
|| (ch == '\u0085') //NEXT LINE (NEL)
|| (ch == '\u2028'); //LINE SEPARATOR (SP)
//maybe also: PARAGRAPH SEPARATOR (PS)
//FORM FEED (FF, 000C) and LINE TABULATION (VT, 000B) are in the "BK" class
//but are no valid XML characters and therefore left out here.
}

}


+ 4
- 0
status.xml View File

@@ -53,6 +53,10 @@
<changes>
<release version="FOP Trunk" date="TBD">
<action context="Layout" dev="JM" type="add">
Added missing support for Next Line (NEL, 0x0085)
and Line Separator (LS, 0x2028) characters.
</action>
<action context="Renderers" dev="JM" type="fix">
Fixed text stroking in SVG when the stroke-width is zero.
</action>

+ 74
- 0
test/layoutengine/standard-testcases/block_uax14_linebreaking_bk.xml View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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$ -->
<testcase>
<info>
<p>
This test checks some of the UAX#14 breaking rules
(this file: BK: Mandatory Break (A) (Non-tailorable)).
</p>
</info>
<fo>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="normal" page-width="2.5in" page-height="10in" margin="5pt">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="normal" white-space-collapse="true">
<fo:flow flow-name="xsl-region-body" font-size="10pt">
<fo:block background-color="silver" font-size="8pt" margin="3pt 0pt 0pt 0pt">
BK -- Mandatory Break (A) (Non-tailorable)
</fo:block>
<!-- Form Feed (FF) and LINE TABULATION (VT) no legal XML characters, thus left out -->
<fo:block background-color="yellow" margin="0pt 0pt 3pt 0pt">
VeryLongWordWithALineSeparator&#x2028;PutInTheMiddleOfIt
</fo:block>
<!-- Paragraph Separator &#x2029; left out as it might have to be treated differently from LS -->
<fo:block background-color="yellow" margin="0pt 0pt 3pt 0pt">
VeryLongWordWithANEL&#x0085;PutInTheMiddleOfIt
</fo:block>
<fo:block background-color="yellow" margin="0pt 0pt 3pt 0pt"
linefeed-treatment="preserve">VeryLongWordWithACarriageReturn
PutInTheMiddleOfIt
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
<eval expected="2" xpath="count(//flow/block[2]/lineArea)"/>
<eval expected="30" xpath="string-length(//flow/block[2]/lineArea[1]/text)"/>
<eval expected="156170" xpath="//flow/block[2]/lineArea[1]/text/@ipd"/>
<eval expected="18" xpath="string-length(//flow/block[2]/lineArea[2]/text)"/>
<eval expected="86150" xpath="//flow/block[2]/lineArea[2]/text/@ipd"/>

<eval expected="2" xpath="count(//flow/block[3]/lineArea)"/>
<eval expected="20" xpath="string-length(//flow/block[3]/lineArea[1]/text)"/>
<eval expected="112810" xpath="//flow/block[3]/lineArea[1]/text/@ipd"/>
<eval expected="18" xpath="string-length(//flow/block[3]/lineArea[2]/text)"/>
<eval expected="86150" xpath="//flow/block[3]/lineArea[2]/text/@ipd"/>
<eval expected="2" xpath="count(//flow/block[4]/lineArea)"/>
<eval expected="31" xpath="string-length(//flow/block[4]/lineArea[1]/text)"/>
<eval expected="161710" xpath="//flow/block[4]/lineArea[1]/text/@ipd"/>
<eval expected="18" xpath="string-length(//flow/block[4]/lineArea[2]/text)"/>
<eval expected="86150" xpath="//flow/block[4]/lineArea[2]/text/@ipd"/>
</checks>
</testcase>

Loading…
Cancel
Save