如何使用 DrawString 正确左对齐文本?
How to properly left-align text with DrawString?
我正在将文本绘制到屏幕外位图中。不幸的是,文本没有正确左对齐(见下图)。文本应该触及左边距(蓝线)但偏离了几个像素。距离随着文字大小的增加而增加。
如何消除这个距离?
我正在使用 .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);
}
}
}
}
基于@ChrisW 提供的link,我创建了一个改进版本(见下文)。它使用 MeasureCharacterRanges
来测量 DrawString
添加的填充。结果看起来黄油多了:
如您所见,它并不完美。蓝线和字母"C"之间还有一些白色space,因为测量的矩形包括所谓的左方位,即左侧两个字符之间添加的白色space。
所以我仍在寻找更好的解决方案。除了测量的填充外,还可以计算方位并减去方位的一半。希望它适用于 .NET Standard 2.0...
顺便说一句:我测量了几种字体、字体样式、字体大小和分辨率。看起来填充是固定的。它可以计算为:
padding = 0.002312554 × font_size × resolution
(填充像素,字体大小 pt,分辨率 pixels/inch)
举个例子:对于 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);
}
}
}
}
据说微软在 GDI+ 中添加了填充,以便更容易实现控件。旧的 GDI 没有这个问题。
当 Microsoft 意识到这是一个错误时,他们添加了绕过 GDI+ 并使用更好的 GDI 实现的 TextRenderer class。
填充应该在左侧为 1/6 em,在右侧为 1/4 em。
您有两个选择:
使用TextRenderer.DrawText。但是,它是 Windows 表格 的一部分。所以它在 .NET Standard 和 .NET Core 中都不可用。
使用Graphics.DrawString with the magic option StringFormat.GenericTypographic。它神奇地删除了填充。
另见:
我正在将文本绘制到屏幕外位图中。不幸的是,文本没有正确左对齐(见下图)。文本应该触及左边距(蓝线)但偏离了几个像素。距离随着文字大小的增加而增加。
如何消除这个距离?
我正在使用 .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);
}
}
}
}
基于@ChrisW 提供的link,我创建了一个改进版本(见下文)。它使用 MeasureCharacterRanges
来测量 DrawString
添加的填充。结果看起来黄油多了:
如您所见,它并不完美。蓝线和字母"C"之间还有一些白色space,因为测量的矩形包括所谓的左方位,即左侧两个字符之间添加的白色space。
所以我仍在寻找更好的解决方案。除了测量的填充外,还可以计算方位并减去方位的一半。希望它适用于 .NET Standard 2.0...
顺便说一句:我测量了几种字体、字体样式、字体大小和分辨率。看起来填充是固定的。它可以计算为:
padding = 0.002312554 × font_size × resolution
(填充像素,字体大小 pt,分辨率 pixels/inch)
举个例子:对于 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);
}
}
}
}
据说微软在 GDI+ 中添加了填充,以便更容易实现控件。旧的 GDI 没有这个问题。
当 Microsoft 意识到这是一个错误时,他们添加了绕过 GDI+ 并使用更好的 GDI 实现的 TextRenderer class。
填充应该在左侧为 1/6 em,在右侧为 1/4 em。
您有两个选择:
使用TextRenderer.DrawText。但是,它是 Windows 表格 的一部分。所以它在 .NET Standard 和 .NET Core 中都不可用。
使用Graphics.DrawString with the magic option StringFormat.GenericTypographic。它神奇地删除了填充。
另见: