JDK-4908407 : 1.4 REGRESSION: Rotated Sans Serif font doesn't render properly when scaled
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 1.4.1,1.4.2
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2003-08-18
  • Updated: 2006-03-22
  • Resolved: 2006-03-22
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
6 b77Fixed
Related Reports
Relates :  
Description
BCDEFGHIJKLMNOPQRSTUVWZXYZ",-470,650);
		g2d.drawString("Arial 18B scaleX 50%, rotated left",-470,725);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",-470,700);
		g2d.rotate(Math.PI / 2);

		g2d.drawString("Arial 18B SCALEX 50%, NOT ROTATED",520,75);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",520,100);
		g2d.drawString("Arial 18B scaleX 50%, not rotated",520,125);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz",520,150);
	}
	public Dimension getPreferredSize()
	{	return new Dimension(750,550);
	}
	public Dimension getMinimumSize()
	{	return this.getPreferredSize();
}	}

---------- END SOURCE ----------
(Review ID: 183637)
======================================================================
Name: jk109818			Date: 08/18/2003


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

FULL OS VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

EXTRA RELEVANT SYSTEM CONFIGURATION :
DELL Latitude 610 laptop with docking station
Multiple displays on Radeon Mobility

A DESCRIPTION OF THE PROBLEM :
When displaying a rotated text string along the y-axis with a SansSerif font, the text string does not get rendered very well with Java 1.4.1. In Java 1.3.1 the rotated text string at least appeared legible and was acceptable even though it may have been "stretched" out.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Demo program provided below.
Compile and run under 1.3.1
Then
Compile and run under 1.4.1

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected rotated text in 1.4.1 to scale as in the earlier version (1.3.1) of Java. Though scaled text in Java 1.3.1 may appear "stretched" it is at least still legible and acceptable.
Rotated text string (representing Y-axis label) in SansSerif font does not scale nicely with verision 1.4.1. This was not the case with 1.3.1. With 1.4.1 depending on how a window (or panel) is sized the text can be:

1.) Text can appear blotchy and illegible.
2.) Single characters in text can appear to be a different font size than other characters within the text.
3.) Large gaps between characters within a word of text.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
   FontScalingProblem.java
*/
//package fontscalingproblem;

import javax.swing.UIManager;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FontScalingProblem
{
  private boolean packFrame = false;

  public FontScalingProblem()
  {
    PlotFrame frame = new PlotFrame();
    if (packFrame)
    {
      frame.pack();
    }
    else
    {
      frame.validate();
    }
//Center the window
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension frameSize = frame.getSize();
    if (frameSize.height > screenSize.height)
    {
      frameSize.height = screenSize.height;
    }
    if (frameSize.width > screenSize.width)
    {
      frameSize.width = screenSize.width;
    }
    frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
    frame.setVisible(true);
  }
//Main method
  public static void main(String[] args)
  {
    try
    {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    new FontScalingProblem();
  }
// Plot Frame
  public class PlotFrame extends JFrame
  {
    private JPanel contentPane;
    private GridLayout gridLayout1 = new GridLayout();
    private BorderLayout borderLayout1 = new BorderLayout();

    private static final int LABEL_PT_BASE = 36;
    private static final int windowWidth = 600, windowHeight = 600;
    private Font labelFont = new Font( "SansSerif", Font.PLAIN, LABEL_PT_BASE );
    private double baseWidth, baseHeight;

    public PlotFrame()
    {
      enableEvents(AWTEvent.WINDOW_EVENT_MASK);
      try
      {
        jbInit();
      }
      catch(Exception e)
      {
        e.printStackTrace();
      }
    }
//Component initialization
    private void jbInit() throws Exception
    {
      contentPane = (JPanel) this.getContentPane();
      gridLayout1.setColumns(1);
      gridLayout1.setHgap(5);
      gridLayout1.setRows(0);
      gridLayout1.setVgap(5);
      contentPane.setLayout(gridLayout1);
      contentPane.add( new PlotPanel() );
      contentPane.add( new PlotPanel() );
      contentPane.add( new PlotPanel() );
//    contentPane.add( new PlotPanel() );
      baseWidth = windowWidth; baseHeight = windowHeight;
      this.setSize(new Dimension(windowWidth, windowHeight));
      this.setTitle("Font Rotation and Scaling Problem");
    }
//Overridden so we can exit when window is closed
    protected void processWindowEvent(WindowEvent e)
    {
      super.processWindowEvent(e);
      if (e.getID() == WindowEvent.WINDOW_CLOSING)
      {
        System.exit(0);
      }
    }
// Plot Panel
    class PlotPanel extends JPanel
    {
      public void paintComponent( Graphics g )
      {
        double xScale, yScale;

        super.paintComponent( g );

        Dimension windowSize = this.getSize();
        Graphics2D g2d = (Graphics2D)g;
// Antialias text.
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// Set font
        g2d.setFont( labelFont );
// Set current scale from original base setting
        xScale = windowSize.width / baseWidth; yScale = windowSize.height / baseHeight;
        g2d.scale( xScale, yScale );
// Add axis lines
        g2d.drawLine( 80, 500, 525, 500 );
        g2d.drawLine( 80, 500, 80, 0 );
// Add axis labels
        g2d.drawString( "This is a X-axis label.", 75f, 550f );
// Rotate -90 degrees
        g2d.rotate( Math.toRadians( 270f ) );
        g2d.drawString( "This is a Y-axis label.", -500f, 75f );
      }
    }
  }
}
---------- END SOURCE ----------

Release Regression From : 1.3.1_08
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Incident Review ID: 182386) 
======================================================================

