2017-02-21 3 views
1

Попытка получить ширину строки в C# для имитации словарной и текстовой позиции (теперь она написана в richTextBox).Странное поведение при вычислении ширины строки в пикселях для моделирования переноса слов

Размер richTextBox составляет 555x454 px, и я использую моноширинный шрифт Courier New 12pt.

Я пробовал TextRenderer.MeasureText() а также Graphics.MeasureString() методов.

TextRenderer возвращал более крупные значения, чем Graphics, поэтому текст, который обычно помещается в одну строку, мой код должен быть завернут в другую строку.

Но с использованием Graphics мой код определил, что конкретная строка короче, чем она напечатана в оригинальном richTextBox, поэтому она была перенесена на следующую строку в неправильном месте.

Во время отладки я выяснил, что рассчитанная ширина отличается, что странно, потому что я использую моноширинный шрифт, поэтому ширина должна быть одинаковой для всех символов. Но я получаю что-то вроде этого от Graphics.MeasureString() (пример .: '' - 5.33333254, 'S' - 15.2239571, '\ r' - 5.328125).

Как я могу точно вычислить ширину строки с помощью C# и таким образом смоделировать перенос слов и определить определенные текстовые позиции в пикселях?

Почему ширина шрифта различна при использовании моноширинного шрифта?

Примечание: Я работаю над личным проектом отслеживания глаз, и я хочу определить, где были размещены отдельные фрагменты текста во время эксперимента, поэтому я могу рассказать, какие слова были пользователем. Напр. в момент времени t пользователь смотрел на точку [256,350] px, и я знаю, что на этом месте есть вызов метода WriteLine. Мой целевой визуальный стимул - это исходный код с отступами, вкладками, окончаниями строк, размещенными в некоторой редактируемой текстовой области (в будущем, возможно, простой простой редактор исходного кода).

Вот мой код:

//before method call 
    var font = new Font("Courier New", 12, GraphicsUnit.Point); 
    var graphics = this.CreateGraphics(); 
    var wrapped = sourceCode.WordWrap(font, 555, graphics); 

    public static List<string> WordWrap(this string sourceCode, Font font, int width, Graphics g) 
     { 
      var wrappedText = new List<string>(); // output 
      var actualLine = new StringBuilder(); 
      var actualWidth = 0.0f; // temp var for computing actual string length 
      var lines = Regex.Split(sourceCode, @"(?<=\r\n)"); // split input to lines and maintain line ending \r\n where they are 
      string[] wordsOfLine; 

      foreach (var line in lines) 
      { 
       wordsOfLine = Regex.Split(line, @"(|\t)").Where(s => !s.Equals("")).ToArray(); // split line by tabs and spaces and maintain delimiters separately 

       foreach (string word in wordsOfLine) 
       { 
        var wordWidth = g.MeasureString(word, font).Width; // compute width of word 

        if (actualWidth + wordWidth > width) // if actual line width is grather than width of text area 
        { 
         wrappedText.Add(actualLine.ToString()); // add line to list 
         actualLine.Clear(); // clear StringBuilder 
         actualWidth = 0; // zero actual line width 
        } 

        actualLine.Append(word); // add word to actual line 
        actualWidth += wordWidth; // add word width to actual line width 
       } 

       if (actualLine.Length > 0) // if there is something in actual line add it to list 
       { 
        wrappedText.Add(actualLine.ToString()); 
       } 
       actualLine.Clear(); // clear vars 
       actualWidth = 0; 
      } 

      return wrappedText; 
     } 

ответ

0

Итак, я, наконец, добавил некоторые вещи в свой код. Я сокращу его.

public static List<string> WordWrap(this string sourceCode, Font font, int width, Graphics g) 
    { 
     g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; 
     var format = StringFormat.GenericTypographic; 
     format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces; 

     var width = g.MeasureString(word, font, 0, format).Width; 
    } 

С этой коррекцией я получаю правильную ширину общих символов (с использованием шрифта моноширинном я получить одинаковую ширину).

Но есть еще проблема с другими непечатаемыми как \t и \n, где я получаю 0.0078125 и 9.6015625 при измерении ширины с Courier New, 12pt шрифт. Второе значение - это ширина любого символа, набранного с помощью этого шрифта, поэтому это не большая проблема, но было бы лучше быть 0, или я ошибаюсь? Если у кого-нибудь есть предложение решить эту проблему, оставьте комментарий.

0

Я считаю, что это гораздо легче выполнить свою задачу, получив символ в заданном месте на экране. Например, если вы используете элемент управления RichTextBox, обратитесь к методу RichTextBox.GetCharIndexFromPosition, чтобы получить индекс символа, ближайшего к указанному местоположению. Вот пример кода, который демонстрирует идею:

private void richTextBox1_MouseMove(object sender, MouseEventArgs e) 
{ 
    var textIndex = richTextBox1.GetCharIndexFromPosition(e.Location); 
    if (richTextBox1.Text.Length > 0) 
     label1.Text = richTextBox1.Text[textIndex].ToString(); 
} 
+0

Это довольно сложно. RichTextBox предназначен только для тестирования.И более того, я держу его разделенным. Будет одно приложение (возможно, какой-то редактор исходного кода в Интернете, но теперь просто простой RichTextBox) и другое отдельное приложение, которое будет обрабатывать автономные данные, собранные в первом приложении. – Gondil

+1

Да, я понимаю, что это сложно, но насколько я понимаю вашу конечную цель, как вы говорите, «поэтому я могу сказать, на какие слова был пользователь». На веб-странице подход с вычислением размера текста и симуляцией переноса слов является более сложным из-за разных разрешений экрана, разных браузеров, различных настроек масштабирования и т. Д. И т. Д. Вот почему я считаю, что легче получить текст, используя определенную точку на экране. Для Интернета этот подход обсуждался здесь: http://stackoverflow.com/questions/2444430/how-to-get-a-word-under-cursor-using-javascript – Pasick

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