如何计算一个数字的正确宽度(以像素为单位)?

Posted

技术标签:

【中文标题】如何计算一个数字的正确宽度(以像素为单位)?【英文标题】:How to compute the correct width of a digit in pixels? 【发布时间】:2019-01-25 13:07:44 【问题描述】:

我有一个自定义控件,将来可能会有用户自定义字体(缩放已经实现)。我必须在两位数下填充一个矩形,形成一个以 10 为基数的数字。我对零位、一个或两个数字有不同的颜色。

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

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

我明白了:

“00”的大小是Width = 31.5486088 Height = 26.8124962 “0”的大小是Width = 19.3298588 Height = 26.8124962

“0”的宽度比“00”的一半大很多。

我知道Graphics.MeasureString 方法,它有很多重载,我也知道StringFormat 类。如何正确计算“0”字符的宽度?

因为字体是用户可自定义的,我不想使用等宽字体来解决问题。

如果我使用以下调用:

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

“0”的宽度似乎是“00”宽度的一半,但是用较小的字体大小绘制时数字重叠:

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

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

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

我想让数字以蓝色矩形为中心。矩形在 UserControl 中必须尽可能大,但也要​​为 UserControl 的高度留出一定百分比的空间。字体应该适应矩形。

【问题讨论】:

对不起,如果我理解不好,但我认为您应该填充您的矩形并在运行时使用自定义字体动态计算矩形的大小。 由于字距调整,字符的宽度实际上会根据它所在的字符串而变化。您可能需要升级到 MeasureCharacterRanges 您能展示一下您是如何绘制矩形和文本的吗?在不清楚什么是程序,这里。请注意,您不能测量单个字母;您还应该考虑内部填充。如前所述,MeasureCharacterRanges 可能是首选工具,但可能不是必需的。什么是必须适合的?文本还是矩形?您发布的小图并未说明预期结果是什么。 顺便说一句,看看这些测试,有和没有MeasureCharacterRanges:How to highlight wrapped text in a control using the graphics(见警告)。还有这里的注释:How can I draw multi-colored text using graphics class. @Jimi 我在问题中添加了更多细节。 【参考方案1】:

需要进行一些小调整才能按预期工作:

    TextRenderingHint.ClearTypeGridFit 在呈现文本时提供更好的结果。 它更精确,并且与 Graphics.DrawString 的网格拟合特性配合得很好。 有关此问题的更多信息,请参阅您可以在下面链接的答案中找到的注释。 StringFormat 在水平和垂直维度上对齐。 一种允许绘制任意长度字符串的修改方法。 如果字符串大于容器,它将使用当前设置进行包装。 不相关:BrushPen 在 Paint 事件之外声明,以便在需要时重新定义它们。

MeasureCharacterRanges 的不同实现在这里:How to highlight wrapped text in a control

关于Graphics.DrawStringTextRenderingHint.ClearTypeGridFit:Drawing a Long String on to a Bitmap results in Drawing Issues


字体 48em:

16em 字体:

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

【讨论】:

以上是关于如何计算一个数字的正确宽度(以像素为单位)?的主要内容,如果未能解决你的问题,请参考以下文章

使用像素大小计算直径

OpenCV图像数字化

svg路径到给定宽度和高度的预先计算比例

CSS 宽度 100% 或最大宽度(以像素为单位)

R确定图像宽度和高度(以像素为单位)

设置宽度(以像素为单位)是不是使用 dp 或 px?