114

How to calculate the length (in pixels) of a string in Java?

Preferable without using Swing.

EDIT: I would like to draw the string using the drawString() in Java2D and use the length for word wrapping.

eflles
  • 6,606
  • 11
  • 41
  • 55
  • 5
    Without Swing? What device are you using? What font? What size? What style? All these things change the display width. – S.Lott Nov 03 '08 at 12:31
  • 1
    @S. Lott. You have it in one. I was tempted to close this as a non-question. – David Arno Nov 03 '08 at 12:34
  • For .NET equivalent there is TextRenderer class, see http://stackoverflow.com/questions/604298/creating-columns-of-text-with-a-variable-width-font/604323#604323 – Spoike Sep 22 '09 at 09:01
  • 4
    How are you going to draw the string? With AWT? Or with some other toolkit? The size of the string in pixels depends on the drawing API that will draw the pixel later on (and of course which font you use and what font size and if the font is bold/italic, etc.). W/o knowing the drawing API and the font properties, a string has no size whatsoever. – Mecki Nov 03 '08 at 12:33

5 Answers5

167

If you just want to use AWT, then use Graphics.getFontMetrics (optionally specifying the font, for a non-default one) to get a FontMetrics and then FontMetrics.stringWidth to find the width for the specified string.

For example, if you have a Graphics variable called g, you'd use:

int width = g.getFontMetrics().stringWidth(text);

For other toolkits, you'll need to give us more information - it's always going to be toolkit-dependent.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 10
    It might be clearer if you provide an example usage, initially I attempted to use it as a static method, as you've written it that way. – Aequitas Jul 15 '15 at 23:13
  • Deprecated! but still the best method!, Why do they deprecate a method when there is not a better way!!! I just don't get it! – Iman Nia Apr 09 '17 at 13:16
  • 2
    @Zich: I'm not sure which method you're saying is deprecated - neither of them are marked as deprecated in the Java 8 documentation as far as I can see... – Jon Skeet Apr 09 '17 at 13:20
  • 'getFontMetrics' from class 'FontMetric' is deprecated, at least in my eclipse! – Iman Nia Apr 09 '17 at 13:32
  • Take a look at this : http://imgur.com/UWsW6hD Do you think I should report it as an eclipse bug? – Iman Nia Apr 09 '17 at 13:35
  • 3
    @Zich: It's a method in `Graphics`, not `FontMetrics`. But you're calling `Toolkit.getFontMetrics`, which is indeed deprecated, and which isn't what this method talks about... you need to be really careful about this kind of thing, particularly before you start talking about reporting bugs... – Jon Skeet Apr 09 '17 at 13:36
  • `Toolkit.getDefaultToolkit()` returns a `FontMetric` it is the same I guess. – Iman Nia Apr 09 '17 at 13:38
  • 1
    @Zich: Well I wouldn't guess - I would use the non-deprecated method, or the non-deprecated methods that `Toolkit.getFontMetrics` suggests instead. – Jon Skeet Apr 09 '17 at 13:43
  • For SWT you could do the following: http://ysgong.blogspot.de/2009/04/calculate-width-of-string-in-swt.html `GC gc = new GC(drawable); Point size = gc.textExtent(string); gc.dispose ();` then size.x is the width of the string when it is displayed by drawable. – Naxos84 Sep 25 '17 at 12:06
  • simple enough, I guess I'll introduce a 1x1 BufferedImage and a Graphics object in my object to measure metrics of a font. – Dmytro Dec 19 '17 at 04:59
68

It doesn't always need to be toolkit-dependent or one doesn't always need use the FontMetrics approach since it requires one to first obtain a graphics object which is absent in a web container or in a headless enviroment.

I have tested this in a web servlet and it does calculate the text width.

import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;

...

String text = "Hello World";
AffineTransform affinetransform = new AffineTransform();     
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);     
Font font = new Font("Tahoma", Font.PLAIN, 12);
int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
int textheight = (int)(font.getStringBounds(text, frc).getHeight());

