United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6488219 Uneven character spacing when printing JTextComponent
JDK-6488219 : Uneven character spacing when printing JTextComponent

Details
Type:
Bug
Submit Date:
2006-10-31
Status:
Closed
Updated Date:
2011-03-07
Project Name:
JDK
Resolved Date:
2011-03-07
Component:
client-libs
OS:
windows_xp
Sub-Component:
2d
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:

Related Reports
Backport:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b86, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Printing text using JTextArea and JTextPane results in noticably uneven spacing between characters, as though the wrong kerning information is being used.  Some letters have too much space after them, resulting in noticable gaps with the following letters.  Other letters have too little space after them, resulting in their touching the letters that follow them.

The problem only occurs in printed output, the screen display looks fine.

The problem occurs for JTextComponent subclasses but not when printing the same strings using drawString.

This was not a problem under JDK 1.4 but appeared starting with JDK 1.5 and continues in JDK 1.6.  In JDK 1.5 some more serious character spacing problems were fixed (see Bug Parade 4724061 and 4352983), but perhaps that solution introduced this new problem.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Build and run the program provided below under JDK 1.4, JDK 1.5 and JDK 1.6.  Carefully compare the spacing after the letters "m", "v", and "s" in the 1.4 output versus the 1.5 and 1.6 outputs.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Spacing should be consistent, as in the 1.4 output
ACTUAL -
Spacing is uneven, as in the 1.5 and 1.6 outputs.  For example, there is too much space following "m" and "s" and too little space after "v".


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import javax.swing.*;
import java.awt.print.*;

class Test implements Printable {
    static JTextArea area;

    static public void main(String args[]) {
        area = new JTextArea();
        area.setEditable(false);
        area.setText("marvelous suspicious solving");

        JFrame frame = new JFrame("Printing test");
        frame.getContentPane().add(area);
        frame.setSize(300,200);
        frame.setVisible(true);

        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(new Test(), job.defaultPage());
        if (job.printDialog()) {
            try { job.print(); }
            catch (Exception e) { }
        }
        System.exit(0);
    }

    public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {
        if (pageIndex >= 1) return Printable.NO_SUCH_PAGE;
        Graphics2D g2d = (Graphics2D)g;
        g2d.translate((int)pf.getImageableX(), (int)pf.getImageableY());
        g2d.setClip(0, 0, (int)pf.getImageableWidth(), (int)pf.getImageableHeight());
        try { area.print(g2d); }
        catch (Exception e) { }
        return Printable.PAGE_EXISTS;
    }

}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Revert to JDK 1.4

or

Print using drawString

Release Regression From : 1.4.2_12
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

                                    

Comments
EVALUATION

yes the fix for 4352983 introduces this.
The printer size character shapes and advances are different so
if they are spaced 'naturally' they do not fit the same width they
did on screen, and if they are spaced as per screen metrics they
do not look naturally spaced and sometimes abut.

A variant of the submitter's program below can be run
on 1.4 and 1.5 and shows that prior to 1.5 the space occupied
by the text did not match the component. That often lead to
clipped text.

import java.awt.*;
import javax.swing.*;
import java.awt.print.*;

class Test1 implements Printable {
    static JLabel label;
    static JFrame frame;

    static public void main(String args[]) {
        label = new JLabel();
        label.setFont(new Font("Dialog", Font.PLAIN, 12));
        label.setText("marvelous suspicious solving");

        frame = new JFrame("Printing test");
        frame.getContentPane().add(label);
        frame.pack();
        frame.setVisible(true);

        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(new Test1(), job.defaultPage());
        if (job.printDialog()) {
            try { job.print(); }
            catch (Exception e) { }
        }
        //System.exit(0);
    }

    public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {
        if (pageIndex >= 1) return Printable.NO_SUCH_PAGE;
        Graphics2D g2d = (Graphics2D)g;
        g2d.translate((int)pf.getImageableX(), (int)pf.getImageableY());
        g2d.setClip(0, 0, (int)pf.getImageableWidth(), (int)pf.getImageableHeight());
        try { frame.printAll(g2d); }
        catch (Exception e) { }
        return Printable.PAGE_EXISTS;
    }

}

For printing swingtext components in JDK6 there is a new API
javax.swing.JTextComponent.print(..) which layout for printing.
It can return a printable or can be used even more simply

import javax.swing.*;
import java.awt.print.*;

class Test2 {

    static public void main(String args[]) {
        JTextArea area = new JTextArea();
        area.setEditable(false);
        area.setText("marvelous suspicious solving");

        JFrame frame = new JFrame("Printing test");
        frame.getContentPane().add(area);
        frame.setSize(300,200);
        frame.setVisible(true);
        try {
          area.print();
        } catch (PrinterException e) {
        }
    }
}

This doesn't help existing code, but pre-existing code has problems either way
- prior to 1.5 : text clipped or too short
- in 1.5 and later : uneven spacing.

Its not clear what to do to improve matters. It needs some thought.
Making it into a screen resolution screen dump would fix the spacing
because screen resolution characters would be used but they'd be
grossly pixellated and so would everything else.

Essentially it needs a better way to distribute the white space
within a target width that is conscious of the true advance widths
of the characters at device resolution so as to minimise the
obviousness of this problem.
But white space isn't going to help as much in cursive scripts
such as Arabic and Indic.
                                     
2006-11-01
EVALUATION

The following seems to help (subjectively) enough to make it implementing
as a solution.

1. Use the printer graphics FontRenderContext to create a TextLayout laid out
for the printer. The reason is that it will contain per-character (or per-glyph)
advances that match those of the characters at screen resolution.

2. Use the screen FontRenderContext to measure the overall desired advance
of the string at screen-resolution.

3. Using the TextLayout from (1) and the screen width from (2), call
TextLayout. getJustifiedLayout(screenWidth) to return a new TextLayout
which is spaced better because it knows the true advances of each character.

Caveat: this can never be perfect when trying to fit printer resolution
text to a screen width.
                                     
2006-12-07



Hardware and Software, Engineered to Work Together