使用 GDI,沿公共基线对齐文本(以多种不同字体绘制)的最简单方法是什么?

发布于 2024-08-19 06:31:47 字数 1958 浏览 4 评论 0 原文

我的问题:

我目前正在开发一个自定义用户控件,该控件在一行上显示文本片段(每个文本片段可能具有不同的字体)。我想将所有这些文本位完全沿着公共基线对齐。例如:

  Hello,    I am    George.  
------------------------------   <- all text aligns to a common baseline
    ^         ^        ^
    |         |        |
 Courier    Arial    Times       <- font used for a particular bit of text
  20pt      40pt     30pt

因为我还没有找到任何 GDI+ 功能来直接执行此操作,所以我想出了自己的方法(概述如下)。但是:

我想知道是否真的没有更简单的方法来完成此任务?

我当前的方法:

1)收集将用于绘制文本的所有System.Drawing.Font的列表。

2) 对于每个 Font,使用以下代码查找基线的垂直位置(以像素为单位):

// variables used in code sample (already set)
Graphics G;
Font font;
...

// compute ratio in order to convert from font design units to pixels:
var designUnitsPerPixel = font.GetHeight(G) / 
                          font.FontFamily.GetLineSpacing(font.Style);

// get the cell ascent (baseline) position in design units:
var cellAscentInDesignUnits = font.FontFamily.GetCellAscent(font.Style);

// finally, convert the baseline position to pixels:
var baseLineInPixels = cellAscentInDesignUnits * designUnitsPerPixel;

3) 对于所有 Font 使用时,确定上面计算的最大baseLineInPixels值,并将该值存储到maxBaseLineInPixels

4) 按照以下方式绘制每一位文本:

// variables used in code sample (already set):
Graphics G;
Font font;
string text;
...

// find out how much space is needed for drawing the text
var measureF = G.MeasureString(text, font);

// determine location where text will be drawn:
var layoutRectF = new RectangleF(new PointF(0f, 0f), measureF);
layoutRectF.Y += maxBaseLineInPixels - baseLineInPixels;
// ^ the latter value 'baseLineInPixels' is specific to the font used

// draw text at specified location
G.DrawString(text, font, Brushed.Black, layoutRectF);

我是否遗漏了什么,或者真的没有更简单的方法吗?

My problem:

I'm currently working on a custom user control which displays pieces of text (each with a potentially different font) on one line. I'd like to align all those bits of text exactly along a common baseline. For example:

  Hello,    I am    George.  
------------------------------   <- all text aligns to a common baseline
    ^         ^        ^
    |         |        |
 Courier    Arial    Times       <- font used for a particular bit of text
  20pt      40pt     30pt

Because I haven't found any GDI+ functionality to do this directly, I came up with my own method (outlined below). However:

I wonder if there really isn't an easier way to get this done?

My current approach:

1) Gather a list of all System.Drawing.Fonts that will be used for drawing text.

2) For each Font, find the vertical position of the baseline in pixels, using the following code:

// variables used in code sample (already set)
Graphics G;
Font font;
...

// compute ratio in order to convert from font design units to pixels:
var designUnitsPerPixel = font.GetHeight(G) / 
                          font.FontFamily.GetLineSpacing(font.Style);

// get the cell ascent (baseline) position in design units:
var cellAscentInDesignUnits = font.FontFamily.GetCellAscent(font.Style);

// finally, convert the baseline position to pixels:
var baseLineInPixels = cellAscentInDesignUnits * designUnitsPerPixel;

3) For all Fonts used, determine the maximum baseLineInPixels value as computed above and store this value to maxBaseLineInPixels.

4) Draw each bit of text in the following manner:

// variables used in code sample (already set):
Graphics G;
Font font;
string text;
...

// find out how much space is needed for drawing the text
var measureF = G.MeasureString(text, font);

// determine location where text will be drawn:
var layoutRectF = new RectangleF(new PointF(0f, 0f), measureF);
layoutRectF.Y += maxBaseLineInPixels - baseLineInPixels;
// ^ the latter value 'baseLineInPixels' is specific to the font used

// draw text at specified location
G.DrawString(text, font, Brushed.Black, layoutRectF);

Am I missing something, or is there really no easier way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

羞稚 2024-08-26 06:31:47

我认为这个方法可行,请你尝试一下。

