测量缩放画布上的文本

发布于 2024-12-09 04:29:24 字数 1556 浏览 0 评论 0原文

我一直在努力处理文本测量和缩放画布。

当画布未缩放时,getTextBounds 和measureText 可提供准确的结果。但是,当画布缩放时,这两种方法都无法提供与打印文本的实际大小相匹配的结果。

为了进行测试,我使用以下 onDraw 方法创建了 View 的子类:

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);

对于缩放 = 0.5,我得到以下输出: canvasscaling 0.5

对于缩放 = 0.51,显示以下结果: canvasscaling 0.51

黄色实线边框标记从 getTextBounds 传递的矩形,青色虚线矩形使用从measureText传递的宽度渲染。

如您所见,缩放 = 0.5 的文本小于测量的尺寸,而缩放 = 0.51 的绘制文本远大于测量的尺寸。

任何帮助表示赞赏!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

余生共白头 2024-12-16 04:29:24

好吧,刚刚知道如何规避这个问题。

问题是 Paint 不知道 Canvas 缩放。因此,measureText 和 getTextBounds 提供未缩放的结果。但由于字体大小不会线性缩放(但是,绘制的矩形会线性缩放),因此您必须手动弥补该影响。

所以解决方案是:

// 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;

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;
两个我 2024-12-16 04:29:24

使用 Mono for Android 我必须使用显示指标,如下所示:

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);
}

其中 f.DrawingFontAndrodid.Text.TextPaint GetDisplay 是:

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

和相同的方法在Java中是:

private Display getDisplay() {
    return ((WindowManager) getContext().getSystemService(
            Context.WINDOW_SERVICE)).getDefaultDisplay();
}

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文