如何使用DrawString正确地使文本左对齐?

时间:2019-01-03 15:11:55

标签: c# .net gdi+

我正在将文本绘制到屏幕外的位图。不幸的是,文本没有正确地左对齐(请参见下图)。文本应触摸左边缘(蓝线),但偏离几个像素。距离随着文字大小而增加。

Left alignment

如何摆脱这个距离?

我正在使用.NET Framework 4.6.1。但这似乎是我不了解的一般性GDI +问题。

用于生成示例的代码:

using System.Drawing;
using System.Drawing.Imaging;

namespace LeftAlignment
{
    class Program
    {
        static void Main(string[] args)
        {
            const int LeftMargin = 10;

            // create off-screen bitmap
            using (Bitmap bitmap = new Bitmap(300, 100))
            {
                // create graphics context
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    // clear bitmap
                    graphics.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);

                    // draw border and left margin
                    graphics.DrawRectangle(Pens.Gray, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1));
                    graphics.DrawLine(Pens.Blue, LeftMargin, 8, LeftMargin, 92);

                    // draw string at 24 pt
                    Font font = new Font("Arial", 24);
                    graphics.DrawString("Cool water", font, Brushes.Black, LeftMargin, 8);

                    // draw string at 36 pt
                    font = new Font("Arial", 36);
                    graphics.DrawString("Cool water", font, Brushes.Black, LeftMargin, 44);
                }

                // save result as PNG
                bitmap.Save("alignment.png", ImageFormat.Png);
            }
        }
    }
}

2 个答案:

答案 0 :(得分:2)

传说中,Microsoft在GDI +中添加了填充,以使控件的实现更加容易。旧的GDI并没有这个问题。

当Microsoft意识到这是一个错误时,他们添加了TextRenderer class来绕过GDI +,并使用更好的GDI实现。

假设填充物在左侧为1/6 em,在右侧为1/4 em。

您有两个选择:

  1. 使用TextRenderer.DrawText。但是,它是 Windows窗体的一部分。因此,.NET Standard和.NET Core均不可用。

  2. 使用Graphics.DrawString和魔术选项StringFormat.GenericTypographic。它神奇地删除了填充。

另请参阅:

答案 1 :(得分:0)

基于@ChrisW提供的链接,我创建了一个改进的版本(请参见下文)。它使用MeasureCharacterRanges来度量DrawString添加的填充。结果看起来黄油很多:

Improved left alignment

如您所见,它并不完美。蓝线和字母“ C”之间仍然存在一些空白,因为所测量的矩形包括所谓的左侧轴承,即两个字符之间添加的空白左侧。 / p>

所以我仍在寻找更好的解决方案。可能可以计算方位角,并减去测得的填充量减去一半的方位角。希望它可以在.NET Standard 2.0中实现...

顺便说一句:我已经测量了几种字体,字体样式,字体大小和分辨率。看起来填充是固定的。可以计算为:

  

填充= 0.002312554×字体大小×分辨率

(以像素为单位的填充,以pt为单位的字体大小,以像素/英寸为单位的分辨率)

举个例子:对于24pt字体和96dpi的图形分辨率,填充为5.33像素。

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;

namespace LeftAlignment
{
    class Program
    {
        static void ImprovedDrawString(Graphics graphics, string text, Font font, float x, float y)
        {
            // measure left padding
            StringFormat sf = new StringFormat(StringFormatFlags.NoClip);
            sf.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, 1) });
            Region[] r = graphics.MeasureCharacterRanges(text, font, new RectangleF(0, 0, 1000, 100), sf);
            float leftPadding = r[0].GetBounds(graphics).Left;

            // draw string
            sf = new StringFormat(StringFormatFlags.NoClip);
            graphics.DrawString(text, font, Brushes.Black, x - leftPadding, y, sf);
        }

        static void Main(string[] args)
        {
            const int LeftMargin = 10;
            const string Text = "Cool water";

            // create off-screen bitmap
            using (Bitmap bitmap = new Bitmap(300, 100))
            {
                // create graphics context
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    // enable high-quality output
                    graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

                    // clear bitmap
                    graphics.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);

                    // draw border and left margin
                    graphics.DrawRectangle(Pens.Gray, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1));
                    graphics.DrawLine(Pens.Blue, LeftMargin, 8, LeftMargin, 92);

                    // draw string at 24 pt
                    Font font = new Font("Arial", 24);
                    ImprovedDrawString(graphics, Text, font, LeftMargin, 8);

                    // draw string at 36 pt
                    font = new Font("Arial", 36);
                    ImprovedDrawString(graphics, Text, font, LeftMargin, 44);
                }

                // save result as PNG
                bitmap.Save("alignment.png", ImageFormat.Png);
            }
        }
    }
}