Android: Measure Text Height on a Canvas

12,194

Solution 1

I think it is because the "p" in "compute" extends below the baseline whereas "Hello World" only contains letters that are above the baseline.

Since the line distance should not depend on what specific letters your text happens to consist of you are probably looking for Paint.FontMetrics which can be obtained via Paint.getFontMetrics(). Compute descent - ascent + leading to get the recommended baseline distance (because ascent has a negative value).

Solution 2

There is a small error in the accepted answer. If you want the text height, you should use

Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;

And if you want the line height, you should use

float lineHeight = fm.bottom - fm.top + fm.leading;

Leading is optional interline spacing, so if you need to get the line hight you can include it. But if you just want the text height, then you can leave it off.

Note

  • I've never actually seen leading be anything else than 0, and as far as I can tell it even seems to be ignored in the TextView source code (and its associated Layout, StaticLayout, etc.). Please correct me if I'm wrong. So it is probably safe it leave it out of line hight calulations, but I'm not completely sure about that.

See also

Share:
12,194
AgentKnopf
Author by

AgentKnopf

Updated on July 28, 2022

Comments

  • AgentKnopf
    AgentKnopf almost 2 years

    I am currently working on rendering a Bitmap, that I then want to send to a mobile printer. However, I am struggling with measuring the height of my text, so I can advance the y position appropriately.

    My basic bitmap/canvas/paint configuration is this (Font Size is 16 and the dimensions of the bitmap are 200x400 (width x height):

    public MyRenderer() {
        // Initialize bitmap
        bitmap = Bitmap.createBitmap(200, 400, Bitmap.Config.ARGB_8888);
    
        // Initialize canvas
        canvas = new Canvas(bitmap);
    
        // Initialize brush (Paint instance)
        brush = new Paint();
        brush.setTextSize(16);
        brush.setTypeface(Typeface.SANS_SERIF);
        brush.setColor(Color.BLACK);
        brush.setStyle(Paint.Style.FILL);
        brush.setAntiAlias(true);
        brush.setTextAlign(Align.LEFT);
    }
    

    So far so good, now what I want to do is: If I use the Paint's method drawText I need to supply the x and y coordinates. As for x that's zero (assuming left aligned text) but as for y, I'd have to calculate the height of each text I print and add it up, so I can keep track of my current y position.

    And this is where it gets odd: I am using the following method to determine the height of a text (using the Paint objected that I initialized previously - it's called "brush"):

    public int measureHeight(String text) {
        Rect result = new Rect();
        // Measure the text rectangle to get the height
        brush.getTextBounds(text, 0, text.length(), result);
        return result.height();
    }
    

    The above method returns the following values for the following texts:

    1. "Hello World" returns a height of 12
    2. "A camera instance can be used to compute 3D transformations and generate a matrix." returns a height of 16
    3. "Introducing Android Design: The place to learn about principles, building blocks, and patterns for creating world-class Android user interfaces. Whether you're a UI professional or a developer playing that role, these docs show you how to make good design decisions, big and small." returns a height of 16

    It makes sense to me, that number 2 and 3 return a greater height than number 1 but if one line has a height of 12 (as number one does) - it makes no sense, that multiple lines have a height of 16 ?

    Am I missing something here? There is a convenience method for measuring the width of a text (using an instance of paint and call measureText("myText") which works perfectly, however I am quite at a loss, when it comes to the height, as the above given results don't make any sense to me.

    EDIT

    I am aware, that getTextBounds probably does no auto-wrapping of multi-lined text, and that's ok, I already wrote a method for splitting text, but even if it just measures one line, the above given length values still seem unlikely.

  • AgentKnopf
    AgentKnopf about 12 years
    I calculated the baseline distance (as per your calculation) for those three above given texts, it always returns 19. Would that mean, if I take 19 has the height, there would still be enough space for the next line to not overlap with the current line? Thank you very much in advance!
  • devconsole
    devconsole about 12 years
    It is a characteristic of the font, you don't have to calculate it repeatedly for different texts. You want the distance to always be the same, regardless of what characters your text consists of. And yes, the lines won't overlap.
  • AgentKnopf
    AgentKnopf about 12 years
    Thank you very much - thats what I did and rendering my bitmap works just fine now :) . I initialize the "line height" once in the constructor of MyRenderer.
  • devconsole
    devconsole about 12 years
    Even simpler: Paint.getFontSpacing()