如何计算数字的正确宽度(以像素为单位)? [英] How to compute the correct width of a digit in pixels?

查看:89
本文介绍了如何计算数字的正确宽度(以像素为单位)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义控件,将来可能会为用户提供可自定义的Font(已经实现了缩放).我必须在以10为底的数字的两位数字下方填充一个矩形.我为零,一个或两个数字使用不同的颜色.

使用字体{Name = Microsoft Sans Serif Size = 16 }和以下 Graphics.MeasureString 方法调用:

  g.MeasureString("00",Font);g.MeasureString("0",Font); 

我得到:

  • "00"的大小为{Width = 31.5486088 高度= 26.8124962 }
  • "0"的大小为{Width = 19.3298588 Height = 26.8124962 }

"0"的宽度比"00"的宽度大一半.

我知道方法

更新:在UserControl的OnPaint方法中,我有以下代码:

  Graphics g = e.Graphics;int []索引= {0,1};CharacterRange [] charRanges =新的CharacterRange [indices.Length];for(int chx = 0; chx  

字体为 {Name ="Microsoft Sans Serif" Size = 25} .运行程序时,这是可见的:

我想使数字以蓝色矩形为中心.矩形在UserControl中必须尽可能大,但还要留出一定空间以保留UserControl高度的百分之一.字体应适应矩形.

解决方案

为使此功能按预期进行,需要进行一些小的调整:

    呈现文本时,
  1. TextRenderingHint.ClearTypeGridFit 会提供更好的结果.
    它更精确,并且与 Graphics.DrawString 的网格拟合特性配合使用.
    有关此问题的更多信息,请参见下面的答案中的注释.
  2. StringFormat 在水平和垂直方向上的对齐方式.
  3. 一种经过修改的方法,可以绘制任意长度的字符串.
    如果字符串大于容器,则将使用当前设置将其包装.
  4. Irrelevant:笔刷 Pen 在Paint事件外部声明,以便在需要时可以重新定义.

这里的 MeasureCharacterRanges 的不同实现:

Font 16em:

字体9em:

 钢笔=新钢笔(Color.LightGreen,1);画笔=新的SolidBrush(Color.White);字符串sourceDigits ="010011001";私人无效panel1_Paint(对象发送者,PaintEventArgs e){e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;CharacterRange [] charRanges =新的CharacterRange [sourceDigits.Length];for(int chx = 0; chx< sourceDigits.Length; ++ chx){charRanges [chx] = new CharacterRange(chx,1);}使用(StringFormat sf = new StringFormat()){sf.Alignment = StringAlignment.Center;sf.LineAlignment = StringAlignment.Center;sf.SetMeasurableCharacterRanges(charRanges);Region []地区= e.Graphics.MeasureCharacterRanges(sourceDigits,Font,e.ClipRectangle,sf);for(int i = 0; i< region.Length; i ++){RectangleF rect = region [i] .GetBounds(e.Graphics);e.Graphics.DrawRectangle(pen,rect.X,rect.Y,rect.Width,rect.Height);e.Graphics.DrawString(char.ToString(sourceDigits [i]),Font,brush,rect,sf);}}} 

I have a custom control that may have user customizable Font in future (the zoom is already implemented). I must fill a rectangle under two digits that form a base-10 number. I have different colors for zero, one or both of the digits.

With the font {Name = Microsoft Sans Serif Size=16} and the following Graphics.MeasureString method calls:

g.MeasureString("00", Font);
g.MeasureString("0", Font);

I get:

  • The size of "00" is {Width = 31.5486088 Height = 26.8124962}
  • The size of "0" is {Width = 19.3298588 Height = 26.8124962}

The width of "0" is a lot bigger that half of the width of "00".

I know of the methods Graphics.MeasureString, it has many overloads, and I also know of the StringFormat class. How can I correctly compute the width of the '0' char?

Because the font will be user-customizable, I do not want to solve the problem using a monospace font.

If I use the following calls:

g.MeasureString("00", Font, 999, StringFormat.GenericTypographic);
g.MeasureString("0", Font, 999, StringFormat.GenericTypographic);

The width of "0" seems to be half of the width of "00", but the digits overlap when drawn with a smaller font size:

Update: In the OnPaint method of an UserControl I have this code:

Graphics g = e.Graphics;

int[] indices = { 0, 1 };
CharacterRange[] charRanges = new CharacterRange[indices.Length];
for (int chx = 0; chx < indices.Length; ++chx)
{
    charRanges[chx] = new CharacterRange(indices[chx], 1);
}

StringFormat sf = new StringFormat(StringFormat.GenericDefault);
sf.SetMeasurableCharacterRanges(charRanges);

Region[] regions = e.Graphics.MeasureCharacterRanges("01", Font, e.ClipRectangle, sf);

RectangleF[] r = new RectangleF[regions.Length];
int i = 0;
foreach (Region rr in regions)
{
    r[i] = rr.GetBounds(g);
    g.DrawRectangle(Pens.Blue, r[i].X, r[i].Y, r[i].Width, r[i].Height);
    ++i;
}

g.DrawString("0", Font, Brushes.Black, r[0], sf);
g.DrawString("1", Font, Brushes.Black, r[1], sf);

The font is {Name = "Microsoft Sans Serif" Size=25}. When running the program, this is what is visible:

I want to make the digits centered in the blue rectangles. The rectangles must be as big as possible in the UserControl but also leaving space for a percent of the Height of the UserControl. The Font should adapt to the rectangles.

解决方案

Small adjustments are required to make this work as intended:

  1. TextRenderingHint.ClearTypeGridFit gives a better result when rendering the Text.
    It's more precise and works well with the grid-fitting nature of Graphics.DrawString.
    See the notes you can find in the answer linked below for more informations on this matter.
  2. StringFormat alignment in both horizontal and vertical dimensions.
  3. A modified method that allows to draw strings of any length.
    If the string is larger than the container, it will be wrapped, with the current settings.
  4. Irrelevant: Brush and Pen are declared outside the Paint event, to allow their re-definition when required.

Different implementations of MeasureCharacterRanges here:
How to highlight wrapped text in a control

About Graphics.DrawString and TextRenderingHint.ClearTypeGridFit:
Drawing a Long String on to a Bitmap results in Drawing Issues


Font 48em:

Font 16em:

Font 9em:

Pen pen = new Pen(Color.LightGreen, 1);
Brush brush = new SolidBrush(Color.White);
string sourceDigits = "010011001";

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

    CharacterRange[] charRanges = new CharacterRange[sourceDigits.Length];
    for (int chx = 0; chx < sourceDigits.Length; ++chx) {
        charRanges[chx] = new CharacterRange(chx, 1);
    }

    using (StringFormat sf = new StringFormat())
    {
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Center;
        sf.SetMeasurableCharacterRanges(charRanges);

        Region[] regions = e.Graphics.MeasureCharacterRanges(sourceDigits, Font, e.ClipRectangle, sf);
        for (int i = 0; i < regions.Length; i++) {
            RectangleF rect = regions[i].GetBounds(e.Graphics);
            e.Graphics.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
            e.Graphics.DrawString(char.ToString(sourceDigits[i]), Font, brush, rect, sf);
        }
    }
}

这篇关于如何计算数字的正确宽度(以像素为单位)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