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 p
for historical sake,here's how I think he did it originally (jruby java pseucodoe)
font = UIManager.getFont("Label.font")
frc = java.awt.font.FontRenderContext.new(font.transform, true, true)
textLayout = java.awt.font.TextLayout.new(text, font, frc)
textLayout.bounds.width
Here is a snippet of code that does something similar -- wrote it for abbreviating the string to a given number of pixels.
public static String abbreviate(final Graphics2D g2, final String text, final int fitToWidth) {
// define how many characters in the caption can be drawn
final FontMetrics fm = g2.getFontMetrics();
Rectangle2D textBounds = fm.getStringBounds(text, g2);
int count = text.length();
while ((textBounds.getWidth() > fitToWidth) && (count > 4)) {
textBounds = fm.getStringBounds(text.substring(0, count--), g2);
}
return count == text.length() ? text : StringUtils.abbreviate(text, count);
}
Try with the FontMetrics class; the stringWidth method returns the size of a string. An example:
JComponent c = getSomeKindOfJComponent();
FontMetrics fm = c.getFontMetrics(c.getFont()); // or another font
int strw = fm.stringWidth("My text");
You might also want to check out SwingUtilities.computeStringWidth
.
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.
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.