How to compute the correct width of a digit in pixels?

时间秒杀一切 提交于 2019-12-24 19:27:21

问题


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.


回答1:


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


来源:https://stackoverflow.com/questions/54365948/how-to-compute-the-correct-width-of-a-digit-in-pixels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!