C# 使用 GDI+ 給圖片新增文字,並使文字自適應矩形區域
阿新 • • 發佈:2018-11-05
需求
需求是要做一個編輯文字的頁面。使用者在網頁端寫文字,文字區域是個矩形框,使用者可以通過下方的拖動條調節文字大小。
如下圖:
提交資料的時候前端傳文字區域的左上角和右下角定位給後臺。因為前端的字型大小單位與後端沒什麼關係,所以不能直接傳字型大小,也就是後端要根據矩形區域以及文字內容來自己推算用什麼樣的字型大小合適。
簡單說就是知道文字的矩形區域,以及文字內容,要讓文字內容根據矩形區域大小調整到適合的字型大小能比較合適地填滿這個區域。
分析&思路
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
包含 Width
和 Height
屬性,讀取這兩個屬性可以獲取到文字內容所佔的寬高(以畫素為單位)。
//
// 摘要:
// 獲取或設定此 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();
}
}
最終效果: