Strange behavior when calculating the line width in pixels to model word wrap

Trying to get line width in C # to simulate wordwrap and text position (now written in richTextBox).

The size of the richTextBox is 555x454 px and I am using the Courier New 12pt monospace font.

I tried the methods TextRenderer.MeasureText() , as well as Graphics.MeasureString() .

TextRenderer returned more values ​​than Graphics , so the text, which usually fits on one line, my code should be wrapped in another line.

But using Graphics , on the other hand, my code determined that a particular line was shorter than it was printed in the original richTextBox, so it was moved to the next line in the wrong place.

During debugging, I found that the calculated width is different, which is strange because I use a monospaced font, so the width should be the same for all characters. But I get something like this from Graphics.MeasureString() (example: '' - 5.33333254, 'S' - 15.2239571, '\ r' - 5.328125).

How can I accurately calculate the line width using C # and thus simulate word wrap and define specific text positions in pixels?

Why is the font width different from different characters when using a monospace font?

Note. I am working on a personal eye tracking project, and I want to determine where the individual fragments of the text were placed during the experiment, so I can tell you which words the user had. E.g. at time t user was looking at the point [256,350] px, and I know that at this point there is a call to the WriteLine method. My target visual stimulus is the source code with indentation, tabs, line endings placed in some editable text area (in the future, maybe a simple simple source code editor).

Here is my code:

  //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; } 
+1
c # fonts word-wrap eye-tracking
Feb 21 '17 at 9:09 on
source share
2 answers

So, I finally added some things to my code. I will cut it.

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

With these fixes, I get the correct width for common characters (using a monospaced font, I get an equal width).

But there is still a problem with other spaces like \t and \n , where I get 0.0078125 and 9.6015625 when measuring the width with Courier New, 12pt font. The second value is the width of any character typed with this font, so this is not a big problem, but would it be better to be 0, or am I mistaken? If anyone has a suggestion to solve this problem, leave a comment.

0
Feb 21 '17 at 17:00
source share

I find it much easier to complete your task by getting a symbol at a specific place on the screen. For example, if you use a RichTextBox control, refer to RichTextBox.GetCharIndexFromPosition to get the index of the character closest to the specified location. Here is a sample code that demonstrates the idea:

 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
Feb 21 '17 at 11:19 on
source share



All Articles