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]

Reply via email to