Name: jk109818			Date: 08/25/2003


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

FULL OS VERSION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Rotated, scaled Arial font is MANGLED on screen, but prints OK in J2SE 1.4.X.

The letter "N" is particularly noticeable, but the other characters are also pretty bad.

Problem ONLY OCCURS IN J2SE 1.4.X.

Problem does NOT occur in J2SE 1.3.X - 1.3 looks just fine on screen.

I have to keep all my customers on J2SE 1.3.1!

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run code below. Text with mangled fonts will display.

Click on the "Print" button at the top of the JFrame to see that printing is OK.

EXPECTED VERSUS ACTUAL BEHAVIOR :
The above code displays text in a JPanel, with a "Print" button to test how it prints.

Fonts used are Arial 12pt, 16pt, and 18pt, BOLD. The fonts are scaled in the X dimension 75%, 60%, and 50%, respectively.

I expected the fonts to display just like they do in J2SE 1.3.1, which is very nice.
The fonts look TERRIBLE, especially the Arial 18pt Bold, scaleX = 50%.

REPRODUCIBILITY :
This bug can be reproduced always.

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

public final class FontMain extends JApplet implements Pageable
{	private 				boolean 			inAnAppletFlag		= false;
	//private 				boolean 			printFlag				= false;
							PrinterJob			pj							= null;
	private				FontPanel			fontPanel				= null;
	private 				JFrame 				localOwnerFrame	= null;
	private				JButton 			printButton				= null;
	private				BorderLayout 	borderLayout 			= new BorderLayout(5,10);
	private				PageFormat		localPageFormat	= null;
	private				Paper				localPaper				= null;

	public FontMain(JFrame ownerFrame)
	{	super();
		localOwnerFrame = ownerFrame;
		localOwnerFrame.addWindowListener(new FontWindowAdapter(ownerFrame));
		inAnAppletFlag	= false;
	}
	public FontMain() //**** constructor for Applet - need a Frame
	{	super();
		inAnAppletFlag	= true;
	}
	public void init() //**** APPLET init() method - also called by main()
	{	if (printButton == null)
		{	this.getContentPane().setLayout(borderLayout);
			this.getContentPane().setBackground(Color.white);
			if (localOwnerFrame != null)
			localOwnerFrame.setBackground(Color.white);

			printButton	= new JButton("------------------- Click HERE to PRINT This Page -------------------");
			printButton.setBackground(Color.white);
			printButton.setForeground(Color.black);
			this.getContentPane().add("North", printButton);

			fontPanel			= new FontPanel(this);
			this.getContentPane().add("Center", fontPanel);

			printButton.addActionListener(new FontActionListener());

			this.validate();
	}	}
	public void start() {}
	public void stop() {}

