Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?
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.Font
s that will be used for drawing text.
2) For each Font
, find the vertical position of t开发者_开发百科he 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 Font
s 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?
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;
}
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);
}
精彩评论