我现在面临的问题是获取符合所提供宽度的字符串(
)例如,如果我有这样的句子(在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它可以工作,但它需要多次迭代,而根本不是一个好的解决方案。
有没有人有这方面的好解决方案?
感谢。
答案 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;
}
}