GDI +在基线上绘制不同大小的文本会产生1px的问题

时间:2012-02-01 17:08:39

标签: c# .net gdi+

我需要打印数字,通过增加字体大小和重量来强调中间的某些数字。在下面的示例中,强调了456

enter image description here

使用的字体和两种尺寸是用户可配置的。

当前代码使用对Graphics.DrawString(...)的三次调用来执行此操作。

我遇到的问题是,对于大多数字体,我看到的是1像素的问题(相对于灰线,456比其他数字高出一个额外的像素):

enter image description here

我在帖子的底部为各种字体添加了一些调试转储(Bob Powell公式)。其他技术产生了类似的结果。

为了在共同基线上打印文本,需要计算特定Font的基线偏移量。我尝试过使用三种技术:

首先,MSDN的代码:http://msdn.microsoft.com/en-us/library/xwf9s90b(v=vs.80).aspx

ascent = fontFamily.GetCellAscent(FontStyle.Regular);
ascentPixel = font.Size * ascent / fontFamily.GetEmHeight(FontStyle.Regular)

其次,代码来自:Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?

最后,鲍勃鲍威尔的帖子代码:http://www.bobpowell.net/formattingtext.htm

这是我的绘制方法:

private void DrawOnBaseline(Graphics g, string text, FontWithBaseline fwb, Brush brush, float x, float y) {
  g.DrawString(text, fwb.Font, brush, x, y - fwb.Baseline, StringFormat.GenericTypographic);
}

FontWithBaseline只是将Font与其各自的基线计算相关联:

public class FontWithBaseline {
  private Font m_font;
  private float m_baseline;

  public FontWithBaseline(Font font) {
    m_font = font;
    m_baseline = CalculateBaseline(font);
  }

  public Font Font { get { return m_font; } }
  public float Baseline { get { return m_baseline; } }

  private static float CalculateBaseline(Font font) {
    ... // I've tried the three formulae here.
  }
}

我还没有尝试Graphics.TestRenderingHint。那是神奇的酱汁吗? 我错过了什么?是否有我可以使用的替代API,我在其中调用绘制耗材的基线的Y坐标?

enter image description here


更新1

我用@LarsTech插入我的代码。他做了一个微妙的不同;他正在添加0.5f。但是,即使这种变体也无法解决问题。这是代码:

protected override void OnPaint(PaintEventArgs e) {
  base.OnPaint(e);
  TryLarsTechnique(e);
}

private void TryLarsTechnique(PaintEventArgs e) {
  base.OnPaint(e);
  Graphics g = e.Graphics;
  GraphicsContainer c = g.BeginContainer();
  g.Clear(Color.White);
  g.SmoothingMode = SmoothingMode.AntiAlias;
  g.TextRenderingHint = TextRenderingHint.AntiAlias;
  Font small = new Font("Arial", 13, FontStyle.Regular, GraphicsUnit.Pixel);
  Font large = new Font("Arial", 17, FontStyle.Bold, GraphicsUnit.Pixel);

  int x = 100;
  int y = 100;
  x += DrawLars(g, "12.3", small, x, y);
  x += DrawLars(g, "456", large, x, y);
  x += DrawLars(g, "8", small, x, y);
  g.EndContainer(c);
}

// returns width of text
private int DrawLars(Graphics g, string text, Font font, int x, int y) {
  float offset = font.SizeInPoints /
                 font.FontFamily.GetEmHeight(font.Style) *
                 font.FontFamily.GetCellAscent(font.Style);
  float pixels = g.DpiY / 72f * offset;
  int numTop = y - (int)(pixels + 0.5f);      
  TextRenderer.DrawText(g, text, font, new Point(x, numTop), Color.Black, Color.Empty, TextFormatFlags.NoPadding);
  return TextRenderer.MeasureText(g, text, font, Size.Empty, TextFormatFlags.NoPadding).Width;
}

我想知道使用GraphicsUnit.Pixel指定字体大小是否是罪魁祸首。也许有办法找到任何特定字体的首选尺寸?


更新2

我尝试用Points指定字体大小而不是像素,这也不能完全解决问题。请注意,在我的情况下,仅使用整点大小不是一个选项。要查看这是否可行,我在Windows Wordpad上尝试了这个。尺寸使用96 dpi(根据定义为每英寸72点),17px,13px转换为12.75和9.75。这是比较的输出:

enter image description here

请注意较小字体在像素级别的高度是多少。因此,Wordpad以某种方式设法使这个更正,而不会将字体大小舍入到方便的值。

3 个答案:

答案 0 :(得分:5)

您没有显示足够的代码来重现问题,所以这里有一个使用您提供的Bob Powell示例的工作示例。

仅限演示代码:

private void panel1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.Clear(Color.White);
  e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

  string[] numbers = new string[] { "1", "2", ".", "3", "4", "5", "6", "8" };

  int x = 10;
  int y = 30;

  foreach (string num in numbers) {
    Font testFont;
    if (num == "4" || num == "5" || num == "6")
      testFont = new Font("Arial", 16, FontStyle.Bold);
    else
      testFont = new Font("Arial", 11, FontStyle.Regular);

    float offset = testFont.SizeInPoints / 
                   testFont.FontFamily.GetEmHeight(testFont.Style) * 
                   testFont.FontFamily.GetCellAscent(testFont.Style);
    float pixels = e.Graphics.DpiY / 72f * offset;

    int numTop = y - (int)(pixels + 0.5f);

    TextRenderer.DrawText(e.Graphics, num, testFont, new Point(x, numTop), 
                          Color.Black, Color.Empty, TextFormatFlags.NoPadding);

    x += TextRenderer.MeasureText(e.Graphics, num, testFont, 
                                  Size.Empty, TextFormatFlags.NoPadding).Width;
  }

  e.Graphics.DrawLine(Pens.Red, new Point(5, y + 1), new Point(x + 5, y + 1));
}

这会产生:

enter image description here

答案 1 :(得分:2)

可能问题是您在pixels中指定字体大小,而偏移量在points中计算并转换回pixels。这可能会引入各种各样的不精确。

尝试在points中指定字体大小,看看它是否有效。

答案 2 :(得分:1)

Graphics.TextRenderingHint设置为使用网格拟合时,缩放的TrueType字体的实际度量(以像素为单位)由GDI +通过搜索字体VDMX table确定,而不是简单地缩放和舍入其设计指标。这就是我在反汇编中逐步浏览Graphics.DrawString所得到的。