///
/// Class that can automatically wrap a given string.
///
public class StringWrap {
///
/// List of characters to break a string around. A char key points to an
/// int weight. Higher weights indicate a higher preference to break at
/// that character.
///
public readonly Hashtable Breakable = new Hashtable();
///
/// Class that can automatically wrap a given string.
///
public StringWrap() {
Breakable[' '] = 10;
Breakable[','] = 20;
Breakable[';'] = 30;
Breakable['.'] = 40;
}
///
/// Cleanse the given text from duplicate (two or more in a row) characters, as
/// specified by Breakable. Will also remove all existing newlines.
///
/// Text to be cleansed.
/// Cleansed text.
protected string Cleanse(string text) {
text = text.Replace('\n', ' ');
string find, replace;
foreach(char c in Breakable.Keys) {
find = new string(c, 2);
replace = new string(c, 1);
while(text.IndexOf(find) != -1)
text = text.Replace(find, replace);
}
return text;
}
///
/// Perform an automatic wrap given text, a target ratio, and a font ratio.
///
/// Text to automatically wrap.
/// Ratio (Height / Width) of the target area
/// for this text.
/// Average ratio (Height / Width) of a single
/// character.
/// Automatically wrapped text.
public string PerformWrap(string text, float targetRatio, float fontRatio) {
string wrap = "";
text = Cleanse(text);
int rows = (int) Math.Sqrt(targetRatio * text.Length / fontRatio),
cols = text.Length / rows,
start = cols, index = 0, last;
for(int i = 0; i < rows - 1; i++) {
last = index;
index = BestBreak(text, start, cols * 2);
wrap += text.Substring(last, index - last).Trim() + "\n";
start = index + cols;
}
wrap += text.Substring(index);
return wrap;
}
///
/// Find the best place to break text given a starting index and a search radius.
///
/// Full text to search through.
/// Index in string to start searching at. Will be used
/// as the center of the radius.
/// Radius (in characters) to search around the given
/// starting index.
/// Optimal index to break the text at.
protected int BestBreak(string text, int start, int radius) {
int bestIndex = start;
float bestWeight = 0, examWeight;
radius = Math.Min(Math.Min(start + radius, text.Length - 1) - start,
start - Math.Max(start - radius, 0));
for(int i = start - radius; i <= start + radius; i++) {
object o = Breakable[text[i]];
if(o == null) continue;
examWeight = (int) o / (float) Math.Abs(start - i);
if(examWeight > bestWeight) {
bestIndex = i;
bestWeight = examWeight;
}
}
return Math.Min(bestIndex + 1, text.Length - 1);
}
}