JDK-6488219 : Uneven character spacing when printing JTextComponent
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-10-31
  • Updated: 2013-08-20
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u10Fixed 7 b08Fixed
Related Reports
Relates :  
Relates :  
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 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.
07-12-2006

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.
01-11-2006