Add the necessary values to these dimensions to create any required margin.

Samuel Edwin Ward
  • 6,526
  • 3
  • 34
  • 62
Olofu Mark
  • 1,040
  • 9
  • 6
  • 1
    I don't think creating an affineTransform and a fontRenderContext this way will result in a good behaviour. I guess font.getTransfrom() would be more logical. – Adrien Gorrell Mar 07 '13 at 15:13
  • 2
    Your example is still using AWT functionality. With SWT Font, for example, it is not going to work, this is why " it's always going to be toolkit-dependent" – serg.nechaev Sep 18 '13 at 03:57
  • 1
    @Olofu Mark - getStringBounds only gives the logical bounds. To make it better it should use getBounds() and LineMetrics object to retrieve the actual height including, ascent+descent. – Jones Oct 14 '13 at 07:57
  • Be warned: running this in a docker container using Sun JDK can cause a host of issues including needing to install X11 libraries, fontconfig, fonts. even after all that it still wont work because it tries to connect to a nonexistant display – Asad-ullah Khan Feb 23 '22 at 21:24
8

Use the getWidth method in the following class:

import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;

class StringMetrics {

  Font font;
  FontRenderContext context;

  public StringMetrics(Graphics2D g2) {

    font = g2.getFont();
    context = g2.getFontRenderContext();
  }

  Rectangle2D getBounds(String message) {

    return font.getStringBounds(message, context);
  }

  double getWidth(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getWidth();
  }

  double getHeight(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getHeight();
  }

}
Ed Poor
  • 1,849
  • 20
  • 13
4

And now for something completely different. The following assumes arial font, and makes a wild guess based on a linear interpolation of character vs width.

// Returns the size in PICA of the string, given space is 200 and 'W' is 1000.
// see https://p2p.wrox.com/access/32197-calculate-character-widths.html

static int picaSize(String s)
{
    // the following characters are sorted by width in Arial font
    String lookup = " .:,;'^`!|jl/\\i-()JfIt[]?{}sr*a\"ce_gFzLxkP+0123456789<=>~qvy$SbduEphonTBCXY#VRKZN%GUAHD@OQ&wmMW";
    int result = 0;
    for (int i = 0; i < s.length(); ++i)
    {
        int c = lookup.indexOf(s.charAt(i));
        result += (c < 0 ? 60 : c) * 7 + 200;
    }
    return result;
}

Interesting, but perhaps not very practical.

John Henckel
  • 10,274
  • 3
  • 79
  • 79
1

I personally was searching for something to let me compute the multiline string area, so I could determine if given area is big enough to print the string - with preserving specific font.

private static Hashtable hash = new Hashtable();
private Font font;
private LineBreakMeasurer lineBreakMeasurer;
private int start, end;

public PixelLengthCheck(Font font) {
    this.font = font;
}

public boolean tryIfStringFits(String textToMeasure, Dimension areaToFit) {
    AttributedString attributedString = new AttributedString(textToMeasure, hash);
    attributedString.addAttribute(TextAttribute.FONT, font);
    AttributedCharacterIterator attributedCharacterIterator =
            attributedString.getIterator();
    start = attributedCharacterIterator.getBeginIndex();
    end = attributedCharacterIterator.getEndIndex();

    lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator,
            new FontRenderContext(null, false, false));

    float width = (float) areaToFit.width;
    float height = 0;
    lineBreakMeasurer.setPosition(start);

    while (lineBreakMeasurer.getPosition() < end) {
        TextLayout textLayout = lineBreakMeasurer.nextLayout(width);
        height += textLayout.getAscent();
        height += textLayout.getDescent() + textLayout.getLeading();
    }

    boolean res = height <= areaToFit.getHeight();

    return res;
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
wmioduszewski
  • 146
  • 2
  • 10