Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为c中的DrawString()方法手动调整字符串#_C#_String_System.drawing_Justify - Fatal编程技术网

C# 为c中的DrawString()方法手动调整字符串#

C# 为c中的DrawString()方法手动调整字符串#,c#,string,system.drawing,justify,C#,String,System.drawing,Justify,我已经为绘制字符串实现了一个相当基本的“Justify”方法,但是我想对其进行优化,使间距更加分散 到目前为止,我得到的是: string lastword = line.Split(' ').Last(); string lineNoLastWord = line.Substring(0,line.LastIndexOf(" ")).Trim();; g.DrawString( lineNoLastWord, Font, brush, textBounds, sf ); g.DrawStrin

我已经为绘制字符串实现了一个相当基本的“Justify”方法,但是我想对其进行优化,使间距更加分散

到目前为止,我得到的是:

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
中的空格进行一些调整

代码后面还有一点背景<代码>行是一种方法的结果,该方法负责检测字符串是否超出边界(宽度),并将其拆分为行和字,同时进行拆分和测量,以确保整条线保持在待绘制区域的宽度范围内。该方法在一个更大的类中实现其他属性,但其要点如下:

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;
    }
}

我想出了一个很好的解决方案。下面是我的
DrawString
方法,它支持文本对齐,并将根据需要断开并添加新的“页面”
Pages
,是一个
List
对象,
NewPage()
方法负责将新图像添加到此列表中

/// <summary>
/// Add a new string to the current page
/// </summary>
/// <param name="text">The string to print</param>
/// <param name="align">Optional alignment of the string</param>
public void DrawString(string text, System.Windows.TextAlignment align = System.Windows.TextAlignment.Left, int MaxWidth = -1 ) {
    RectangleF textBounds;
    SolidBrush brush = new SolidBrush( ForeColor );
    StringFormat sf = ConvertAlignment(align);
    LineBreaker lines = breakIntoLines(text, MaxWidth);

    int currentLine = 1;

    int originX = X;

    foreach ( string line in lines.Lines ) {
        // add string to document
        using ( Graphics g=Graphics.FromImage( Pages[CurrentPage - 1] ) ) {
            g.CompositingQuality = CompositingQuality.HighQuality;

            textBounds=new RectangleF( X, Y, lines.MaxWidthPixels, lines.StringHeight );

            if ( align==System.Windows.TextAlignment.Justify ) {

                if ( currentLine<lines.Lines.Count ) {
                    string lastword=line.Split( ' ' ).Last();
                    if ( line.Contains( ' ' ) ) {
                        // routine to caclulate how much padding is needed and apply the extra spaces as evenly as possibly by looping
                        // through the words. it starts at the first word adding a space after if needed and then continues through the
                        // remaining words adding a space before them as needed and excludes the right most word which is printed as right
                        // align always.
                        string lineNoLastWord=line.Substring( 0, line.LastIndexOf( " " ) ).Trim();
                        List<string> words=lineNoLastWord.Split( ' ' ).ToList<string>();
                        int lastwordwidth=(Int32)g.MeasureString( " "+lastword, Font ).Width;
                        int extraspace=lines.MaxWidthPixels-(Int32)( g.MeasureString( " "+lineNoLastWord, Font ).Width+lastwordwidth );
                        int totalspacesneeded=(Int32)Math.Ceiling( (decimal)extraspace/(decimal)lines.SpaceWidth );
                        int spacecount=lineNoLastWord.Count( x => x==' ' );
                        int currentwordspace=0;

                        if ( words.Count>1 ) {
                            while ( totalspacesneeded>0 ) {
                                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
                                    words[currentwordspace]+=" ";
                                } else {
                                    // insert space before word
                                    words[currentwordspace]=" "+words[currentwordspace];
                                }
                                currentwordspace++;
                                totalspacesneeded--;
                                if ( totalspacesneeded==0 ) { break; }
                            }
                        }
                        lineNoLastWord=String.Join( " ", words );

                        g.DrawString( lineNoLastWord, Font, brush, textBounds, sf );
                        g.DrawString( lastword, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Right ) );
                    } else {
                        // when only 1 word, just draw it
                        g.DrawString( line, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Left ) );
                    }
                } else {
                    // just draw the last line
                    g.DrawString( line, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Left ) );
                }

            } else {
                g.DrawString( line, Font, brush, textBounds, sf );
            }
        }
        Y+=lines.LineHeight;
        if ( Y+lines.LineHeight>Pages[CurrentPage-1].Height ) {
            NewPage();
            if ( currentLine<lines.Lines.Count ) { X=originX; }
        }
        currentLine++;
    }
}

