I'm trying to write application which need to draw many strings using Graphics2D
class in Java. I need to get sizes of each String object (to calculate exact position of each string).
There are so many strings that it should be done before the paint()
method is called and only once at the beginning of my program (so then I don't have Graphics2D
object yet). I know that there is a method Font.getStringBounds()
but it needs a FontRenderContext
object as a parameter.
When i tried to create my own object:
FontRenderContext frc = new FontRenderContext(MyFont.getTransform(), true, true)
and then obtain the strings bounds I've always get different sizes than when I obtain FontRenderContext
using Graphics2D.getFontRenderContext()
method inside paint()
. The differences are not big (about 1E-3) but I wonder why there is any difference at all?
However, is there any better and secure way to obtain sizes of a string?
Thnx for any help in advance!
Nev-ah. Gon-na. Happen.
The reason is the rendering and computation you're looking for from FRC is specific to a Graphics context, i.e. a specific Graphics2D object. The one you're interested in is one you're handed at runtime- it's like no other (you have to assume).
You can compute as much as you want using an FRC from some other Graphics2D, but your computations all all for naught when you try to use them at runtime with the Graphics2D paintComponent is handed, which is the Graphics2D you're going to use, no matter what.
So, yes, this would be nice but it's entirely theoretical. All that nice information is effectively locked away inside that FRC because without the exact Graphics2D the AttributedString is actually going to be drawn to, that FRC is worse than useless- it's an illusion you might actually try to embrace.
It makes sense, since everything really IS dependent on the Graphics2D you get handed at runtime. So the best thing to do is just accept it and write your code to call out from within paintComponent out to whatever objects and whatever specialized computation you have to do and build your design around the fact that THIS is the way things are.
I's a good question and a good thing to wish you could do, just, you can't. You see other people asking for this elsewhere on the web, in other forums. Notice the lack of useful answers and / or deafening silence.
Besides using FontMetrics
, a JLabel
can be used to determine the size of both unformatted and (basic HTML) rendered text. Here is an example.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
/** Sample code obtained from a thread on the Oracle forums that I cannot
locate at this instant. My question was related to an unexpected rendering of
JLabel. It was resolved by the 'added this' line courtesy of Darryl Burke. */
public class LabelRenderTest {
String title = "<html><body style='width: 160px; padding: 8px'>"
+ "<h1>Do U C Me?</h1>"
+ "Here is a long string that will wrap. "
+ "The effect we want is a multi-line label.";
LabelRenderTest() {
BufferedImage image = new BufferedImage(
640,
480,
BufferedImage.TYPE_INT_RGB);
Graphics2D imageGraphics = image.createGraphics();
GradientPaint gp = new GradientPaint(
20f, 20f, Color.blue,
620f, 460f, Color.white);
imageGraphics.setPaint(gp);
imageGraphics.fillRect(0, 0, 800, 600);
JLabel textLabel = new JLabel(title);
textLabel.setSize(textLabel.getPreferredSize()); // <==== added this
Dimension d = textLabel.getPreferredSize();
BufferedImage bi = new BufferedImage(
d.width,
d.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
g.setColor(new Color(255, 255, 255, 128));
g.fillRoundRect(
0,
0,
bi.getWidth(null),
bi.getHeight(null),
15,
10);
g.setColor(Color.black);
textLabel.paint(g);
Graphics g2 = image.getGraphics();
g2.drawImage(bi, 20, 20, null);
ImageIcon ii = new ImageIcon(image);
JLabel imageLabel = new JLabel(ii);
JOptionPane.showMessageDialog(null, imageLabel);
}
public static void main(String[] args) {
LabelRenderTest ist = new LabelRenderTest();
}
}
Edit 1:
As to your "many strings" comment. Paint the strings to a BufferedImage
that is only regenerated if needed. Use the buffered image each time paintComponent()
is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With