JDK-4724061 : Misspacing in Printer Output of Styled Text
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.3.1,1.4.0,1.4.1_01
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_98,windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2002-07-31
  • Updated: 2003-09-24
  • Resolved: 2003-09-24
Related Reports
Duplicate :  
Description

Name: jk109818			Date: 07/31/2002


FULL PRODUCT VERSION :

java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Printer Kyocera Ecosys FS 1000+

A DESCRIPTION OF THE PROBLEM :
I reported this bug which appears in Java 1.3.1 and
1.4beta3 in December 2001 (11-12-2001) and got answer by
Daniel Dresser in January 2002 that it will be fixed in
final release of Java 1.4. But this bug still appears. I
presume the bug is related to Bug 4625837 and 4352983:

I am printing a JTextPane which contains styled text. On
screen the text is
layouted fine, also in print preview, but on printer output
there is missing or
too large space between words which belong to different
attribute sets.
In the example below, the text is printed as
"When all myfive and country senses see"
without space between "my" and "five", the letters "y"
and "f" are touching.
The word "five" is correctly printed in bold.
In other cases, there is to much space between words. If
the fontsize is set to
little values like 8, some words will be printed one upon
another. The problem
appears on different printers.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the code example below!
2.
3.

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.plaf.basic.*;


public class PrintingStyledText extends JFrame {

	private PrintableTextPane textPane;

	public PrintingStyledText() {

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});


		final PrinterJob prnJob = PrinterJob.getPrinterJob();

		textPane = new PrintableTextPane();
		prnJob.setPrintable(textPane);

		JButton printButton = new JButton("Print");
		printButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				if (prnJob.printDialog()) {
					try {
						prnJob.print();
					} catch (PrinterException e) {
						e.printStackTrace();
						System.err.println("Printing
error " + e.toString());
					}
				}
			}
		});

		JPanel panel = (JPanel)getContentPane();
		panel.setLayout(new BorderLayout());
		panel.add(textPane, BorderLayout.CENTER);
		panel.add(printButton, BorderLayout.SOUTH);
	}

	public static void main(String[] args) {
		JFrame frame = new PrintingStyledText();
		frame.setLocation(400, 100);
		frame.pack();
		frame.show();
	}

	class PrintableTextPane extends JTextPane implements Printable {

		String dylanThomas = "When all my five and country senses
see\n";

		public PrintableTextPane() {
			super();
			DefaultStyledDocument document = (DefaultStyledDocument)
getDocument();

			SimpleAttributeSet attributes = new SimpleAttributeSet
();
			StyleConstants.setFontFamily(attributes, "Arial");
			SimpleAttributeSet boldAttribute = new
SimpleAttributeSet();
			StyleConstants.setBold(boldAttribute, true);

			int offset = 0;
			for (int i = 1; i < 31; i++) {
				try {
					offset = document.getLength();
					StyleConstants.setFontSize(attributes,
i);
					document.insertString(offset,
dylanThomas, attributes);
					document.setCharacterAttributes(offset
+ 12, 4, boldAttribute, false);
				} catch (BadLocationException ble) {
					System.out.println
("BadLocationException");
				}
			}
		}

		public int print(Graphics g, PageFormat pageFormat, int
pageIndex) {
			if (pageIndex != 0) return NO_SUCH_PAGE;
			Graphics2D g2 = (Graphics2D)g;
			g2.translate(pageFormat.getImageableX(),
pageFormat.getImageableY());

			setDoubleBuffered(false);
			paint(g2);
			setDoubleBuffered(true);

			return PAGE_EXISTS;

		}

	}
}
---------- END SOURCE ----------
(Review ID: 145768) 
======================================================================

Name: jk109818			Date: 08/02/2002


FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)


FULL OPERATING SYSTEM VERSION :
Windows 98 [Version 4.10.2222]


A DESCRIPTION OF THE PROBLEM :

If you print a JTextPane containing lines with plain text
with the occasional bold, italic, or underlined text, the
bold, italic, or underlined text can wind up shifted left or
right relative to where it should be.  The result is either
extra spaces or overlapping of characters and can be as much
as several characters wide, it is not a small effect.

