从绘制在图像上的文本中删除顶部和底部填充

Posted

技术标签:

【中文标题】从绘制在图像上的文本中删除顶部和底部填充【英文标题】:Remove top and bottom padding from Text drawn on an Image 【发布时间】:2019-01-26 16:17:01 【问题描述】:

我正在从指定的文本生成图像,但我遇到了一个问题:我无法删除我生成的图像内绘制的文本的顶部和底部填充。

我尝试在使用 Graphics.DrawString() 时更改字符串格式,但我只设法删除了左右填充。

private void button1_Click(object sender, EventArgs e)

    Font font = new Font("Arial", 52, FontStyle.Regular);
    Image i = GetTextAsImage(textBox1.Text,400, font, Color.Black, Color.LightGray);
    i.Save("myImage.jpeg", ImageFormat.Jpeg);


private Image GetTextAsImage(String text, int widthInPixel, Font textFont, Color textColor, Color backColor)

    //first, create a dummy bitmap just to get a graphics object
    Image img = new Bitmap(1, 1);
    Graphics drawing = Graphics.FromImage(img);

    //measure the string to see how big the image needs to be
    SizeF textSize = drawing.MeasureString(text, textFont);

    //free up the dummy image and old graphics object
    img.Dispose();
    drawing.Dispose();

    //create a new image of the right size
    img = new Bitmap((int)textSize.Width, textFont.Height);

    drawing = Graphics.FromImage(img);
    drawing.SmoothingMode = SmoothingMode.AntiAlias;
    drawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    //paint the background
    drawing.Clear(backColor);
    //create a brush for the text
    Brush textBrush = new SolidBrush(textColor);
    drawing.DrawString(text, textFont, textBrush, 0, 0,StringFormat.GenericTypographic);

    drawing.Save();
    textBrush.Dispose();
    drawing.Dispose();
    return img;

这是我得到的输出:

这是预期的输出:

【问题讨论】:

试试TextRenderer.DrawText。有了这个你可以指定TextFormatFlags.NoPadding 您是否要删除文本上方和下方未使用的行的填充或 所有 行,即如果文本仅包含“短”字符而没有下降(“acemnorsuvwxz” ) 你想要一个非常短的图像吗? TextRenderer.DrawText / MeasureText 似乎比Graphics.DrawString / MeasureString 更受欢迎。根据文章The wonders of text rendering and GDI,它提供了更好更快的结果。它还解释了如何删除填充。 【参考方案1】:

我建议您使用一种稍微不同的方法,使用GraphicsPath 类来测量和绘制位图对象上的文本。

优点是GraphicsPath 类报告它所引用的对象将被绘制的实际坐标,以及与特定字体相关的文本大小。 这些度量值使用GraphicsPath.GetBounds() 方法以RectagleF 结构返回。 基本构造函数假定 Pen 大小为 1 像素。

只有一个(小)细节需要注意:GDI+ 位图对象仅接受以整数值表示的尺寸,而所有其他度量均以浮点值表示。 我们需要补偿舍入,但通常只有 ± 1 个像素。

结果示例:

过程说明:

定义字体系列和大小 将文本字符串添加到GraphicsPath 对象 获取文本对象的GraphicsPath边界矩形 使用边界矩形大小构建位图对象 将带有Graphics.TranslateTransform 的世界坐标移动到由边界矩形Y 位置和笔大小定义的坐标,使用它们的负值:我们需要向后移动 那个衡量标准。 绘制文本

另请参阅有关 GraphicsPath 和字体的这些说明:Properly draw text using Graphics Path

示例代码:

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;


string text = "This is my Text";
Font font = new Font("Arial", 52, FontStyle.Regular, GraphicsUnit.Point);
float penSize = 1f;

using (var path = new GraphicsPath()) 
    path.AddString(text, font.FontFamily, (int)font.Style, font.Size, Point.Empty, StringFormat.GenericTypographic);

    RectangleF textBounds = path.GetBounds();

    using (var bitmap = new Bitmap((int)textBounds.Width, (int)textBounds.Height, PixelFormat.Format32bppArgb))
    using (var g = Graphics.FromImage(bitmap)) 
    using (var brush = new SolidBrush(Color.LightGreen)) 
        g.SmoothingMode = SmoothingMode.AntiAlias;
        // When rendering without a GraphicsPath object
        //g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
        g.Clear(Color.Black);
        g.TranslateTransform(-(textBounds.X + penSize), -(textBounds.Y + penSize));
        g.FillPath(brush, path);
        
        bitmap.Save("[Image Path]", ImageFormat.Png);
        // Or: return (Bitmap)bitmap.Clone();
        // Or: return bitmap; declaring the bitmap without a using statement
    

【讨论】:

这对我来说非常接近完美。但是尝试使用单个字符,例如 A。我看到左下角可能被裁剪了大约 4 个像素。但是然后尝试使用数字 1。它在这方面非常失败。和我一样。谁能明白为什么会这样? @Craig 单个字符更难测量(通常需要至少两个):path.GetBounds() 可能返回不同的测量值(没有正确的填充)。我去看看 @Craig 我稍微改变了转换方法,所以无论如何它都应该忽略绘图框的填充边界。如果您发现其他故障,请告诉我(注意这是一种简化的计算方法,Font 对象应更加小心处理)。有关此事的更多信息,请参阅Properly draw text using Graphics Path。 太棒了!我会玩它,但看起来你是个高手!

以上是关于从绘制在图像上的文本中删除顶部和底部填充的主要内容,如果未能解决你的问题,请参考以下文章

如何删除 textView 上的顶部和底部填充?

如何删除 Android Checkbox 的内部图像填充(顶部、左侧和底部)

如何从 Mac OS 中的 SwiftUI 列表中删除底部/顶部项目填充

使用swift 2仅使用顶部和底部边框的文本字段

如何删除填充顶部和底部以设置高度和宽度

如何在移动屏幕上的文本顶部对齐右侧图像