	final void pageSetup()
	{	if (pj == null)
		{	pj							= PrinterJob.getPrinterJob();
			pj.setPageable(this);
		}
		//First time thru
		if (localPageFormat == null)
		{	localPageFormat	= pj.defaultPage();
			localPageFormat.setOrientation(PageFormat.LANDSCAPE);
		}
		localPaper					= localPageFormat.getPaper();
		localPaper.setImageableArea(8.0d,26.0d,(double)(localPaper.getWidth()-16),(double)(localPaper.getHeight()-52));
		localPageFormat.setPaper(localPaper);
	}
	void printDocument()
	{	this.pageSetup();

		if (pj != null)
		{	if (pj.printDialog())
			{	try
				{	pj.print();
				}
				catch(Throwable e)
				{	System.err.println("print() Throwable = " + e);
		}	}	}
		else
		{	System.err.println("Printing Canceled by User");
		}
		pj	=	null;
	}
	public final int getNumberOfPages()
	{	return 1;
	}
	public final PageFormat getPageFormat(int parmpagenum)
	{	return this.localPageFormat;
	}
	public final Printable getPrintable(int parmpagenum)
	{	return this.fontPanel;
	}
	public final String getAppletInfo()
	{	return "Rotated, Scaled Font Bug Test";
	}
	public Dimension getMinimumSize()
	{	return this.getPreferredSize();
	}
	public Dimension getPreferredSize()
	{	return new Dimension(800,570);
	}

	final class FontActionListener implements ActionListener
	{	public void actionPerformed(ActionEvent e)
		{	printDocument();
	}	}

	final class FontWindowAdapter extends WindowAdapter
	{	JFrame localOriginFrame;
		public FontWindowAdapter(Component comp)
		{	if(comp instanceof JFrame)
			{	localOriginFrame = (JFrame)comp;
		}	}
		public void windowClosing(WindowEvent we)
		{	if(! inAnAppletFlag)
				System.exit(0);
	}	}

	public static void main(String args[])
	{	JFrame frame1 				= new JFrame("Rotated, Scaled Font Bug Test");

		FontMain fontPanel 			= new FontMain(frame1);

		fontPanel.inAnAppletFlag 		= false;

		fontPanel.init();
		fontPanel.resize(fontPanel.getPreferredSize());

		frame1.getContentPane().add(fontPanel);
		frame1.setSize(fontPanel.getSize());
		frame1.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame1.pack();
		frame1.setVisible(true);
		fontPanel.start();
}	}

final class FontPanel extends JPanel implements Printable
{	private					FontMain				fontMain							= null;
	private					Graphics2D			g2d									= null;
	private					RepaintManager		currentRepaintManager		= null;
	private					Font 						font12B 							= new Font("Arial", Font.BOLD, 12);
	private					Font 						font16B 							= new Font("Arial", Font.BOLD, 16);
	private					Font 						font18B 							= new Font("Arial", Font.BOLD, 18);
	private					String					version								= System.getProperty("java.version");

	FontPanel(FontMain parentJApplet)
	{	super();
		fontMain				= parentJApplet;
		this.setLayout(null);
		this.setBackground(Color.white);
	}

	public int print(java.awt.Graphics parmg, java.awt.print.PageFormat parmPageFormat, int parmPageIndex)
	{	if (parmPageIndex != 0)
		{	return NO_SUCH_PAGE;
		}
		g2d							=	(Graphics2D) parmg;
		this.disableDoubleBuffering();

		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,	RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.setRenderingHint(RenderingHints.KEY_RENDERING,		RenderingHints.VALUE_RENDER_QUALITY);

		g2d.translate(26,8);
		this.drawFonts(g2d, parmPageIndex);
		return Printable.PAGE_EXISTS;
	}

