1.简介
因为项目需要,需要在项目里生成二维码,且中间带logo。最开始是用的QRCoder,在Windows里面运行没问题,换到Linux、MAC OS里面就直接报错。为了解决该问题,探索了很多方法。本文详细记录了该过程。
2.QRCoder
在网上寻找的方法,是采用QRCoder来绘制二维码。该方法百度即可。注意先Nuget引用该库。下面是主要代码:
函数主体:
/// <summary>
/// 生成二维码
/// </summary>
/// <param name="msg">信息</param>
/// <param name="version">版本 1 ~ 40</param>
/// <param name="pixel">像素点大小</param>
/// <param name="icon_path">图标路径</param>
/// <param name="icon_size">图标尺寸</param>
/// <param name="icon_border">图标边框厚度</param>
/// <param name="white_edge">二维码白边</param>
/// <returns>位图</returns>
public static Bitmap CreateQRCode(string msg, int version, int pixel, string icon_path, int icon_size, int icon_border, bool white_edge)
{
QRCoder.QRCodeGenerator qRCodeGenerator = new QRCoder.QRCodeGenerator();
QRCoder.QRCodeData qRCodeData = qRCodeGenerator.CreateQrCode(msg, QRCoder.QRCodeGenerator.ECCLevel.M, true, true, QRCoder.QRCodeGenerator.EciMode.Utf8, version);
QRCoder.QRCode qRCode = new QRCoder.QRCode(qRCodeData);
Bitmap icon = new Bitmap(icon_path);
Bitmap bmp = qRCode.GetGraphic(pixel, Color.Black, Color.White, icon, icon_size, icon_border, white_edge);
return bmp;
}
调用过程:
string icon_path = "logo.ico";
Bitmap bmp = QRCodeHelper.CreateQRCode("你好啊", 5, 20, icon_path, 20, 5, true);
bmp.Save("qq.png");
程序运行后,在WIndows环境成功运行,且能生成二维码。
发布程序到Centos后,发现程序报错了,报错如下所示:
Unhandled exception. System.TypeInitializationException: The type initializer for 'Gdip' threw an exception.
---> System.DllNotFoundException: Unable to load shared library 'libgdiplus' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibgdiplus: cannot open shared object file: No such file or directory
at System.Drawing.SafeNativeMethods.Gdip.GdiplusStartup(IntPtr& token, StartupInput& input, StartupOutput& output)
at System.Drawing.SafeNativeMethods.Gdip..cctor()
--- End of inner exception stack trace ---
at System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(String filename, IntPtr& bitmap)
at System.Drawing.Bitmap..ctor(String filename, Boolean useIcm)
at System.Drawing.Bitmap..ctor(String filename)
at QrCode.Encoder.CreateQRCode(String msg, Int32 version, Int32 pixel, String icon_path, Int32 icon_size, Int32 icon_border, Boolean white_edge) in E:\VS_project\QrCode\QrCode\Program.cs:line 40
at QrCode.Program.Main(String[] args) in E:\VS_project\QrCode\QrCode\Program.cs:line 12
Aborted (core dumped)
经过百度发现原因是,在二维码绘制中使用 System.Draw.Common类库,而该类库如果想正常使用,需要安装插件,语句如下:
yum install autoconf automake libtool
yum install freetype-devel fontconfig libXft-devel
yum install libjpeg-turbo-devel libpng-devel giflib-devel libtiff-devel libexif-devel
yum install glib2-devel cairo-devel
git clone https://github.com/mono/libgdiplus
cd libgdiplus
./autogen.sh
make
make install
创建符号链接:
ln -s /usr/local/lib/libgdiplus.so /usr/lib64/libgdiplus.so
ln -s /usr/local/lib/libgdiplus.so /usr/libgdiplus.so
这样的办法确实是可以解决问题,但是放在商业性的项目产品就不行了。不会让你手工部署这些环境的,考虑到上述情况后。需要寻找其他方法。经过测试后, 发现是可以跨平台生成二维码,在二维码和logo叠加过程GetGraphic出现了问题,去github下载了源代码,重点了看了下QRCode类里面GetGraphic方法,发现可以重写该方法,使用开源类库重写该方法就行。
下面是主要代码:
public IBitmap GetGraphic(int pixelsPerModule = 8, IBitmap bitmap = null, bool drawQuietZones = false)
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
var offset = drawQuietZones ? 0 : 4 * pixelsPerModule;
SKPaint lightPaint = new SKPaint();
lightPaint.Color = new SKColor(255, 255, 255);
SKPaint darkPaint = new SKPaint();
darkPaint.Color = new SKColor(0, 0, 0);
WriteableBitmap renderTarget = new WriteableBitmap(new PixelSize(size, size), new Vector(96, 96), PixelFormat.Rgba8888);
Stream stream;
using (var lockedBitmap = renderTarget.Lock())
{
SKImageInfo info = new SKImageInfo(lockedBitmap.Size.Width, lockedBitmap.Size.Height, lockedBitmap.Format.ToSkColorType());
SKSurface currentSurface = SKSurface.Create(info, lockedBitmap.Address, lockedBitmap.RowBytes);
SKCanvas canvas = currentSurface.Canvas;
canvas.Clear(new SKColor(255, 255, 255));
for (var x = 0; x < size + offset; x = x + pixelsPerModule)
{
for (var y = 0; y < size + offset; y = y + pixelsPerModule)
{
var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1];
if (module)
{
canvas.DrawRect(x - offset, y - offset, pixelsPerModule, pixelsPerModule, darkPaint);
}
else
{
canvas.DrawRect(x - offset, y - offset, pixelsPerModule, pixelsPerModule, lightPaint);
}
}
}
if (bitmap != null)
{
using (Stream icon = new MemoryStream())
{
bitmap.Save(icon);
icon.Seek(0, SeekOrigin.Begin);
SKData data = SKData.Create(icon);
SKImage img = SKImage.FromEncodedData(data);
int position = (int)(size - bitmap.Size.Width) / 2;
canvas.DrawImage(img, new SKPoint(position, position));
}
}
stream = SKImage.FromPixels(info, lockedBitmap.Address, lockedBitmap.RowBytes)
.Encode(SKEncodedImageFormat.Png, 100)
.AsStream();
}
renderTarget.Dispose();
IBitmap qrCode = new Bitmap(stream);
stream.Close();
stream.Dispose();
return qrCode;
}
这样就解决了问题。程序可以跨平台运行,并生成二维码。
该问题的解决,让我发现当找不到更好的方法时,可以换个思路,尝试去看看源码。一直是应用,但是也可以在别人的基础上修改创新下。本文是针对具体应用下的问题解决,为了方便大家更好的参考,我会把源码上传,后续审核通过了,我会放地址。当然直接搜索我的id,也是可以找到的。