使用DrawString居中单个字符

6
我尝试了所有建议的方法来居中文本,但在居中单个字符时似乎无法得到想要的结果。
我有一个矩形。在这个矩形里,我用DrawEllipse画了一个圆。现在我想使用同一个矩形和DrawString来画一个单独的字符,以使其完全居中。
这是我的基本代码:
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;

using (Graphics g = Graphics.FromImage(xImage))
{
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.TextRenderingHint = TextRenderingHint.AntiAlias;
    g.CompositingQuality = CompositingQuality.HighQuality;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;

    g.FillEllipse(fillBrush, imageRect.X, imageRect.Y, imageRect.Width - 1, imageRect.Height - 1);

    g.DrawString(Text, font, Brushes.White, imageRect, stringFormat);
}

文本水平居中了...但垂直居中不正确。使用一个对称的字符,如大写字母 "I",我发现字符的上部总是比矩形边缘更靠近,而字符的底部则不是。距离可能增加了至少 50%。
我认为它是在测量像小写字母 "j" 这样下垂的字符所需的足够空间。然而,由于我正在尝试创建一个只有一个字母的图标,我需要更精确的居中。

你有比较过使用DrawString方法和TextRenderer.DrawText方法(使用TextFormatFlags的HoriztonalCenter和VerticalCenter)的结果吗? - Jacob G
无需复制。请注意,大写字母I没有像字符g一样的下降部分。 - Hans Passant
我尝试了TextRenderer.DrawText并获得了类似的结果。 - Trevor Elliott
Hans:我的观点是,当我绘制没有下降线的字符时,它们以一种提供了不存在下降线的空白空间的垂直居中方式被居中。因此,该字符并不真正居中。 - Trevor Elliott
我假设GDI没有办法做到这一点,所以我要做的是测量文本,创建一个大小为测量值的位图,在位图上绘制文本,锁定位,获取实际包含数据(非透明像素)的子矩形的边界区域。然后在第一个矩形上居中显示该矩形。我认为应该可以解决问题,但性能会很差。 - Trevor Elliott
3个回答

10

使用GraphicsPath来完成大小计算。

public static void DrawCenteredText(Graphics canvas, Font font, float size, Rectangle bounds, string text)
{
    var path = new GraphicsPath();
    path.AddString(text, font.FontFamily, (int)font.Style, size, new Point(0, 0), StringFormat.GenericTypographic);

    // Determine physical size of the character when rendered
    var area = Rectangle.Round(path.GetBounds());

    // Slide it to be centered in the specified bounds
    var offset = new Point(bounds.Left + (bounds.Width / 2 - area.Width / 2) - area.Left, bounds.Top + (bounds.Height / 2 - area.Height / 2) - area.Top);
    var translate = new Matrix();
    translate.Translate(offset.X, offset.Y);
    path.Transform(translate);

    // Now render it however desired
    canvas.SmoothingMode = SmoothingMode.AntiAlias;
    canvas.FillPath(SystemBrushes.ControlText, path);
}

那真是相当聪明的做法。我会试一下的。 - Trevor Elliott
@ccnyou说得对,这太完美了!我之前尝试使用MeasureString但是没有成功。今天我找到了这个答案。虽然问题很老,但是问题仍然存在。这个方法立刻解决了它! - Misiu
非常酷!+1 - GraphicsPath(连同Label一起)是如此被低估的。;-) - TaW
这个应该更受关注,我为此搜索了几个小时 :) - Daniel Sharp

1

如果你使用

StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);

你得到了

enter image description here

相反

enter image description here

希望这可以帮到你。

请注意您的图像中,“g”字母没有居中对齐。我不是在寻找排版居中,也不是绘制需要字符对齐的单词。我正在绘制一个单一字符,并希望它无论是哪个字符都能在圆形的正中心。想象一下平面设计师如何通过在Photoshop中居中来制作图标。我需要以编程方式实现这一点。 - Trevor Elliott
好的,我认输了,John Arlen给出了完美的答案! - punker76
应该是: StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic()); - Anthony Wieser

1

虽然John Arlen的回答非常完美,但我想发表自己的回答:

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
         // Set up string.
        string measureString = "HelloWorld";
        Font stringFont = new Font("Arial", 100, FontStyle.Regular, GraphicsUnit.Pixel);

        // Measure string.
        SizeF stringSize = new SizeF();
        stringSize = e.Graphics.MeasureString(measureString, stringFont);

        // Draw rectangle representing size of string.
        e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 10.0F, 10.0F, stringSize.Width, stringSize.Height);

        // Draw string to screen.
        e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(10, 10f + stringSize.Height / 12.0f));
    }

代码结果如下:

我的电脑中的结果

"HelloWorld"在红色框中垂直居中。

对于降部线的高度,大约等于通过MeasureString计算出的stringSize.Height的1/6。


| 顶部内边距 1/6

| 文字主体 3/6

| 字体下沉 1/6

| 底部内边距 1/6


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接