【发布时间】:2016-07-28 05:00:12
【问题描述】:
我已经实现了一个相当基本的“对齐”方法来绘制字符串,但是我想对其进行优化,使间距更加分散。
到目前为止,我所拥有的是:
string lastword = line.Split(' ').Last();
string lineNoLastWord = line.Substring(0,line.LastIndexOf(" ")).Trim();;
g.DrawString( lineNoLastWord, Font, brush, textBounds, sf );
g.DrawString( lastword, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Right ) );
ConvertAlignment 是一个自定义方法,如下:
private StringFormat ConvertAlignment(System.Windows.TextAlignment align) {
StringFormat s = new StringFormat();
switch ( align ) {
case System.Windows.TextAlignment.Left:
case System.Windows.TextAlignment.Justify:
s.LineAlignment=StringAlignment.Near;
break;
case System.Windows.TextAlignment.Right:
s.LineAlignment=StringAlignment.Far;
break;
case System.Windows.TextAlignment.Center:
s.LineAlignment=StringAlignment.Center;
break;
}
s.Alignment = s.LineAlignment;
return s;
}
结果很接近,但需要对字符串lineNoLastWord中的空格进行一些调整。
代码背后的更多背景知识。 line 是一种方法的结果,该方法负责检测字符串是否超出范围(宽度),并将其分解为行和单词,并在执行过程中进行分解和测量,以确保整行保持在宽度范围内要绘制的区域。该方法在一个更大的类中实现了其他属性,但这里是它的要点:
internal LineBreaker breakIntoLines( string s, int maxLineWidth ) {
List<string> sResults = new List<string>();
int stringHeight;
int lineHeight;
int maxWidthPixels = maxLineWidth;
string[] lines = s.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None);
using ( Graphics g=Graphics.FromImage( Pages[CurrentPage - 1] ) ) {
g.CompositingQuality = CompositingQuality.HighQuality;
if ( maxLineWidth<=0||maxLineWidth>( Pages[CurrentPage-1].Width-X ) ) {
maxWidthPixels=Pages[CurrentPage-1].Width-X;
}
lineHeight = (Int32)( g.MeasureString( "X", Font ).Height*(float)( (float)LineSpacing/(float)100 ) );
stringHeight = (Int32)g.MeasureString( "X", Font ).Height;
foreach ( string line in lines ) {
string[] words=line.Split( new string[] { " " }, StringSplitOptions.None );
sResults.Add( "" );
for ( int i=0; i<words.Length; i++ ) {
if ( sResults[sResults.Count-1].Length==0 ) {
sResults[sResults.Count-1]=words[i];
} else {
if ( g.MeasureString( sResults[sResults.Count-1]+" "+words[i], Font ).Width<maxWidthPixels ) {
sResults[sResults.Count-1]+=" "+words[i];
} else {
sResults.Add( words[i] );
}
}
}
}
}
return new LineBreaker() {
LineHeight = lineHeight,
StringHeight = stringHeight,
MaxWidthPixels = maxWidthPixels,
Lines = sResults
};
}
internal class LineBreaker {
public List<string> Lines { get; set; }
public int MaxWidthPixels { get; set; }
public int StringHeight { get; set; }
public int LineHeight { get; set; }
public LineBreaker() {
Lines = new List<string>();
MaxWidthPixels = 0;
StringHeight = 0;
LineHeight = 0;
}
public LineBreaker( List<string> lines, int maxWidthPixels, int stringHeight, int lineHeight ) {
Lines = lines;
MaxWidthPixels = maxWidthPixels;
LineHeight = lineHeight;
StringHeight = stringHeight;
}
}
下图演示了由此引起的问题:
我也看过this stackoverflow question and answers,发现也是一种低效的空间方式,因为字符串的大小未知,文档的宽度未知,字数过多会导致字符串过长,或者太短,没有任何正确的理由。完全对齐意味着文本在左侧和右侧对齐,并且通常内部的内容尽可能均匀地间隔开。这就是我想要实现的方式。
解决方案,可能是对 lastWord 和 lineNoLastWord 字符串进行计算,并进行一些测量以确保输出的可行性,因为字符串中没有两个单词会运行或聚集在一起,并且右侧不会有填充,但左侧可能仍包含缩进或制表符。要考虑的另一部分是,如果字符串短于某个阈值,则不应应用任何理由。
更新
我有以下概念,应该可以,只需要从指定的索引中获取单词并插入适当的空格:
int lastwordwidth = (Int32)g.MeasureString(" " + lastword, Font).Width;
int extraspace=lines.MaxWidthPixels-(Int32)( g.MeasureString( " "+lineNoLastWord, Font ).Width+lastwordwidth );
int totalspacesneeded = (Int32)Math.Floor((decimal)(extraspace / lines.SpaceWidth));
int spacecount = lineNoLastWord.Count(x => x == ' ');
int currentwordspace = 0;
for ( int i=0; i<spacecount; i++ ) {
if ( currentwordspace>spacecount ) { currentwordspace = 0; }
// insert spaces where spaces already exist between each word
// use currentwordspace to determine which word to replace with a word and another space
if ( currentwordspace==0 ) {
// insert space after word
} else {
// insert space before word
}
currentwordspace++;
}
【问题讨论】:
-
你没看到this吗?如果计算一个浮点数以知道在 每个 单词之后移动多远,因此它将单词 均匀 分布在整个行中..
-
是的,我看到我想我可以选择性地插入空格,这样会更快导致更少的图形调用。
-
好吧,你可以或更好地使用 n 空间,但你需要相当精确地知道它们的宽度,这并不像听起来那么简单,因为大多数测量字符串调用不会像人们希望......但在真正遇到问题之前不要担心性能......(请参阅'premature optimization' ;-)
-
我正在尝试使用距边缘的距离减去字符串的长度。采用该结果并计算空间宽度的除数。接下来,将计算空间,然后,我不知道......某种插入空间例程,直到满足 floor() 结果。它应该足够精确,并根据需要在第一个或最后几个单词之间展开空格。我将此设置作为异步打印到 pdf 例程的一部分(请参阅我的其他文章,自从我发布解决方案以来取得了相当大的进展)
-
@SanuelJackson,我不清楚你想要实现什么。您想要在最后一个单词之前有一个空格,还是在每个单词之后有一个空格?为什么不直接使用系统理由呢?顺便说一句:您是否知道 MeasureString 对于文本片段的详细操作或插入符号定位往往不准确 - 请参阅remarks in the docs。你提到了PDF输出;最终结果是绘制成 .Net 图形,还是为了将文本注入 PDF 编写器库而进行测量?
标签: c# string system.drawing justify