问题
Trying to get string width in C# to simulate wordwrap and position of text (now written in richTextBox).
Size of richTextBox is 555x454 px and I use monospaced font Courier New 12pt.
I tried TextRenderer.MeasureText()
and also Graphics.MeasureString()
methods.
TextRenderer
was returning bigger values than Graphics
so text which normally fits into one line, my code determined should be wrapped to other line.
But with using Graphics
, on the other hand, my code determined that particular string is shorter than it is printed in original richTextBox so it was wrapped to next line in wrong place.
During debugging I found out that computed widths differs, which is strange because I use monospaced font so widths should be 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 compute string width with C# and so simulate word wrap and determine particular text positions in pixels?
Why is width different in different characters when using monospaced font?
Note: I am working on personal Eye tracking project and I want to determine, where particular pieces of text was placed during experiment so I can tell on which words was user looking. For ex. at time t
user was looking on point [256,350]px and I know that at this place there is call of method WriteLine
.
My target visual stimulus is source code, with indents, tabs, line endings, placed in some editable text area (In the future maybe some simple online 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:
I believe that it is much easier to accomplish your task by obtaining a character under a given location on a screen. For example, if you're using the RichTextBox control, refer to the RichTextBox.GetCharIndexFromPosition method to get the index of the character nearest to the specified location. Here is some 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();
}
回答2:
So I finally added some things in my code. I will shorten 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 this corrections I get correct width of common characters (with use of monospaced font i get equal widths).
But there is still problem with other whitespaces like \t
and \n
where I get 0.0078125
and 9.6015625
when measuring width with Courier New, 12pt font. The second value is a width of any character typed with this font so it is not a big problem but it would be better to be 0 or am I wrong? If anybody have a suggestion to solve this problem leave a comment please.
来源:https://stackoverflow.com/questions/42362999/strange-behavior-while-computing-string-width-in-pixels-for-simulation-of-word-w