/// <summary>
/// Break a long string into multiple lines. Is also carriage return aware.
/// </summary>
/// <param name="s">the string</param>
/// <param name="maxLineWidth">the maximum width of the rectangle. if -1, will use the full width of the image</param>
/// <returns></returns>
internal LineBreaker breakIntoLines( string s, int maxLineWidth ) {
    List<string> sResults = new List<string>();

    int stringHeight;
    int lineHeight;
    int maxWidthPixels = maxLineWidth;
    int spaceWidth;

    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;
        spaceWidth=(Int32)g.MeasureString( " ", Font ).Width;
        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,
        SpaceWidth = spaceWidth
    };
}

/// <summary>
/// Helper method to convert TextAlignment to StringFormat
/// </summary>
/// <param name="align">System.Windows.TextAlignment</param>
/// <returns>System.Drawing.StringFormat</returns>
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;
}

/// <summary>
/// Class to hold the line data after broken up and measured using breakIntoLines()
/// </summary>
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 int SpaceWidth { get; set; }

    public LineBreaker() {
        Lines = new List<string>();
        MaxWidthPixels = 0;
        StringHeight = 0;
        LineHeight = 0;
        SpaceWidth = 0;
    }

    public LineBreaker( List<string> lines, int maxWidthPixels, int stringHeight, int lineHeight, int spaceWidth ) {
        Lines = lines;
        MaxWidthPixels = maxWidthPixels;
        LineHeight = lineHeight;
        StringHeight = stringHeight;
        SpaceWidth = spaceWidth;
    }
}
//
///向当前页面添加新字符串
/// 
///要打印的字符串
///字符串的可选对齐方式
public void DrawString(string text,System.Windows.TextAlignment=System.Windows.TextAlignment.Left,int-MaxWidth=-1){
矩形文本边界;
SolidBrush笔刷=新的SolidBrush(前景色);
StringFormat sf=转换对齐(对齐);
换行符行=换行符行(文本,最大宽度);
int currentLine=1;
int originX=X;
foreach(行中的字符串行。行){
//向文档中添加字符串
使用(Graphics g=Graphics.FromImage(第[CurrentPage-1]页)){
g、 合成质量=合成质量。高质量;
textBounds=新矩形F(X,Y,lines.MaxWidthPixels,lines.StringHeight);
if(align==System.Windows.TextAlignment.Justify){
如果(当前线x='');
int currentwordspace=0;
如果(字数>1){
而(总空间需求>0){
如果(currentwordspace>spacecount){currentwordspace=0;}
//在每个单词之间已存在空格的位置插入空格
//使用currentwordspace确定用一个单词和另一个空格替换哪个单词
如果(currentwordspace==0){
//在单词后插入空格
字[currentwordspace]+=“”;
}否则{
//在单词前插入空格
单词[currentwordspace]=“”+单词[currentwordspace];
}
currentwordspace++;
总空间需要--;
如果(totalspacesneed==0){break;}
}
}
lineNoLastWord=String.Join(“,words);
g、 抽绳(lineNoLastWord、字体、画笔、文本边界、sf);
g、 DrawString(lastword、字体、画笔、textBounds、ConvertAlignment(System.Windows.TextAlignment.Right));
}否则{
//当只有一个单词时,就画出来
g、 抽绳(线条、字体、画笔、文本边界、ConvertAlignment(System.Windows.TextAlignment.Left));
}
}否则{
//画最后一条线
g、 抽绳(线条、字体、画笔、文本边界、ConvertAlignment(System.Windows.TextAlignment.Left));
}
}否则{
g、 抽绳(线条、字体、画笔、文本边界、sf);
}
}
Y+=线条。线条高度;
如果(Y+lines.LineHeight>Pages[CurrentPage-1].Height){
NewPage();

如果(CurrentLine你没看到吗?如果计算一个浮点数来知道每个单词后面移动的距离,那么它会将单词均匀地分布在整行上。。是的,我看到我可以有选择地插入空格,这将更快地导致更少的图形调用。你可以或更好地使用n个空格,但你需要精确地知道它们的宽度,这并不像听起来那么简单,因为大多数测量调用都不会像人们希望的那样精确。但在实际出现问题之前,不要担心性能问题。(请参阅;-)我尝试使用从边缘到字符串长度的距离减去字符串的长度来进行此操作。获取该结果并计算空间宽度的除数。接下来,将计算空间,然后,我不知道……某种类型的插入空间例程,直到地板()结果已经满足。它应该足够精确,并根据需要在前几个或最后几个单词之间分配空格。我将此设置作为异步打印到pdf例程的一部分(请参阅我的其他文章,自从发布解决方案以来,已经取得了相当大的进展)@SanuelJackson,我不清楚你到底想实现什么。你想在最后一个单词前留一个空格,还是在每个单词后留一个空格?为什么不使用系统对正?作为旁白:你是否意识到,对于文本片段或插入符号的详细操作,度量值往往不准确?请参阅。你提到的PDF输出;最终结果是绘制到.Net图形中,还是为了将文本插入PDF writer库而进行测量?
/// <summary>
/// Add a new string to the current page
/// </summary>
/// <param name="text">The string to print</param>
/// <param name="align">Optional alignment of the string</param>
public void DrawString(string text, System.Windows.TextAlignment align = System.Windows.TextAlignment.Left, int MaxWidth = -1 ) {
    RectangleF textBounds;
    SolidBrush brush = new SolidBrush( ForeColor );
    StringFormat sf = ConvertAlignment(align);
    LineBreaker lines = breakIntoLines(text, MaxWidth);

    int currentLine = 1;

    int originX = X;

    foreach ( string line in lines.Lines ) {
        // add string to document
        using ( Graphics g=Graphics.FromImage( Pages[CurrentPage - 1] ) ) {
            g.CompositingQuality = CompositingQuality.HighQuality;

            textBounds=new RectangleF( X, Y, lines.MaxWidthPixels, lines.StringHeight );

            if ( align==System.Windows.TextAlignment.Justify ) {

                if ( currentLine<lines.Lines.Count ) {
                    string lastword=line.Split( ' ' ).Last();
                    if ( line.Contains( ' ' ) ) {
                        // routine to caclulate how much padding is needed and apply the extra spaces as evenly as possibly by looping
                        // through the words. it starts at the first word adding a space after if needed and then continues through the
                        // remaining words adding a space before them as needed and excludes the right most word which is printed as right
                        // align always.
                        string lineNoLastWord=line.Substring( 0, line.LastIndexOf( " " ) ).Trim();
                        List<string> words=lineNoLastWord.Split( ' ' ).ToList<string>();
                        int lastwordwidth=(Int32)g.MeasureString( " "+lastword, Font ).Width;
                        int extraspace=lines.MaxWidthPixels-(Int32)( g.MeasureString( " "+lineNoLastWord, Font ).Width+lastwordwidth );
                        int totalspacesneeded=(Int32)Math.Ceiling( (decimal)extraspace/(decimal)lines.SpaceWidth );
                        int spacecount=lineNoLastWord.Count( x => x==' ' );
                        int currentwordspace=0;

                        if ( words.Count>1 ) {
                            while ( totalspacesneeded>0 ) {
                                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
                                    words[currentwordspace]+=" ";
                                } else {
                                    // insert space before word
                                    words[currentwordspace]=" "+words[currentwordspace];
                                }
                                currentwordspace++;
                                totalspacesneeded--;
                                if ( totalspacesneeded==0 ) { break; }
                            }
                        }
                        lineNoLastWord=String.Join( " ", words );

                        g.DrawString( lineNoLastWord, Font, brush, textBounds, sf );
                        g.DrawString( lastword, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Right ) );
                    } else {
                        // when only 1 word, just draw it
                        g.DrawString( line, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Left ) );
                    }
                } else {
                    // just draw the last line
                    g.DrawString( line, Font, brush, textBounds, ConvertAlignment( System.Windows.TextAlignment.Left ) );
                }

            } else {
                g.DrawString( line, Font, brush, textBounds, sf );
            }
        }
        Y+=lines.LineHeight;
        if ( Y+lines.LineHeight>Pages[CurrentPage-1].Height ) {
            NewPage();
            if ( currentLine<lines.Lines.Count ) { X=originX; }
        }
        currentLine++;
    }
}

