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);
}
}
# 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.