使用 GDI+,沿公共基线对齐文本(以几种不同字体绘制)的最简单方法是啥?
Posted
技术标签:
【中文标题】使用 GDI+,沿公共基线对齐文本(以几种不同字体绘制)的最简单方法是啥?【英文标题】:Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?使用 GDI+,沿公共基线对齐文本(以几种不同字体绘制)的最简单方法是什么? 【发布时间】:2011-01-09 03:46:44 【问题描述】:我的问题:
我目前正在开发一个自定义用户控件,该控件在一行上显示文本(每个文本可能具有不同的字体)。我想将所有这些文本完全对齐到一个共同的基线上。例如:
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
s 的列表。
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
s,确定上面计算的最大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);
是我遗漏了什么,还是真的没有更简单的方法?
【问题讨论】:
感谢您的反馈。听到(嗯... :) 使用 GDI+ 绘制文本实际上是 这么复杂,而且我不只是想象这一点,我松了一口气。从那以后,我决定放弃这个问题并继续使用 WPF(GDI+ 不再重要,希望事情会更容易一些)。 【参考方案1】:我觉得这个方法行得通,请你试试。
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;
【讨论】:
感谢发帖。但是,不幸的是,这个解决方案没有帮助......对不起。这有两个原因:1.我早就放弃了这个(请参阅原始发布日期)并决定改用 WPF。 2. 我怀疑您的代码中有一些好的方法(MeasureString
、GetMaxHeight
);不幸的是,剩下的大部分代码都暗示了一些别的东西,即您纯粹使用预先计算的固定坐标和两次绘制文本(这似乎没有必要)。不过,感谢您抽出宝贵时间回答。【参考方案2】:
这几天我一直在研究同样的事情,我终于找到了答案on this blog page。这段代码(在文章的底部)对我来说非常有效,希望能帮助其他任何人解决这个问题:
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);
【讨论】:
以上是关于使用 GDI+,沿公共基线对齐文本(以几种不同字体绘制)的最简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Xamarin Forms 将不同字体大小的标签垂直对齐到同一基线
Apple Watch:如何在基线处对齐两个不同字体大小的标签?