List<RectangleF> rects = new List<RectangleF>();

private void Form1_Paint(object sender, PaintEventArgs e)
{
    ////////////////////Not Set baseLine
    //baseline
    e.Graphics.DrawLine(Pens.Red , new Point(100,200),new Point(800,200));

    //words
    Point point = new Point(100,100);
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, point);
    RectangleF rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30)));
    e.Graphics.DrawRectangle(Pens.Green,rectangleF.X ,rectangleF.Y , rectangleF.Width , rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(400, 100);
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, point);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40)));
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(800, 100);
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, point);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20)));
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    ///////////////////SetBaseLine/////////////////////////////
    var maxHeight = GetMaxHeight();
    ///////////////////

    //baseLine
    e.Graphics.DrawLine(Pens.Pink, new Point(100, (int) (400 + maxHeight / 2)), new Point(800, (int) (400 + maxHeight / 2)));

    StringFormat stringFormat = new StringFormat();
    stringFormat.LineAlignment = StringAlignment.Center;

    //words
    point = new Point(100, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30)));
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, new RectangleF(rectangleF.X ,rectangleF.Y , rectangleF.Width , maxHeight) , stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(400, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40)));
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(800, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20)));
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

}

private float GetMaxHeight()
{
    float temp = 0;
    foreach (RectangleF rectangleF in rects)
        if (rectangleF.Height > temp)
            temp = rectangleF.Height;

    return temp;
}

I thinks this way is work , please you try.

List<RectangleF> rects = new List<RectangleF>();

private void Form1_Paint(object sender, PaintEventArgs e)
{
    ////////////////////Not Set baseLine
    //baseline
    e.Graphics.DrawLine(Pens.Red , new Point(100,200),new Point(800,200));

    //words
    Point point = new Point(100,100);
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, point);
    RectangleF rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30)));
    e.Graphics.DrawRectangle(Pens.Green,rectangleF.X ,rectangleF.Y , rectangleF.Width , rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(400, 100);
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, point);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40)));
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(800, 100);
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, point);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20)));
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    ///////////////////SetBaseLine/////////////////////////////
    var maxHeight = GetMaxHeight();
    ///////////////////

    //baseLine
    e.Graphics.DrawLine(Pens.Pink, new Point(100, (int) (400 + maxHeight / 2)), new Point(800, (int) (400 + maxHeight / 2)));

    StringFormat stringFormat = new StringFormat();
    stringFormat.LineAlignment = StringAlignment.Center;

    //words
    point = new Point(100, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30)));
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, new RectangleF(rectangleF.X ,rectangleF.Y , rectangleF.Width , maxHeight) , stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(400, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40)));
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

    point = new Point(800, 400);
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20)));
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat);
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height);
    rects.Add(rectangleF);

}

private float GetMaxHeight()
{
    float temp = 0;
    foreach (RectangleF rectangleF in rects)
        if (rectangleF.Height > temp)
            temp = rectangleF.Height;

    return temp;
}
潦草背影 2024-08-26 06:31:47

过去几天我一直在研究同样的事情,终于找到了答案在此博客页面。这段代码(在文章底部)对我来说非常有效,希望能帮助其他人解决这个问题:

private void DrawOnBaseline(string s, Graphics g, Font f, Brush b, Point pos)
    {
      float baselineOffset=f.SizeInPoints/f.FontFamily.GetEmHeight(f.Style)*f.FontFamily.GetCellAscent(f.Style);
      float baselineOffsetPixels = g.DpiY/72f*baselineOffset;

      g.DrawString(s,f,b,new Point(pos.X,pos.Y-(int)(baselineOffsetPixels+0.5f)),StringFormat.GenericTypographic);
    }

I've been researching the same thing for the last few days, and I finally found an answer on this blog page. This code (at the bottom of the article) worked really well for me and hopefully helps anyone else struggling with this problem:

private void DrawOnBaseline(string s, Graphics g, Font f, Brush b, Point pos)
    {
      float baselineOffset=f.SizeInPoints/f.FontFamily.GetEmHeight(f.Style)*f.FontFamily.GetCellAscent(f.Style);
      float baselineOffsetPixels = g.DpiY/72f*baselineOffset;

      g.DrawString(s,f,b,new Point(pos.X,pos.Y-(int)(baselineOffsetPixels+0.5f)),StringFormat.GenericTypographic);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文