Reputation: 2863
I'm trying to create two columns of left-aligned text with a variable-width font. My current process:
This feels logically sound to me, but the output looks like this (with a variable-width font):
a 12345678910
as 12345678910
asd 12345678910
asdf 12345678910
asdfg 12345678910
The output I'm looking for is this:
a 12345678910
as 12345678910
asd 12345678910
asdf 12345678910
asdfg 12345678910
What am I missing?
const int maxNameWidth = 150;
Font measurementFont;
Graphics graphics;
float spaceWidth;
void Main()
{
measurementFont = new Font("Arial", 14);
graphics = Graphics.FromImage(new Bitmap(1, 1));
spaceWidth = graphics.MeasureString(" ", measurementFont).Width;
addRow("a", "12345678910");
addRow("as", "12345678910");
addRow("asd", "12345678910");
addRow("asdf", "12345678910");
addRow("asdfg", "12345678910");
measurementFont.Dispose();
graphics.Dispose();
}
void addRow(string name, string value) {
float width = graphics.MeasureString(name, measurementFont).Width;
int amountOfSpacesNeeded = Convert.ToInt32((maxNameWidth - width) / spaceWidth);
Console.WriteLine(name + " ".PadRight(amountOfSpacesNeeded) + content); // The console font is Arial
}
Edit: Fixed dumb error with padding name
instead of actually multiplying the amount of spaces. The result is a lot better now, but still a bit off. I do notice that the width of five spaces is not equal to the spaceWidth * 5
though...
Upvotes: 3
Views: 1912
Reputation: 3034
Author has added his own solution while I was working on this code. Yet, though his code seems to do its work, it adds to the complexity of the solution. My code has a much simpler algorithm.
void addRows(string name, string value)
{
string teststring = name + "";
int spacesAdded = 0;
while (maxNameWidth > graphics.MeasureString(teststring + "x", measurementFont).Width)
{
spacesAdded++;
teststring += " ";
}
Console.WriteLine(":" + name + " ".PadRight(spacesAdded) + ":" + value + ":" + spacesAdded);
}
I used the "x" to get a similar result as his and it can be replaced by other characters to test with more strings to see if it works. I have tested the with only 2 extra strings that include very wide "ooooo" and very narrow "....." to get a satisfying result.
PS: It seems I previously thought differently while answering. Since I am also trying to learn new things and to find new approaches, I tried to get a result for this question. Anyways, this time I am on the right path
Upvotes: 1
Reputation: 2863
Finally got it. However I think that this solution is disgusting trash so I'll keep this question opened until someone has a nicer/less brute force method.
Basically, since the calculations get me 1-2 spaces off due to incalculable kerning (as far as I can google), I just measure the string again and add or subtract spaces from it until it reaches the opposite side of maxNameWidth
from its direction; eg. if it's too short then then add a space, measure again and see if that's over maxNameWidth
, if yes then stop, and vice versa.
Full code:
const int maxNameWidth = 150;
Font measurementFont;
Graphics graphics;
float spaceWidth, xWidth;
enum Direction { None, FromHigh, FromLow };
void Main()
{
measurementFont = new Font("Arial", 14);
graphics = Graphics.FromImage(new Bitmap(1, 1));
spaceWidth = graphics.MeasureString(" ", measurementFont).Width;
xWidth = graphics.MeasureString("x", measurementFont).Width;
addRow("a", "12345678910");
addRow("as", "12345678910");
addRow("asd", "12345678910");
addRow("asdf", "12345678910");
addRow("asdfg", "12345678910");
measurementFont.Dispose();
graphics.Dispose();
}
void addRow(string name, string value) {
float width = graphics.MeasureString(name, measurementFont).Width;
int amountOfSpacesNeeded = Convert.ToInt32((maxNameWidth - width) / spaceWidth);
string firstColumn = name + " ".PadRight(amountOfSpacesNeeded);
float currWidth;
Direction dir = Direction.None;
while (true) {
// I add an 'x' here because just measuring a bunch of spaces does not work (the width of one space is apparently equal to the width of five according to graphics)
currWidth = graphics.MeasureString(firstColumn + "x", measurementFont).Width - xWidth;
if (((dir == Direction.FromLow) || (dir == Direction.None)) && (currWidth < maxNameWidth)) {
dir = Direction.FromLow;
firstColumn += " ";
}
else if (((dir == Direction.FromHigh) || (dir == Direction.None)) && (currWidth > maxNameWidth)) {
dir = Direction.FromHigh;
firstColumn = firstColumn.Remove(firstColumn.Length - 1);
}
else {
break;
}
}
Console.WriteLine(firstColumn + value);
}
Output (paste somewhere with 14pt Arial to see the alignment):
a 12345678910
as 12345678910
asd 12345678910
asdf 12345678910
asdfg 12345678910
Upvotes: 2