获得符合所提供宽度的字符串的最佳方法是什么?

时间:2009-07-22 06:57:58

标签: c# javascript measurement

我现在面临的问题是获取符合所提供宽度的字符串(

例如,如果我有这样的句子(在JavaScript中):

var mystring = "This is my sentence, I need to get just 50 pixels from it."

如果我在C#中使用MeasureString方法,我可以得到宽度:

Font font = new Font("Segoe UI", 11, FontStyle.Regular, GraphicsUnit.Point);
SizeF size = graphics.MeasureString(mystring, font);

假设此字符串的宽度为400px,但我在网站上显示的最大宽度为50px。

如果我缩短字符串并测量它直到它的宽度小于50px它可以工作,但它需要多次迭代,而根本不是一个好的解决方案

有没有人有这方面的好解决方案?

感谢。

5 个答案:

答案 0 :(得分:1)

使用二进制搜索获得最佳长度,不应进行多次迭代。鉴于文本渲染的复杂性,我相信这是你能做到的最好的。你不能只为每个焦点取一个宽度并将它们加在一起。

答案 1 :(得分:1)

我想在此部分添加 - “如果我缩短字符串并测量它,直到宽度小于50px ......”

为什么不从字符串的中间开始进行二进制搜索。这是初学者的location。无论弦长多久 - 找出你理想的长度还需要更短的时间。复杂性从n减少到log(n)。

为了获得更好的结果,您可以在开始二进制搜索之前截断字符串,如果它很长,例如500个字符。

答案 2 :(得分:1)

string myString = "This is my sentence, I need to get just 50 pixels from it."
Font font = new Font("Segoe UI", 11, FontStyle.Regular, GraphicsUnit.Point); 

int desiredWidth = 50;
int myStringWidth = TextRenderer.MeasureText(myString , font).Width;
string result = mystring.Substring(0, myString.Length * desiredWidth / myStringWidth);

此解决方案不考虑换行符。

答案 3 :(得分:0)

您可以将字符串的宽度近似为子字符串宽度的总和。如果你的子串由空格界限†它甚至可能是一个很好的近似值。但是你要知道完全任何特定字符串的宽度,因为文本渲染引擎会执行字距调整(改变字符之间的间距)。

†你可能希望它们至少在欧洲语言中使用,因为阅读文本只能打破空格而不是文本中断的文字更容易,即使它导致文本看起来更加粗糙。

答案 4 :(得分:0)

StringBoxer类的GetBoxedString方法"估计"你可以放入一个矩形并返回它的字符串数量(以空格分隔的单词,输入分隔的单词甚至长字符)(复制/粘贴它时要小心,因为我不能将我的整个代码整合到灰色中下面的方框):

public sealed class StringBoxer   {

public string GetBoxedString(string s, Size size, Font font)
{

  int longestStringLengthInWidth = 0;
  var result = string.Empty;
  if (size.Height < font.Height)
  {
    return string.Empty;
  }

  using (var bmp = new Bitmap(size.Width, size.Height))
  {
    int index = 0;
    var words = this.SplitString(s);


    var measuredSizeBeforeAddingWord = new SizeF(0, 0);

    using (var graphic = Graphics.FromImage(bmp))
    {
      longestStringLengthInWidth = CalculateLongestStringLength(size, font);

      do
      {
        if (words[index].Length > longestStringLengthInWidth)
        {
          //// If a word is longer than the maximum string length for the specified size then break it into characters and add char 0 at the begining of each of those characters
          var brokenCharacters = words[index].Select(c => ((char)0) + c.ToString()).ToList();
          brokenCharacters.Add(" ");
          words.RemoveAt(index);
          words.InsertRange(index, brokenCharacters);
        }

        var measuredSizeAfterAddingWord = graphic.MeasureString(result + (!words[index].EndsWith("\n") ? words[index] + " " : words[index]), font, size);
        if ((words[index].Contains('\n') || measuredSizeAfterAddingWord == measuredSizeBeforeAddingWord) && measuredSizeAfterAddingWord.Height >= size.Height-font.Height)
        {
          return result.TrimEnd();
        }

        measuredSizeBeforeAddingWord = measuredSizeAfterAddingWord;

        if (words[index].Contains((char)0))
        {
          result += words[index].Replace(((char)0).ToString(), string.Empty);
        }
        else
        {
          result += (!words[index].EndsWith("\n") ? words[index] + " " : words[index]);
        }

        index++;
      }
      while (index < words.Count);
    }
  }

  return result.TrimEnd();
}

private List<string> SplitString(string s)
{
  var words = s.Split(' ').ToList();
  var index = 0;
  do
  {
    // If a word contains Enter key(s) then break it into more words and replace them with the original word.
    if (!words[index].Contains("\n"))
    {
      index++;
      continue;
    }

    var enterSplitWords = (words[index] + " ").Split('\n');
    var brokenWords = enterSplitWords.Select(str => (enterSplitWords.LastOrDefault() != str ? str + "\n" : str).Replace(" ", string.Empty)).ToList();
    words.RemoveAt(index);
    words.InsertRange(index, brokenWords);
    index += brokenWords.Count;
  }
  while (index < words.Count);

  return words;
}

private static int CalculateLongestStringLength(Size size, Font font)
{
  var tempString = string.Empty;
  var longestStringLengthInWidth = 0;
  using (var bmp = new Bitmap(size.Width, size.Height))
  {
    using (var graphic = Graphics.FromImage(bmp))
    {
      do
      {
        if (Math.Floor(graphic.MeasureString(tempString, font, size).Height) <= font.Height)
        {
          longestStringLengthInWidth++;
        }
        else
        {
          break;
        }

        tempString += "x";
      } while (true);
    }
  }

  return longestStringLengthInWidth;
}

}