measuring text on scaled canvas
Asked Answered
W

2

7

I've been struggling with text measuring and scaled canvases.

When the canvas is unscaled, getTextBounds and measureText deliver accurate results. However, when the canvas is scaled both methods do not deliver results that match the actual size of a printed text.

For testing I've created a subclass of View with the following onDraw method:

final float scaling = 0.51f;
final int fontSize = 50;

canvas.scale(scaling, scaling);
font = Typeface.create("Arial", Typeface.NORMAL);

Paint paint = new Paint();
paint.setColor(0xff4444ff);
paint.setTypeface(font);
paint.setTextSize(fontSize);
paint.setAntiAlias(true);

int x = 10;
int y = 100;
final String text = "Lorem ipsum dolor sit amet, consectetur adipisici elit...";
canvas.drawText(text, x, y, paint);

// draw border using getTextBounds

paint.setColor(0xffff0000);
paint.setStyle(Paint.Style.STROKE);
paint.setTypeface(font);
paint.setTextSize(fontSize);
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
bounds.offset(x, y);
paint.setColor(0x80ffff00);
canvas.drawRect(bounds, paint);

// draw border using measureText

float w = paint.measureText(text);
bounds.left = x;
bounds.right = (int) Math.ceil(bounds.left + w);
bounds.top -= 10;
bounds.bottom += 10;
paint.setColor(0x8000ffff);
paint.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 0));
canvas.drawRect(bounds, paint);

for scaling = 0.5 I get the following output: canvas scaling 0.5

for scaling = 0.51 the following result is shown: canvas scaling 0.51

The yellow solid border marks the rect delivered from getTextBounds, the dashed cyan rect is rendered using the width delivered from measureText.

As you can see, the text with scaling = 0.5 is smaller than the measured dimensions and with scaling=0.51 the drawn text is way bigger than the measured dimension.

Any help is appreciated!

Wellgrounded answered 10/10, 2011 at 12:25 Comment(0)
W
4

Ok, just found out how to circumvent the issue.

The problem is that the Paint does not know about the Canvas scaling. Therefore measureText and getTextBounds deliver the unscaled result. But since the font size does not scale linearly (however, the drawn rect does ), you have to make up for that effect manually.

So the solution would be:

// paint the text as usual
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize);
canvas.drawText(text, x, y, paint);


// measure the text using scaled font size and correct the scaled value afterwards
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize * scaling);
float w = paint.measureText(text) / scaling;
Wellgrounded answered 13/10, 2011 at 7:48 Comment(0)
P
0

Using Mono for Android I had to use display metrics as shown here:

public override System.Drawing.SizeF MeasureString(MyFont f, string text)
{
  Rect r = new Rect();

  f.DrawingFont.GetTextBounds(text, 0, text.Length, r);

  //Manual scaling using DisplayMetrics due to Android
  //issues for compatibility with older versions
  Android.Util.DisplayMetrics metrics = new Android.Util.DisplayMetrics();
  GetDisplay.GetMetrics(metrics);

  return new System.Drawing.SizeF(r.Width(), r.Height() * metrics.Density);
}

Where f.DrawingFont is an Androdid.Text.TextPaint GetDisplay is:

private Display GetDisplay()
{
  return this.GetSystemService(Android.Content.Context.WindowService).JavaCast<Android.Views.IWindowManager>().DefaultDisplay;
}

And the same method in Java is:

private Display getDisplay() {
    return ((WindowManager) getContext().getSystemService(
            Context.WINDOW_SERVICE)).getDefaultDisplay();
}
Paranoiac answered 10/10, 2011 at 14:1 Comment(3)
you're correction is applied always and does not compensate for a scaling value in the Canvas, no? I actually don't think that the calculation of the bounding rect is incorrect, but that the font rendering code does not pick the proper font size according to the canvas scaling...Wellgrounded
Yes, it's calculated always that text dimensions are needed without scaling the canvas.Gide
Forgot to mention this is used mostly used for positioning text in the canvas, knowing exact dimensions and position is necessary.Gide

© 2022 - 2024 — McMap. All rights reserved.