The effect does not occur on the screen.  It appears to be
dependent on the scaling that is done when imaging for the
printer (300/72, etc.), and the amount of the effect is
different on 300dpi, 400dpi, and 600dpi printers.  I have
verified that you get the same effects if you draw the
JTextPane to the screen using a different scaling factors,
so I believe it is a Graphics2D problem, not a printer,
printer driver, or PDL mapping problem.

The effect is dependent on the text that appears before the
style change on the line.  Style changes early in the line
don't show the problem.  Style changes later in the line can
shift left or right depending on the earlier characters.
The problem appears to be dependent on cummulative font
metrics.  The styled text shifts right after lots of
preceding narrow characters like ........... and shifts left
after lots of preceding wide characters like @@@@@@@@@@@@@.
 The effect is also font size dependent, some sizes hardly
show the problem, larger sizes tend to be worse.  Defining
the JTextPane with HTML or StyledDocument makes no
difference, you get the identical behavior in both cases.  I
tried both 1.4 and 1.3.1 and the problem is the same.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the program provided
2. Print on different printers


EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected: Printed output should be identical (WYSIWYG) to
the JTextPane displayed on the screen.
Actual: bold, italic, and underlined words shift several
characters left or right relatie to their correct positions.


REPRODUCIBILITY :
This bug can be reproduced always.

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

class Test implements Printable {
    static JTextPane pane;
    
    static public void main(String args[]){
        pane = new JTextPane();
        pane.setContentType("text/html");
        pane.setText("<html>"
        +"<font face=Serif size=-1>plain<b>bold</b>plain</font><br>"
        +"<font size=+0>plain<b>bold</b>plain</font><br>"
        +"<font size=+1>plain<b>bold</b>plain</font><br>"
        +"<font size=-1>@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=+0>@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=+1>@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=-1>@@@@@@@@@@@@@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=+0>@@@@@@@@@@@@@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=+1>@@@@@@@@@@@@@@@@@@@@@@plain<b>bold</b>plain</font><br>"
        +"<font size=-1>plain<i>italic</i>plain</font><br>"
        +"<font size=+0>plain<i>italic</i>plain</font><br>"
        +"<font size=+1>plain<i>italic</i>plain</font><br>"
        +"<font size=-1>@@@@@@@@@@plain<i>italic</i>plain</font><br>"
        +"<font size=+0>@@@@@@@@@@plain<i>italic</i>plain</font><br>"
        +"<font size=+1>@@@@@@@@@@plain<i>italic</i>plain</font><br>"
        +"</html>");

        JFrame frame = new JFrame("Printing test");
        frame.getContentPane().add(pane);
        frame.setSize(600,800);
        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 { pane.print(g2d); }
        catch (Exception e) { }
        return Printable.PAGE_EXISTS;
    }
    
}


---------- END SOURCE ----------
(Review ID: 160227)
======================================================================

Comments
EVALUATION I believe Swing already have a bug on this, and it can be demonstrated on screen if you apply a scale to the Graphics used by Swing. Its is rooted in a couple of things 1) Screen resolution text doesn't scale linearly up to printer resolution because of the effects of tuning for screens by font designers. 2) Swing doesn't measure the text in the context in which it will be displayed. The different text segments are being drawn with separate text calls, and their locations are calculated (ie the string lengths are measured) assuming it will be displayed at 72dpi. In order to get "WSYWIG" at all pt sizes Swing would need to use TextLayout or GlyphVector. These can perform layout at the screen resolution and will then draw individual glyphs using those pre-calculated positions. Thus the overall string length that results can be made to match screen metrics. This is not without its costs - code would need to be changed, more expensive APIs would need to be used, and text that is measured for screen resolution may not look ideal on the printer. But on that last point it is clearly better than what is happening currently. Also the printing code does not always use GDI/Postscript text primitives when printing a TextLayout. In particular if anything other than default layout is mandated it would not be able to do so in the current implementatation. This would lead to larger spool files but that is probably not as significant a problem for UI screen dumps, and in any case can be addressed to some degree by assigning positions whilst still using the GDI or postscript primitives. ###@###.### 2002-08-02 ============================
02-08-2002