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-character><space>" (for example, ". ") and "<space><word-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 ("<space><word-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-character><space>") 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
int space = prevWordX - rx + prevWordWidth;
float emDiff = (float) space / (float) currentFontSize * 1000f;
// this prevents a problem in Acrobat Reader where large
// numbers cause text to disappear or default to a limit
if (emDiff < -33000) {
closeText();
pdf.append("1 0 0 1 " + (rx / 1000f) + " " + (bl / 1000f)
+ " Tm [" + startText);
textOpen = true;
} else {
pdf.append(Float.toString(emDiff));
pdf.append(" ");
pdf.append(startText);
}
// 2003-03-12, JV : kerning toevoegen voor spatie gevolgd door
karakter
if (kerningAvailable && l > 0) {
addKerning(pdf, (new Integer((int) ' ')), (new Integer((int)
area.getFontState().mapChar(s.charAt(0)))),
kerning, startText, endText);
}
}
prevWordWidth = area.getContentWidth();
prevWordX = rx;
for (int i = 0; i < l; i++) {
char ch = area.getFontState().mapChar(s.charAt(i));
if (!useMultiByte) {
if (ch > 127) {
pdf.append("\\");
pdf.append(Integer.toOctalString((int) ch));
} else {
switch (ch) {
case '(':
case ')':
case '\\':
pdf.append("\\");
break;
}
pdf.append(ch);
}
} else {
pdf.append(getUnicodeString(ch));
}
if (kerningAvailable && (i + 1) < l) {
addKerning(pdf, (new Integer((int) ch)),
(new Integer((int)
area.getFontState().mapChar(s.charAt(i + 1)))),
kerning, startText, endText);
}
}
pdf.append(endText);
currentStream.add(pdf.toString());
this.currentXPosition += area.getContentWidth();
}
}
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
