How to get scale to work properly on text

I have been having problems scaling down text to fit inside a 5 by 7 grid as show here:

http://forum.java.sun.com/thread.jspa?threadID=5181810

Unable to get an answer I decided to simplify the problem I am having so I have come armed with a simple example and images.

First off I used the drawString() method to draw the letter M onto a panel. I took a screen shot and counted the width and height in pixels. Armed with this information I added a rectangle with the same dimensions. To show they are both the same I took a screen shot and zoomed in as you can see in this image both the M and rectangle are 7 by 9

http://img260.imageshack.us/img260/8062/beforezg2.jpg

Using simple maths I then added in the scale method that would scale the rectangle and M to 5 by 7 .

g2d.scale((gridWidth/charWidth),(gridHeight/charHeight));

I ran the program and again took a screen shot and counted the pixels again

As you can see here

http://img255.imageshack.us/img255/2937/afterhb0.jpg

It worked fine for the rectangle that got resized to a 5 by 7 grid yet the M got resized to a 6 by 6 grid.

Does anyone know why this is happening and how to get round it so I can scale the text to a specific grid?

Thanks

(For your convenience here is the code that I used)

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import javax.swing.JFrame;

class MyCanvas2extends JComponent{

public MyCanvas2(){

}

publicvoid paint(Graphics g){

double gridWidth = 5.0;

double gridHeight = 7.0;

double charWidth = 7.0;

double charHeight = 9.0;

Graphics2D g2d = (Graphics2D)g;

BufferedImage image =

new BufferedImage((int)(this.getWidth()),

(int)(this.getHeight()),

BufferedImage.TYPE_INT_RGB);

g2d = (Graphics2D)image.createGraphics();

// Set background colour to white

g2d.setColor(Color.WHITE);

g2d.fillRect(0,0,(int)(this.getWidth()),

(int)( this.getHeight()));

// Set text written in Black

g2d.setColor(Color.BLACK);

// Commented out for now to get screenshot before scale

//g2d.scale((gridWidth/charWidth),(gridHeight/charHeight));

g2d.drawString("M", (this.getWidth()/2)+10, (this.getHeight()/2));

g2d.fillRect((this.getWidth()/2),(this.getHeight()/2),(int)charWidth,(int)charHeight);

g.drawImage(image,0,0,null);

}

}

publicclass TestScale{

publicstaticvoid main(String[] a){

JFrame window =new JFrame();

window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

window.setBounds(30, 30, 200, 200);

window.getContentPane().add(new MyCanvas2());

window.setVisible(true);

}

}

[4320 byte] By [4fingersa] at [2007-11-27 7:11:28]
# 1

Use the actual width and height of the String together with your

final/desired display width and height to calculate the scale factors.

import java.awt.*;

import java.awt.event.*;

import java.awt.font.*;

import java.awt.geom.*;

import javax.swing.*;

public class ScalingSmallText extends JComponent implements ActionListener {

double scale = 1.0;

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,

RenderingHints.VALUE_FRACTIONALMETRICS_ON);

g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,

RenderingHints.VALUE_STROKE_PURE);

int w = getWidth();

int h = getHeight();

double[][] sizes = { {5.0,7.0}, {6.0,6.0}, {9.0,7.0}, {8.0,6.0} };

String[] s = { "M", "T", "X", "S" };

Font font = g2.getFont();

FontRenderContext frc = g2.getFontRenderContext();

LineMetrics lm = font.getLineMetrics("0", frc);

float height = lm.getAscent() + lm.getDescent();

for(int j = 0; j < sizes.length; j++) {

float width = (float)font.getStringBounds(s[j], frc).getWidth();

double xScale = scale*sizes[j][0]/width;

double yScale = scale*sizes[j][1]/height;

double x = (w/(3.0))*(1 + j%2);

double y = (h/(3.0))*(1 + j/2);

//System.out.printf("xScale = %.2f yScale = %.2f%n", x, y);

AffineTransform at = AffineTransform.getTranslateInstance(x, y);

at.scale(xScale, yScale);

g2.setFont(font.deriveFont(at));

g2.setPaint(Color.blue);

g2.drawString(s[j], 0, 0);

y -= lm.getAscent()*yScale;

g2.setPaint(Color.red);

Rectangle2D r = new Rectangle2D.Double(x, y, sizes[j][0], sizes[j][1]);

at.setToTranslation((1.0-scale)*r.getX(), (1.0-scale)*r.getY());

at.scale(scale, scale);

g2.draw(at.createTransformedShape(r));

}

}

public void actionPerformed(ActionEvent e) {

boolean selected = ((JToggleButton)e.getSource()).isSelected();

scale = selected ? 4.0 : 1.0;

repaint();

}

private JPanel getEnlarger() {

JToggleButton button = new JToggleButton("enlarge");

button.addActionListener(this);

JPanel panel = new JPanel();

panel.add(button);

return panel;

}

public static void main(String[] args) {

ScalingSmallText test = new ScalingSmallText();

JFrame f = new JFrame();

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(test);

f.getContentPane().add(test.getEnlarger(), "Last");

f.setSize(400,400);

f.setLocationRelativeTo(null);

f.setVisible(true);

}

}

crwooda at 2007-7-12 19:03:06 > top of Java-index,Security,Cryptography...
# 2

Thank you for your suggestion and after looking through the code it seems to increase or decrease the text by a scale factor of four.

This sadly still doesn抰 quite answer my question of how to get it to fit inside a 5 by 7 grid using the scale method (or any other method you can think of).

All I need to know is the maths of how it will work I should be able to do the rest. If you can answer that then I will gladly give you the duke points.

Thank you again

4fingersa at 2007-7-12 19:03:06 > top of Java-index,Security,Cryptography...
# 3

after looking through the code it seems to increase or decrease the text by a scale

factor of four

The idea behind the four is simply to enlarge the graphic so you can see what is drawn.

Specifically, to inspect the the fit of the glyph and the enclosing rectangle.

I tried to show how to scale the text to any arbitrary width and height. I don't understand

the idea of your grid and might be able to do more if you can explain it. You seem to be

working on an order of magnitude of single-digit pixels.

The scaling technique is demonstrated clearly in the example. You use the width or height

dimension of your desired display size and divide it by the respective width or height of

the glyph.

double xScale = desired_display_width/width_of_glyph;

double yScale = desired_display_height/height_of_glyph;

Use the rendering hints to get a more accurate rendering — this may have been (some

of) the difficulty you were experiencing when you scaled from 7 x 9 to 5 x 7 and ended up

with a 6 x 6 glyph.

Using an AffineTransform makes the scaling easy to set and apply.

crwooda at 2007-7-12 19:03:06 > top of Java-index,Security,Cryptography...