【问题标题】:System.Drawing.Font - change Letter Spacing?System.Drawing.Font - 更改字母间距?
【发布时间】:2011-12-11 15:32:23
【问题描述】:

使用System.Drawing.Font,有没有办法改变字母间距,就像你可以改变字体大小一样?

我希望增加字母间距以适应特定宽度。

如果这是不可能的,是否有替代方案来获得预期的效果?例如,有没有一种简单的方法可以让多个图形适合我的特定宽度?

【问题讨论】:

  • 你在哪里使用这个字体?有什么用?
  • 它在 GDI+ 图像中使用,Graphics.DrawString()
  • System.Drawing 中没有内置机制来绘制调整了字母间距的文本。您将不得不为此滚动自己的代码。首先查看MeasureCharacterRanges

标签: .net fonts drawing system.drawing


【解决方案1】:

我不认为 DrawString 将允许您指定任何详细的字符间距,但我会创建一个辅助函数,根据所需的宽度计算间距,然后绘制每个字符以适合。

试试这个,看看你的表现如何......

    public void DrawSpacedText(Graphics g, Font font, Brush brush, PointF point, string text, int desiredWidth)
    {
        //Calculate spacing
        float widthNeeded = 0;
        foreach (char c in text)
        {
            widthNeeded += g.MeasureString(c.ToString(), font).Width;
        }
        float spacing = (desiredWidth - widthNeeded) / (text.Length - 1);

        //draw text
        float indent = 0;
        foreach (char c in text)
        {
            g.DrawString(c.ToString(), font, brush, new PointF(point.X + indent, point.Y));
            indent += g.MeasureString(c.ToString(), font).Width + spacing;
        }
    }

您可能可以优化它,使每个字符只调用一次 MeasureString。或者甚至使用 MeasureCharacterRanges 来获取一个数组(我相信无论如何它更准确)

编辑:这是一个使用 MeasureCharacterRanges 代替的示例...

    public void DrawSpacedText(Graphics g, Font font, Brush brush, PointF point, string text, int desiredWidth)
    {
        //Calculate spacing
        float widthNeeded = 0;
        Region[] regions = g.MeasureCharacterRanges(text, font, new RectangleF(point, new SizeF(desiredWidth, font.Height + 10)), StringFormat.GenericDefault);
        float[] widths = new float[regions.Length];
        for(int i = 0; i < widths.Length; i++)
        {
            widths[i] = regions[i].GetBounds(g).Width;
            widthNeeded += widths[i];
        }
        float spacing = (desiredWidth - widthNeeded) / (text.Length - 1);

        //draw text
        float indent = 0;
        int index = 0;
        foreach (char c in text)
        {
            g.DrawString(c.ToString(), font, brush, new PointF(point.X + indent, point.Y));
            indent += widths[index] + spacing;
            index++;
        }
    }

【讨论】:

  • +1 这是一个不错的方法。但是,固定间距会搞砸text kerning,这很难正确实现。换句话说,引用 Wikipedia 的话,结果不会像使用适当的字距调整那样视觉上令人愉悦
  • 假设一个字符 === 字形,这有点幼稚。没有比迪烟,没有结扎等等。只是 ASCII 表的下半部分。
  • @olegz 如果唯一的目的是测量字符串而不是绘制它,这就足够了吗?
  • @pelican_george,答案是肯定的,除非字符串包含 CJK 字符、变音符号或者您希望代码对 i18n 友好
  • 您将如何处理它以使其对 i18n 友好?
【解决方案2】:

此代码支持在文本中换行并返回一个 Image 对象。

public static Image ConvertTextToImage(String text, Font font, Color textColor, int spacing = 0)
{
    var textParts = SplitOnBreakLines(text);
    // dummy image, just create drawing
    var img = new Bitmap(1, 1);
    var drawing = Graphics.FromImage(img);
    // calculate width, height
    var width = 0.0F;
    foreach (char c in text)
    {
        width += drawing.MeasureString(c.ToString(), font).Width;
    }           
    width = (width + spacing * (ClearBreakLines(text).Length - 1))  / textParts.Length;
    if (width <= 0)
        width = 1;
    var height = font.Height * textParts.Length;
    // clear and create new objects
    img.Dispose();
    drawing.Dispose();

    img = new Bitmap((int)width, (int)height);
    drawing = Graphics.FromImage(img);

    //Adjust for high quality
    drawing.CompositingQuality = CompositingQuality.HighQuality;
    drawing.InterpolationMode = InterpolationMode.HighQualityBilinear;
    drawing.PixelOffsetMode = PixelOffsetMode.HighQuality;
    drawing.SmoothingMode = SmoothingMode.HighQuality;
    drawing.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

    //paint the background
    drawing.Clear(Color.Transparent);

    //create a brush for the text
    var textBrush = new SolidBrush(textColor);
    //write text
    var indent = 0.0F;
    var point = new Point();            
    foreach (var textPart in textParts)
    {
        foreach (char c in textPart)
        {
            drawing.DrawString(c.ToString(), font, textBrush, new PointF(point.X + indent, point.Y));
            indent += drawing.MeasureString(c.ToString(), font).Width + spacing;
        }
        indent = 0.0F;
        point.Y += font.Height;
    }
    drawing.Save();
    textBrush.Dispose();
    drawing.Dispose();
    return img;           
}

public static string ClearBreakLines(string s)
{
    return Regex.Replace(s, @"\r\n?|\n", string.Empty);
}

public static string[] SplitOnBreakLines(string s)
{
    return Regex.Split(s, @"\r\n?|\n");           
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 2011-07-05
    • 2016-10-07
    相关资源
    最近更新 更多