I've read all the existing questions I could find and have tried it both ways.
FontMetrics fm = g.getFontMetrics();
FontRenderContext frc = g.getFontRenderContext();
Rectangle2D rect = font.getStringBounds(line,frc);
int width = (int)rect.getWidth();
and also
int width = fm.stringWidth(line);
Neither are giving me the correct number of pixels.
As an example...the word 'ELLIS' in a particular font and size is actually 58 pixels wide.
Both of those methods tell me that it's 42 wide.
Its black font on a white field, so I'm considering re arranging my entire code so I can loop through a line of the BufferedImage and count the distance between the first and last black pixel. This would at least get me a lot closer.
There has to be a simpler way to do this though.
Appreciate any help.
I have a Font object and I need both the width and height of the font. I know that the getSize() returns the height of the font as the point-size of a font is generally the height, but I'm at a loss when it comes to determining the width of the font.
Alternatively, being able to determine the width of each specific character supported by the Font would also be an acceptable solution.
My best guess is that the width is specified when using the loadFont method, but the documentation does not specify whether the size parameter represents the width or the height of the font.
All fonts being used are mono-space fonts such as DejaVu Sans Mono, GNU FreeMono, and Lucida Sans Unicode.
I have run into the same problem, and there seems to be no easy way. My solution was to create a Text element with the correct font and check it's size:
double computeTextWidth(Font font, String text, double wrappingWidth) {
Text helper = new Text();
helper.setFont(font);
helper.setText(text);
// Note that the wrapping width needs to be set to zero before
// getting the text's real preferred width.
helper.setWrappingWidth(0);
helper.setLineSpacing(0);
double w = Math.min(helper.prefWidth(-1), wrappingWidth);
helper.setWrappingWidth((int)Math.ceil(w));
double textWidth = Math.ceil(helper.getLayoutBounds().getWidth());
return textWidth;
}
If I remember correctly the trick here is to set prefWidth to -1. See the JavaDoc.
FontMetrics metrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(font);
float charWidth = metrics.computeStringWidth("a");
float charHeight = metrics.getLineHeight();
I want to align a String drawn by a canvas perfectly in the middle. So if I have code like so
Paint p = new Paint();
Canvas c = holder.lockCanvas();
String centered_text = "Hello World";
and then set my Paint style and size
p.setTextSize(35);
p.setStyle(Style.STROKE);
Now to draw my String in the middle I would do
c.drawText(centered_text, c.getWidth()/2, c.getHeight()/2, p);
But this wouldn't actually center my text, it would put the top left corner in the center. To center it I would need to know the Strings litteral width and height in pixels not characters.
int string_width = centered_text.getWidth(); //String.getWidth() is not a real methode
int string_height = centered_text.getHeight(); //String.getHeight() is not a real methode
c.drawText(centered_text, (c.getWidth()/2)-(string_width/2), (c.getHeight()/2)-(string_height/2), p);
This would center the text but String.getWidth() and String.getHeight() are not real methodes. So my idea is to use the size of the text and the size of the String to find the width and the height. So something along te lines of:
int string_width = centered_text.length()*p.getTextSize();
int string_height = p.getTextSize();
But I feel like this is wrong because different chars are different sizes... anyone have any ideas.
Assuming you're in Java Swing, what you want is FontMetrics.
new FontMetrics(canvas.getFont()).getStringBounds(someString, canvas.getGraphics());
will return a Rectangle2D with the size of your string in it.
Is there any way how to determine the optimal canvas size for text rendering?
The input is a string with newlines, I want to contruct the canvas to fit (no insets) while using both font types - proportional and non-proportional, these types will be never mixed.
Thanks.
From the Java Tutorial Measuring Text
FontMetrics metrics = graphics.getFontMetrics(font);
int hgt = metrics.getHeight();
int adv = metrics.stringWidth(text);
Dimension size = new Dimension(adv+2, hgt+2);
You probably need to do this line by line of your text and detect whether your font changes between lines.
Is there a friendlier way to get an instance of FontMetrics than
FontMetrics fm = Graphics.getFontMetrics(Font);
I hate this way because of the following example:
If you want to create in a game a menu and you want all the menuitems in the center of the screen you need fontmetrics. But, mostly, menuitems are clickable. So I create an array of Rectangles and all the rectangles fits around the items, so when the mouse is pressed, I can simply use
for (int i = 0; i < rects.length; i++)
if (rects[i].contains(mouseX, mouseY)) { ... }
But to create the rects I also need FontMetrics for their coordinates. So this mean that I have to construct all my rectangles in the paint-method of my menu.
So I want a way to get the FontMetrics so I can construct the Rectangles in a method called by the constructor.
For me the easiest way was to:
Font font = new Font("Helvetica",Font.PLAIN,12);
Canvas c = new Canvas();
FontMetrics fm = c.getFontMetrics(font);
Benefits:
If you call c.getGraphics() it will return null (thus there is no graphics object)
This (canvas) will also work in headless mode.
Now you can easily get height and width...
The really correct answer is to use Toolkit.
Font font = new Font("Courier New", Font.PLAIN, 14);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
Once the background component, i.e. whatever is behind your menu, has been rendered, it has a Graphics object that you can use to get the metrics for a given font, just once.
You certainly don't want to be doing this in the paint method, which should be as lightweight as possible. I'd hang this code on a listener that gets called when the component is first rendered. It can store the resulting FontMetrics object somewhere where you can later access it, either in a paint method for drawing those menu item boxes.
Rather than determining the measurements of your menu graphics at the last moment, i.e. when painting, it might be a good idea instead to create some components to represent your menu. You can place those components on the Glass Pane more info here so they'll float above everything else, and you'll have the added bonus that those components are all capable of accepting mouse clicks and firing listener events on them, and since they only capture events on their own geometry you don't even have to figure out which part of menu was hit by the click, if at all.
Another advantage of using components here is that you may entirely get around the requirement for fiddling with font metrics. There are ready-made menu items, or you could just use JLabels, and you can specify their alignment, you can use a LayoutManager to size the boxes to the width of the biggest label, and so forth.
Assuming the menu text is fixed, you could pre-draw the text to a BufferedImage with alpha transparency and make your calculations then. Then, when you need the menu text, just draw the image.
You'll still have to do some offset calculations to centre the image (assuming the panel size can change), but these should be relatively lightweight.
I think this is a good solution
private static HashMap<Font, FontMetrics> fontmetrics = new HashMap<Font, FontMetrics>();
public static FontMetrics getFontMetrics(Font font)
{
if (fontmetrics.containsKey(font))
{
return fontmetrics.get(font);
}
FontMetrics fm = createFontMetrics(font);
fontmetrics.put(font, fm);
return fm;
}
private static FontMetrics createFontMetrics(Font font)
{
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = bi.getGraphics();
FontMetrics fm = g.getFontMetrics(font);
g.dispose();
bi = null;
return fm;
}
Adding to what Lonzak said, how about this:
public static FontMetrics getFontMetrics(Font font){
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration config = gd.getDefaultConfiguration();
Canvas c = new Canvas(config);
return c.getFontMetrics(font);
}
You could store the 'config' variable as a static variable so it is constructed once in some utility font class that contains other font related information for your game/development environment. I guess you could also do this with the canvas variable.
Updated recommendation. FontMetrics is deprecated. Use LineMetrics instead.
String text = "some string";
FontRenderContext frc = new FontRenderContext(font.getTransform(), true, true);
LineMetrics lm = font.getLineMetrics(text, frc);
However, some methods such as SwingUtilities.computeStringWidth require a FontMetrics instance. Another option is to compute the bounds of the String.
Rectangle2D bounds = font.getStringBounds(text, frc);
Then the width and height may be obtained from the bounds.