	private void disableDoubleBuffering()
	{	currentRepaintManager	=	RepaintManager.currentManager(this);
		currentRepaintManager.setDoubleBufferingEnabled(false);
	}

	private void enableDoubleBuffering()
	{	currentRepaintManager	=	RepaintManager.currentManager(this);
		currentRepaintManager.setDoubleBufferingEnabled(true);
	}
	public void paintComponent(Graphics parmg)
	{	super.paintComponent(parmg);
		g2d						=	(Graphics2D) parmg;
		this.enableDoubleBuffering();
		g2d.translate(36,18);
		this.drawFonts(g2d,0);
	}
	private void drawFonts(Graphics2D g2d, int parmPageIndex)
	{	g2d.setColor(Color.black);
		g2d.setFont(font18B);

		g2d.drawString("Java version " + version, 25, 50);

		font12B		= font12B.deriveFont(AffineTransform.getScaleInstance(0.75d,1.0d));
		g2d.setFont(font12B);

		g2d.rotate(Math.PI / 2);
		g2d.drawString("Arial 12B SCALEX 75%, ROTATED RIGHT",200,-100);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",200,-75);
		g2d.drawString("Arial 12B scaleX 75%, rotated right",200,-50);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",200,-25);
		g2d.rotate(-Math.PI / 2);

		g2d.rotate(-Math.PI / 2);
		g2d.drawString("Arial 12B SCALEX 75%, ROTATED LEFT",-470,150);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",-470,175);
		g2d.drawString("Arial 12B scaleX 75%, rotated left",-470,200);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",-470,225);
		g2d.rotate(Math.PI / 2);

		g2d.drawString("Arial 12B SCALEX 75%, NOT ROTATED",25,75);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",25,100);
		g2d.drawString("Arial 12B scaleX 75%, not rotated",25,125);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",25,150);

		font16B		= font16B.deriveFont(AffineTransform.getScaleInstance(0.6d,1.0d));
		g2d.setFont(font16B);

		g2d.rotate(Math.PI / 2);
		g2d.drawString("Arial 16B SCALEX 60%, ROTATED RIGHT",200,-275);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",200,-250);
		g2d.drawString("Arial 16B scaleX 60%, rotated right",200,-325);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",200,-300);
		g2d.rotate(-Math.PI / 2);

		g2d.rotate(-Math.PI / 2);
		g2d.drawString("Arial 16B SCALEX 60%, ROTATED LEFT",-470,375);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",-470,400);
		g2d.drawString("Arial 16B scaleX 60%, rotated left",-470,425);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",-470,450);
		g2d.rotate(Math.PI / 2);

		g2d.drawString("Arial 16B SCALEX 60%, NOT ROTATED",275,75);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",275,100);
		g2d.drawString("Arial 16B scaleX 60%, not rotated",275,125);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",275,150);

		font18B		= font18B.deriveFont(AffineTransform.getScaleInstance(0.5d,1.0d));
		g2d.setFont(font18B);

		g2d.rotate(Math.PI / 2);
		g2d.drawString("Arial 18B SCALEX 50%, ROTATED RIGHT",200,-525);
		g2d.drawString("ABCDEFGHIJKLMNOPQRSTUVWZXYZ",200,-500);
		g2d.drawString("Arial 18B scaleX 50%, rotated right",200,-575);
		g2d.drawString("abcdefghijklmnopqrstuvwxyz0123456789",200,-600);
		g2d.rotate(-Math.PI / 2);

		g2d.rotate(-Math.PI / 2);
		g2d.drawString("Arial 18B SCALEX 50%, ROTATED LEFT",-470,675);
		g2d.drawString("A

Comments
EVALUATION This bug was fixed as part of changes for 4654540 by keeping proportions in transformation matrix used for hinting and ensuring hinting engine deals with positive stretch transformations only. See attachments for before/after examples.
14-03-2006

EVALUATION Too late for tiger. ###@###.### 2003-12-11
11-12-2003