【问题标题】:How to vertically align non-monospaced text如何垂直对齐非等宽文本
【发布时间】:2018-04-26 01:39:18
【问题描述】:

我正在尝试使用可变宽度字体创建两列左对齐文本。我目前的流程:

  1. 测量将进入第一列的字符串
  2. 从所述第一列的最大宽度中减去其宽度以获得差值
  3. 将其除以单个空格的宽度以获得所需的空格数
  4. 用必要数量的空格右填充结果

这在我看来合乎逻辑,但输出看起来像这样(使用可变宽度字体):

a                    12345678910
as                  12345678910
asd                12345678910
asdf               12345678910
asdfg             12345678910

我正在寻找的输出是这样的:

a          12345678910
as         12345678910
asd        12345678910
asdf       12345678910
asdfg      12345678910

我错过了什么?

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
}

编辑:修复了使用填充 name 而不是实际乘以空格数量的愚蠢错误。结果现在好多了,但还是差了一点。我确实注意到五个空格的宽度不等于 spaceWidth * 5 虽然...

【问题讨论】:

  • 你想要什么输出?
  • @GaurangDave 编辑显示它。
  • 你能不能把“Console.WriteLine()”命令放在行之间,并在做某事之前和之后显示每个计算的值。检查数字,看看他们是否在做正确的事情。或者您也可以使用调试工具,但这是最简单的方法。如果您找不到解决方案,我(或其他任何人)可能会稍后回来查找问题。如果您在我们这样做之前找到了答案,请也发布答案。
  • 这很奇怪。你真的在控制台上使用 Ariel 吗?无论如何,这种方法不适用于可变宽度字体,因为通常列之间所需的空间不会是空格字符宽度的偶数倍。正确的方法是在精确的像素偏移处绘制每个单独的列字符串。
  • @glenebob 是的,我正在使用 LinqPad 运行它,它允许我更改控制台输出的字体系列和大小。你提到的间距确实是真的——而且没有办法估计并将它也包括在计算中,是吗?不幸的是,我无法在使用此代码的地方使用实际绘图(纯文本环境),但如果下次可用,我会记住这一点,谢谢!

标签: c# .net


【解决方案1】:

终于明白了。但是我认为这个解决方案是令人作呕的垃圾,所以我会一直打开这个问题,直到有人有更好/更少的蛮力方法。

基本上,由于无法计算的字距调整(据我所知),计算使我减少了 1-2 个空格,我只需再次测量字符串并从中添加或减去空格,直到它到达 @987654321 的另一侧@从它的方向;例如。如果太短则添加一个空格,再次测量并查看是否超过maxNameWidth,如果是则停止,反之亦然。

完整代码:

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);

}

输出(用 14pt Arial 粘贴到某处以查看对齐方式):

a                     12345678910
as                   12345678910
asd                 12345678910
asdf                12345678910
asdfg              12345678910

【讨论】:

  • 在尝试失败后,我正在研究解决方案。请也阅读。
  • 我正要接受你的回答,但它确实有一个你错过的错误。 “x”的宽度计算不正确(xwidth)。记住 5*"x" 不等于 "xxxxx",这是因为字符周围有多余的像素。与我的解决方案相比,由于此错误,您的解决方案增加了 3 个空格。这对小项目并不重要,但对大项目可能会产生很大影响。
【解决方案2】:

在我处理此代码时,作者添加了他自己的解决方案。然而,尽管他的代码似乎可以发挥作用,但它增加了解决方案的复杂性。我的代码有一个更简单的算法。

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);
}

我用“x”得到了和他类似的结果,可以用其他字符替换它来测试更多的字符串,看看它是否有效。我只测试了 2 个额外的字符串,包括非常宽的“ooooo”和非常窄的“.....”,以获得令人满意的结果。


PS:看来我之前在回答时想法不同。由于我也在尝试学习新事物并寻找新方法,因此我试图得到这个问题的结果。不管怎样,这次我是在正确的道路上

【讨论】:

  • 谢谢,这确实使代码更简洁!可以进一步压缩它,只需将空格直接添加到 name 并完全跳过 PadRight 调用。我猜我们自己为几个字符串连接空格可能不是太低效(不知道PadRight是如何实现的)?
  • 感谢您接受作为答案。我对您的回答发表了评论,即单个字符存在宽度错误。后来我注意到我的也有类似的错误。如果您尝试写插入的总空间,我的“a”将为 24,而您的为 27。这是我们比较宽度的方式造成的。您能否尝试修复这两个代码并提供对答案的编辑?
猜你喜欢
  • 2011-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多