在.NET DrawingContext的DrawText方法中计算文本换行

6
我正在处理一个项目,需要我近似于以图像形式呈现的文本以及用于文本的DHTML编辑器。这些图像是使用.NET 4 DrawingContext对象的DrawText方法呈现的。
DrawText方法将文本以及字体信息和尺寸一起输入,并计算必要的换行,以使文本尽可能适应空间,如果文本太长,则在末尾放置省略号。因此,如果我有以下代码来绘制矩形中的文本,它会进行缩写:
string longText = @"A choice of five engines, although the 2-liter turbo diesel, supposedly good for 48 m.p.g. highway, is not coming to America, at least for now. A 300-horsepower supercharged gasoline engine will likely be the first offered in the United States. All models will use start-stop technology, and fuel consumption will decrease by an average of 19 percent across the A6 lineup. A 245-horsepower A6 hybrid was also unveiled, but no decision has yet been made as to its North America sales prospects. Figure later in 2012, if sufficient demand is detected.";

var drawing = new DrawingGroup();
using (var context = drawing.Open())
{
    var text = new FormattedText(longText,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface("Calibri"),
        30,
        Brushes.Green);
    text.MaxTextHeight = myRect.Height;
    text.MaxTextWidth = myRect.Width;

    context.DrawText(text, new Point(0, 0));
}

var db = new DrawingBrush(drawing);
db.Stretch = Stretch.None;
myRect.Fill = db;

有没有一种方法可以计算文本如何被换行?在这个例子中,输出的文本会在“2升”和“48英里每加仑”处换行,如下图所示: alt text
2个回答

3

您可以使用Graphics.MeasureString(String, Font, Int32)函数。您需要传递字符串、字体和最大宽度,该函数将返回一个SizeF对象,其中包含形成的矩形。您可以使用此方法获取总高度,从而得到行数:

Graphics g = ...;
Font f = new Font("Calibri", 30.0);
SizeF sz = g.MeasureString(longText, f, myRect.Width);
float height = sz.Height;
int lines = (int)Math.round(height / f.Height); // overall height divided by the line height = number of lines

获取Graphics对象的方法有很多种,只要您使用它来测量而不是绘制(您可能需要更正其DpiX、DpiY和PageUnit字段,因为这些会影响测量),任何一种方法都可以。

获取Graphics对象的方式:

Graphics g = e.Graphics; // in OnPaint, with PaintEventArgs e
Graphics g = x.CreateGrahics(); // where x is any Form or Control
Graphics g = Graphics.CreateFrom(img); // where img is an Image.

Graphics.MeasureString不是TextRenderer.MeasureText()的替代品。 - Hans Passant
+1 Hans。实际上,这个问题的根源在于使用上述技术的代码几乎经常不准确。 - t3rse

2

不确定您是否仍需要解决方案,或者此特定解决方案是否适用于您的应用程序,但是如果您在using块之后插入以下片段,它将显示每行文本(因此可以确定文本在哪里被换行)。

我采用了非常“ghetto/guerrilla”方法,即在调试时浏览属性,寻找换行的文本段 - 我找到了它们,并且它们位于可访问的属性中...所以就是这样。可能有更合适/直接的方法。

// Object heirarchy:
// DrawingGroup (whole thing)
//  - DrawingGroup (lines)
//     - GlyphRunDrawing.GlyphRun.Characters (parts of lines)

// Note, if text is clipped, the ellipsis will be placed in its own 
// separate "line" below.  Give it a try and you'll see what I mean.

List<DrawingGroup> lines = drawing.Children.OfType<DrawingGroup>().ToList();

foreach (DrawingGroup line in lines)
{
    List<char> lineparts = line.Children
        .OfType<GlyphRunDrawing>()
        .SelectMany(grd => grd.GlyphRun.Characters)
        .ToList();

    string lineText = new string(lineparts.ToArray());

    Debug.WriteLine(lineText);
}

顺便说一下,嗨David. :-)

太好了。我正在查看框架源代码,并看到了枚举行的技术,但似乎FormattedText对象上的所有内容都是私有的;我差点使用反射来公开这些属性,但这是一个更好的解决方案。顺便说一句,嗨,肖恩!自从在拉米拉达的那条街上以来已经很长时间了! - t3rse

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