/// <summary>
/// Break a long string into multiple lines. Is also carriage return aware.
/// </summary>
/// <param name="s">the string</param>
/// <param name="maxLineWidth">the maximum width of the rectangle. if -1, will use the full width of the image</param>
/// <returns></returns>
internal LineBreaker breakIntoLines( string s, int maxLineWidth ) {
    List<string> sResults = new List<string>();

    int stringHeight;
    int lineHeight;
    int maxWidthPixels = maxLineWidth;
    int spaceWidth;

    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;
        spaceWidth=(Int32)g.MeasureString( " ", Font ).Width;
        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,
        SpaceWidth = spaceWidth
    };
}

/// <summary>
/// Helper method to convert TextAlignment to StringFormat
/// </summary>
/// <param name="align">System.Windows.TextAlignment</param>
/// <returns>System.Drawing.StringFormat</returns>
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;
}

/// <summary>
/// Class to hold the line data after broken up and measured using breakIntoLines()
/// </summary>
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 int SpaceWidth { get; set; }

    public LineBreaker() {
        Lines = new List<string>();
        MaxWidthPixels = 0;
        StringHeight = 0;
        LineHeight = 0;
        SpaceWidth = 0;
    }

    public LineBreaker( List<string> lines, int maxWidthPixels, int stringHeight, int lineHeight, int spaceWidth ) {
        Lines = lines;
        MaxWidthPixels = maxWidthPixels;
        LineHeight = lineHeight;
        StringHeight = stringHeight;
        SpaceWidth = spaceWidth;
    }
}