前言
之前寫過一篇 Linux/Docker 中使用 System.Drawing.Common 踩坑小計, 當時主要是有一塊影像處理的需要從 .net framework
遷移到 .net core
上,使用這個方案,基本可以說是無縫遷移。但是最近發現了兩個問題:
- 構建 Docker 時需安裝
libgdiplus
- 多行文字輸出,在
Windows
和Linux
下表現不一致
針對以上問題,想著嘗試一下其他解決方案試試,後來遇見它:SixLabors.ImageSharp。下面通過實戰來了解一下它的具體使用。
合併圖片
合併圖片即將一張小圖片放到一張大圖片上,準備素材如下:
模板圖 (basic.bmp) :
電子簽名圖 (test.png):
合併操作程式碼:
/// <summary>
/// 合併圖片
/// </summary>
/// <param name="templateImage"></param>
/// <param name="mergeImagePath">合併圖片</param>
/// <param name="x">X座標</param>
/// <param name="y">y座標</param>
/// <returns></returns>
public static Image MergeImage(Image templateImage, string mergeImagePath, int x, int y)
{
// 載入需要合併的圖片
var mergeImage = Image.Load(mergeImagePath);
templateImage.Mutate(o =>
{
o.DrawImage(mergeImage, new Point(x, y), 1);
});
return templateImage;
}
使用:
static void Main(string[] args)
{
var templatePath = "./basic.bmp";
var mergeImagePath = "./test.png";
var outputImage = Image.Load(templatePath);
outputImage = MergeImage(outputImage, mergeImagePath, 700, 1825);
outputImage.Save("result.png");
}
效果圖 (result.png):
圖片縮放
經過上一步圖片合併,我們可以發現電子簽名的圖片有些大了,我們需要縮放一下。
改進 MergeImage 程式碼:
/// <summary>
/// 合併圖片
/// </summary>
/// <param name="templateImage"></param>
/// <param name="mergeImagePath">合併圖片</param>
/// <param name="x">X座標</param>
/// <param name="y">y座標</param>
/// <param name="width">寬度</param>
/// <param name="height">高度</param>
/// <returns></returns>
public static Image MergeImage(Image templateImage, string mergeImagePath, int x, int y, int width, int height)
{
var mergeImage = Image.Load(mergeImagePath);
mergeImage.Mutate(m =>
{
m.Resize(new Size(width, height));
});
templateImage.Mutate(o =>
{
o.DrawImage(mergeImage, new Point(x, y), 1);
});
return templateImage;
}
呼叫和之前一樣,但是需要提供你希望縮放圖片寬高即可。
效果圖 (result.png):
輸出多行文字
/// <summary>
/// 多行文字輸出
/// </summary>
/// <param name="templateImage"></param>
/// <param name="text"></param>
/// <param name="fontSize"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="wrapTextWidth">文字框長度</param>
/// <returns></returns>
public static Image AddMultilineText(Image templateImage, string text, int fontSize, int x, int y, int wrapTextWidth)
{
var fonts = new FontCollection();
var fontFamily = fonts.Install("./simhei.ttf");
var font = new Font(fontFamily, fontSize, FontStyle.Bold);
var textOptions = new TextOptions()
{
ApplyKerning = true,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
WrapTextWidth = wrapTextWidth
};
var graphicsOptions = new GraphicsOptions()
{
Antialias = true
};
var options = new TextGraphicsOptions(graphicsOptions, textOptions);
templateImage.Mutate(o =>
{
o.DrawText(options, text, font, Color.Black, new PointF(x, y));
});
return templateImage;
}
呼叫方式:
static void Main(string[] args)
{
var templatePath = "./basic.bmp";
var mergeImagePath = "./test.png";
var text =
"兩側胸廓對稱;兩側肺野透光度正常,未見異常密度增高影;兩肺紋理略增粗。兩肺門無增大、增濃;心影大小、形態如常;縱隔居中,兩膈面光整,肋膈角清晰銳利。 兩肺門無增大、增濃;心影大小、形態如常;縱隔居中,兩膈面光整,肋膈角清晰銳利。";
var outputImage = Image.Load(templatePath);
outputImage = MergeImage(outputImage, mergeImagePath, 700, 1825, 195, 53);
outputImage = AddMultilineText(outputImage, text, 35, 143, 1050, 1100);
outputImage.Save("20201123-result-3.png");
}
效果圖:
以上可以看到,文字並沒有達到我想要的換行效果,嘗試過調整 wrapTextWidth
的值,結果都是失敗。後來發現可以自己手動給文字新增\r\n
換行符,來讓文字輸出的時候自動換行。所以自己整了一個給文字新增的換行符的方法。
CutText:
/// <summary>
/// 自動換行字串
/// </summary>
/// <param name="str">文字字串</param>
/// <param name="len">每行的長度,多於這個長度自動換行</param>
/// <returns></returns>
public static string CutText(string str, int len)
{
var result = string.Empty;
var strLen = Encoding.Default.GetByteCount(str);
for (var i = 0; i < strLen; i++)
{
var r = i % len;
var last = (str.Length / len) * len;
if (i != 0 && i <= last)
{
if (r == 0)
{
result += str.Substring(i - len, len) + "\r\n";
}
}
else if (i > last)
{
result += str.Substring(i - 1);
break;
}
}
return result;
}
改進一下 AddMultilineText 方法:
/// <summary>
/// 多行文字輸出
/// </summary>
/// <param name="templateImage"></param>
/// <param name="text"></param>
/// <param name="fontSize"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="wrapTextWidth">文字框長度</param>
/// <returns></returns>
public static Image AddMultilineText(Image templateImage, string text, int fontSize, int x, int y, int wrapTextWidth)
{
var fonts = new FontCollection();
var fontFamily = fonts.Install("./simhei.ttf");
var font = new Font(fontFamily, fontSize, FontStyle.Bold);
var textOptions = new TextOptions()
{
ApplyKerning = true,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
WrapTextWidth = wrapTextWidth
};
var graphicsOptions = new GraphicsOptions()
{
Antialias = true
};
var options = new TextGraphicsOptions(graphicsOptions, textOptions);
templateImage.Mutate(o =>
{
var result = CutText(text, wrapTextWidth / fontSize);
o.DrawText(options, result, font, Color.Black, new PointF(x, y));
});
return templateImage;
}
執行檢視效果圖:
小結
目前使用 SixLabors.ImageSharp
重寫 System.Drawing.Common
實現的程式碼,效果還不錯,API
介面也挺簡潔的,也不需要在釋出到 Docker 的時候安裝額外的元件依賴,總的來說還是不錯。