/** Non-space characters on which we can end a line. */
private static final String BREAK_CHARS = "-/";
+ /** 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;
- /** Contains an array of widths to adjust for kerning and letter spacing */
+ /**
+ * Contains an array of widths to adjust for kerning. The first entry can
+ * be used to influence the start position of the first letter. The entry i+1 defines the
+ * cursor advancement after the character i. A null entry means no special advancement.
+ */
private MinOptMax[] letterAdjustArray; //size = textArray.length + 1
- /** The sum of all entries in the letterAdjustArray */
- private MinOptMax totalLetterAdjust = new MinOptMax(0);
private static final char NEWLINE = '\n';
iLScount--;
}
- for (int i = ai.iStartIndex + 1; i < ai.iBreakIndex + 1; i++) {
- MinOptMax ladj = letterAdjustArray[i];
+ for (int i = ai.iStartIndex; i < ai.iBreakIndex; i++) {
+ MinOptMax ladj = letterAdjustArray[i + 1];
if (ladj != null && ladj.isElastic()) {
iLScount++;
}
int[] letterAdjust = new int[wordChars.length()];
int lsCount = areaInfo.iLScount;
for (int letter = 0; letter < len; letter++) {
- MinOptMax adj = letterAdjustArray[letter + wordStartIndex + 1];
- letterAdjust[letter] = (adj != null ? adj.opt : 0);
+ MinOptMax adj = letterAdjustArray[letter + wordStartIndex];
+ if (letter > 0) {
+ letterAdjust[letter] = (adj != null ? adj.opt : 0);
+ }
if (lsCount > 0) {
letterAdjust[letter] += textArea.getTextLetterSpaceAdjust();
lsCount--;
} else {
letterAdjustArray[index].add(width);
}
- totalLetterAdjust.add(width);
}
private void addToLetterAdjust(int index, MinOptMax width) {
} else {
letterAdjustArray[index].add(width);
}
- totalLetterAdjust.add(width);
}
/**
char previous = textArray[i - 1];
kern = font.getKernValue(previous, c) * font.getFontSize() / 1000;
if (kern != 0) {
- addToLetterAdjust(i + 1, kern);
+ //log.info("Kerning between " + previous + " and " + c + ": " + kern);
+ addToLetterAdjust(i, kern);
}
wordIPD.add(kern);
}
hc.updateOffset(iStopIndex - iStartIndex);
+ //log.info("Word: " + new String(textArray, iStartIndex, iStopIndex - iStartIndex));
for (int i = iStartIndex; i < iStopIndex; i++) {
char c = textArray[i];
newIPD.add(new MinOptMax(font.getCharWidth(c)));
+ //if (i > iStartIndex) {
+ if (i < iStopIndex) {
+ MinOptMax la = this.letterAdjustArray[i + 1];
+ if ((i == iStopIndex - 1) && bHyphenFollows) {
+ //the letter adjust here needs to be handled further down during
+ //element generation because it depends on hyph/no-hyph condition
+ la = null;
+ }
+ if (la != null) {
+ newIPD.add(la);
+ }
+ }
}
// add letter spaces
boolean bIsWordEnd
iReturnedIndex++;
} // end of while
setFinished(true);
+ //ElementListObserver.observe(returnList, "text-changed", null);
return returnList;
}
// 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) {
+ MinOptMax widthIfNoBreakOccurs = null;
+ if (ai.iBreakIndex < textArray.length) {
+ //Add in kerning in no-break condition
+ 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
- wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, new MinOptMax(0)));
+ wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, widthIfNoBreakOccurs));
} else if (bSuppressibleLetterSpace) {
// the word fragment ends with a character that acts as a hyphen
// if a break occurs the width does not increase,
private LinkedList createElementsForAHyphen(int alignment,
int widthIfBreakOccurs, MinOptMax widthIfNoBreakOccurs) {
+ if (widthIfNoBreakOccurs == null) {
+ widthIfNoBreakOccurs = ZERO_MINOPTMAX;
+ }
LinkedList hyphenElements = new LinkedList();
switch (alignment) {
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="fix">
+ Bugfix: The combination of hyphenation and kerning resulted in slightly ragged
+ right ends for right-aligned and justified text.
+ </action>
+ <action context="Code" dev="JM" type="fix">
+ Bugfix: NullPointerException in AreaAdditionUtil in a table-cell spanning
+ multiple pages and with a marker.
+ </action>
+ <action context="Code" dev="JM" type="fix" fixes-bug="39533">
+ Bugfix: NullPointerException in RTF output when a table does not contain
+ table-columns.
+ </action>
+ <action context="Code" dev="JM" type="fix" fixes-bug="39607" due-to="Julien Aymé">
+ Bugfix: NullPointerException in RTF library when there are no borders on
+ the parent table.
+ </action>
<action context="Code" dev="JM" type="add">
Automatic support for all fonts available to the Java2D subsystem for all
Java2D-descendant renderers (TIFF, PNG, print, AWT).
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2006 The Apache Software Foundation
+
+ Licensed 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>
+ Checks hyphenation in combination with kerning.
+ </p>
+ </info>
+ <cfg>
+ <base14kerning>true</base14kerning>
+ </cfg>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" hyphenate="true">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple" page-height="5in" page-width="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="simple">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block font-size="20pt" line-height="1.0" text-align="justify" text-align-last="justify" background-color="lightgray" start-indent="10pt" end-indent="10pt" border="solid 1pt">
+ hyphenation regression advantage foundation vandalism AVAVAVA vandavanda
+ hyphenation regression advantage foundation vandalism AVAVAVA vandavanda
+ hyphenation regression
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport)"/>
+
+ <eval expected="17230" xpath="//flow/block[1]/lineArea[1]/text[1]/@twsadjust"/>
+ <eval expected="-1339" xpath="//flow/block[1]/lineArea[2]/text[1]/@twsadjust"/>
+ <eval expected="2393" xpath="//flow/block[1]/lineArea[3]/text[1]/@twsadjust"/>
+ <eval expected="5900" xpath="//flow/block[1]/lineArea[4]/text[1]/@twsadjust"/>
+ <eval expected="9900" xpath="//flow/block[1]/lineArea[5]/text[1]/@twsadjust"/>
+
+ <!-- In no-break condition there's a -400 kerning between a and v. It musn't occur if "vandavanda" is hyphenated. -->
+ <eval expected="vanda-" xpath="//flow/block[1]/lineArea[2]/text[1]/word[4]"/>
+ <eval expected="0 -500 0 0 0 0" xpath="//flow/block[1]/lineArea[2]/text[1]/word[4]/@letter-adjust"/>
+ <eval expected="vanda" xpath="//flow/block[1]/lineArea[3]/text[1]/word[1]"/>
+ <eval expected="0 -500 0 0 0" xpath="//flow/block[1]/lineArea[3]/text[1]/word[1]/@letter-adjust"/>
+ </checks>
+</testcase>