Hi,
To begin with: we use FOP 0.20.4, and this mail is about a partly fix
for missing letter-spacing/kerning between words in PDF's.
In PDF's produced with FOP compared to PDF's from other an other source,
we noticed that FOP doesn't apply kerning (letter-spacing) to spaces
between words, most obviously at the end of sentences. I.e., no kerning
is applied to the character pairs
word-characterspace (for example, . ) and
spaceword-character (for example, V).
The cause, or in any case, the place where it can (partly?) be fixed,
can be found in the method renderWordArea(WordArea area) in
PDFRenderer.java.
The latter case (spaceword-character) I could easily fix. All I
had to do was move the lines
String s;
if (area.getPageNumberID()
!= null) {// this text is a page number, so resolve it
s = idReferences.getPageNumber(area.getPageNumberID());
if (s == null) {
s = ;
}
} else {
s = area.getText();
}
int l = s.length();
up a few lines so they precede 'if (!textOpen || bl != prevWordY)',
and add the following lines in the else-part of
'if (!textOpen || bl != prevWordY)':
if (kerningAvailable l 0) {
addKerning(pdf, (new Integer((int) ' ')),
(new Integer((int) area.getFontState().mapChar(s.charAt(0,
kerning, startText, endText);
}
I've attached the complete code of the method renderWordArea separately.
But..., that's only half of the solution! The first case
(word-characterspace) is more difficult. I think that in
renderWordArea we can not be sure that a space is following the word we
just rendered - or can we? And we don't have the last character of the
previous word available. Anyone has any ideas?
Regards, Joop Vriend.
/**
* render inline area to PDF
*
* @param area inline area to render
*/
public void renderWordArea(WordArea area) {
synchronized (_wordAreaPDF) {
StringBuffer pdf = _wordAreaPDF;
pdf.setLength(0);
Hashtable kerning = null;
boolean kerningAvailable = false;
kerning = area.getFontState().getKerning();
if (kerning != null !kerning.isEmpty()) {
kerningAvailable = true;
}
String name = area.getFontState().getFontName();
int size = area.getFontState().getFontSize();
// This assumes that *all* CIDFonts use a /ToUnicode mapping
boolean useMultiByte = false;
Font f = (Font) area.getFontState().
getFontInfo().getFonts().get(name);
if (f instanceof LazyFont) {
if (((LazyFont) f).getRealFont() instanceof CIDFont) {
useMultiByte = true;
}
} else if (f instanceof CIDFont) {
useMultiByte = true;
}
// String startText = useMultiByte ? FEFF : (;
String startText = useMultiByte ? : (;
String endText = useMultiByte ? : ) ;
if ((!name.equals(this.currentFontName))
|| (size != this.currentFontSize)) {
closeText();
this.currentFontName = name;
this.currentFontSize = size;
pdf = pdf.append(/ + name + + ((float)size / 1000) + Tf\n);
}
//Do letter spacing (must be outside of [..] TJ)
float letterspacing =
((float) area.getFontState().getLetterSpacing()) / 1000;
if (letterspacing != this.currentLetterSpacing) {
this.currentLetterSpacing = letterspacing;
closeText();
pdf.append(letterspacing).append( Tc\n);
}
PDFColor areaColor = null;
if (this.currentFill instanceof PDFColor) {
areaColor = (PDFColor) this.currentFill;
}
if (areaColor == null || areaColor.red() != (double) area.getRed()
|| areaColor.green() != (double) area.getGreen()
|| areaColor.blue() != (double) area.getBlue()) {
areaColor = new PDFColor((double) area.getRed(),
(double) area.getGreen(),
(double) area.getBlue());
closeText();
this.currentFill = areaColor;
pdf.append(this.currentFill.getColorSpaceOut(true));
}
int rx = this.currentXPosition;
int bl = this.currentYPosition;
addWordLines(area, rx, bl, size, areaColor);
String s;
if (area.getPageNumberID() != null) {// this text is a page number, so
resolve it
s = idReferences.getPageNumber(area.getPageNumberID());
if (s == null) {
s = ;
}
} else {
s = area.getText();
}
int l = s.length();
if (!textOpen || bl != prevWordY) {
closeText();
pdf.append(1 0 0 1 + (rx / 1000f) + + (bl / 1000f)
+ Tm [ + startText);
prevWordY = bl;
textOpen = true;
} else {
// express the space between words in thousandths of an em