C# 使用 GDI+ 给图片添加文字,并使文字自适应矩形区域

PS:这篇文章是 GDI+ 总结系列的第二篇,如果对 GDI+ 的基础使用不熟悉的朋友可以先看第一篇文章《C# 使用 GDI+ 画图》


需求

需求是要做一个编辑文字的页面。用户在网页端写文字,文字区域是个矩形框,用户可以通过下方的拖动条调节文字大小。
如下图:

1.png

提交数据的时候前端传文字区域的左上角和右下角定位给后台。因为前端的字体大小单位与后端没什么关系,所以不能直接传字体大小,也就是后端要根据矩形区域以及文字内容来自己推算用什么样的字体大小合适。

简单说就是知道文字的矩形区域,以及文字内容,要让文字内容根据矩形区域大小调整到适合的字体大小能比较合适地填满这个区域。


分析&思路

Graphics 类有个 MeasureString 方法,可以用来计算以当前字体写出来的文字会占据多少像素。
如下:

//
// 摘要:
//     测量用指定的 System.Drawing.Font 绘制的指定字符串。
//
// 参数:
//   text:
//     要测量的字符串。
//
//   font:
//     System.Drawing.Font,它定义字符串的文本格式。
//
// 返回结果:
//     此方法返回 System.Drawing.SizeF 结构,该结构表示 text 参数指定的、使用 font 参数绘制的字符串的大小,单位由 System.Drawing.Graphics.PageUnit
//     属性指定。
//
// 异常:
//   T:System.ArgumentException:
//     font 为 null。
public SizeF MeasureString(string text, Font font);

这个方法返回的 SizeF 包含 WidthHeight 属性,读取这两个属性可以获取到文字内容所占的宽高(以像素为单位)。

//
// 摘要:
//     获取或设置此 System.Drawing.SizeF 结构的水平分量。
//
// 返回结果:
//     此 System.Drawing.SizeF 结构的水平分量,通常以像素为单位进行度量。
public float Width { get; set; }

// 摘要:
//     获取或设置此 System.Drawing.SizeF 结构的垂直分量。
//
// 返回结果:
//     此 System.Drawing.SizeF 结构的垂直分量,通常以像素为单位进行度量。
public float Height { get; set; }

于是我们可以先根据前端传过来的文字左上角与右下角定位,算出文字的矩形区域,然后估计一个字体大小,再用 MeasureString 方法计算出估算的文字所占区域,比较和实际的文字区域大小,大了则缩小字体,小了则增大字体。这样即可大约找出合适的文字大小。


具体实现

  • 添加文字方法
/// <summary>
/// 图片添加文字,文字大小自适应
/// </summary>
/// <param name="imgPath">图片路径</param>
/// <param name="locationLeftTop">左上角定位(x1,y1)</param>
/// <param name="locationRightBottom">右下角定位(x2,y2)</param>
/// <param name="text">文字内容</param>
/// <param name="fontName">字体名称</param>
/// <returns>添加文字后的Bitmap对象</returns>
public static Bitmap AddText(string imgPath, string locationLeftTop, string locationRightBottom, string text, string fontName = "华文行楷")
{
    Image img = Image.FromFile(imgPath);

    int width = img.Width;
    int height = img.Height;
    Bitmap bmp = new Bitmap(width, height);
    Graphics graph = Graphics.FromImage(bmp);

    // 计算文字区域
    // 左上角
    string[] location = locationLeftTop.Split(',');
    float x1 = float.Parse(location[0]);
    float y1 = float.Parse(location[1]);
    // 右下角
    location = locationRightBottom.Split(',');
    float x2 = float.Parse(location[0]);
    float y2 = float.Parse(location[1]);
    // 区域宽高
    float fontWidth = x2 - x1;
    float fontHeight = y2 - y1;

    float fontSize = fontHeight;  // 初次估计先用文字区域高度作为文字字体大小,后面再做调整,单位为px

    Font font = new Font(fontName, fontSize, GraphicsUnit.Pixel);
    SizeF sf = graph.MeasureString(text, font);

    int times = 0;

    // 调整字体大小以适应文字区域
    if (sf.Width > fontWidth)
    {
        while (sf.Width > fontWidth)
        {
            fontSize -= 0.1f;
            font = new Font(fontName, fontSize, GraphicsUnit.Pixel);
            sf = graph.MeasureString(text, font);

            times++;
        }

        Console.WriteLine("一开始估计大了,最终字体大小为{0},循环了{1}次", font.ToString(), times);
    }
    else if (sf.Width < fontWidth)
    {
        while (sf.Width < fontWidth)
        {
            fontSize += 0.1f;
            font = new Font(fontName, fontSize, GraphicsUnit.Pixel);
            sf = graph.MeasureString(text, font);

            times++;
        }

        Console.WriteLine("一开始估计小了,最终字体大小为{0},循环了{1}次", font.ToString(), times);
    }

    // 最终的得出的字体所占区域一般不会刚好等于实际区域
    // 所以根据两个区域的相差之处再把文字开始位置(左上角定位)稍微调整一下
    x1 += (fontWidth - sf.Width) / 2;
    y1 += (fontHeight - sf.Height) / 2;

    graph.DrawImage(img, 0, 0, width, height);
    graph.DrawString(text, font, new SolidBrush(Color.Black), x1, y1);

    graph.Dispose();
    img.Dispose();

    return bmp;
}
  • 测试调用
private static void Main(string[] args)
{
    try
    {
        DrawingEntity drawing = new DrawingEntity();

        Console.WriteLine("Start drawing ...");
        System.Drawing.Bitmap bmp = drawing.AddText(@"D:\test\39585148.png", "177.75,63.84", "674.73, 141.6", "大海啊,全是浪");
        bmp.Save(@"D:\test\output.png");
        bmp.Dispose();
        Console.WriteLine("Done!");
    }
    catch (System.Exception ex)
    {
        Console.WriteLine("出错了!!\n" + ex.ToString());
    }
    finally
    {
        System.Console.WriteLine("\nPress any key to continue ...");
        System.Console.ReadKey();
    }
}

最终效果:


2.png

系列其他文章:
C# 使用 GDI+ 画图
C# 使用 GDI+ 实现添加中心旋转(任意角度)的文字

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,387评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,845评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,091评论 0 246
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,308评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,662评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,795评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,008评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,743评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,466评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,687评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,181评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,531评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,177评论 3 239
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,126评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,862评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,734评论 2 274