2016-04-07 9 views
0

Я применил довольно рудиментарный метод «Justify» для рисования строки, однако я бы хотел ее оптимизировать, чтобы расстояние было немного более разбросанным.Обоснование строки вручную для метода DrawString() в C#

То, что я до сих пор, это:

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

Следующее изображение демонстрирует проблему, что это является причиной:

demonstration

Я также видел this stackoverflow question and answers, и обнаружил, что это также неэффективный путь к пространству из-за неизвестного размера строки, а неизвестная ширина документа приведет к тому, что строка будет слишком длинной, если слишком много слов, или слишком коротким, при этом ничего не будет оправдано. Полное обоснование означает, что текстовые строки расположены как на левой, так и на правой сторонах, и, как правило, содержимое внутри равномерно разнесено как можно больше. Вот как я хотел бы его реализовать.

Решение, вероятно, расчет на lastWord и lineNoLastWord строк с некоторыми измерениями, чтобы обеспечить жизнеспособность выхода в том, что нет двух слов в строке будет работать или кучу вместе, и там не будет обивки на правая сторона, однако левая может по-прежнему содержать отступ или вкладку. Другая часть, которую следует рассмотреть, - это если строка короче определенного порога, тогда никакое обоснование не должно применяться.

UPDATE

У меня есть следующая концепция, которая должна работать, просто нужно, чтобы получить слово из указанного индекса и вставьте правильное место:

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

Разве вы не видели [это] (http://csharphelper.com/blog/2014/10/fully-justify-a-line-of-text-in- с /)? Если вы вычисляете float, чтобы знать, как далеко двигаться после слова __each__, чтобы он распространял слова __evenly__ по строке. – TaW

+0

Да, я видел, что, я полагаю, я мог бы выборочно вставлять пробелы, которые будут быстрее вызывать меньше графических вызовов. –

+0

Ну, вы можете или лучше использовать n-пространства, но вам нужно будет точно узнать их ширину, что не так просто, как кажется, учитывая, что большинство измерительных вызовов не будут такими точными, как хотелось бы. не беспокойтесь о производительности, прежде чем у вас действительно возникнет проблема. (http://c2.com/cgi/wiki?PrematureOptimization) ;-) – TaW

ответ

0

Я разобрался решение для это хорошо работает. Ниже приведен мой метод DrawString, который является текстовым выравниванием, и будет разбит и добавит новые «Страницы» по мере необходимости. Pages, является объектом List<Image>, а метод 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; 
    } 
} 

выше комбинации метод поддерживает:

  • выравнивание по левому краю
  • Выравнивание по правому краю Выравнивание
  • Центр
  • Обоснуйте выравнивание - Последняя строка посланный с Обоснуйте, просто быть распечатаны выравнивание по левому краю поскольку это, как правило, конец абзаца.
  • Все отправленные строки будут проверять ограничения, используя изображение, или указанную ширину, которая находится в пределах диапазона между текущей позицией X и краем. Ширины, превышающие или в отрицательном диапазоне, будут установлены на расстояние между X и правой стороной изображения. Каждая строка находится внутри собственной ограничивающей рамки.
  • Линии не обрезаны.
  • Линия обертывание разбивается на слово, как требуется, и на возврат каретки («\ п» или «\ г \ п»)

LineSpacing является просто целым числом, где 100 означает 100% LineHeight. X - целое число для получения/установки позиции X. Y - целое число для получения/установки позиции Y. Font является геттер/сеттер для System.Drawing.Font

 Смежные вопросы

  • Нет связанных вопросов^_^