使用 C# 生成 EMF 矢量图形文件 您所在的位置:网站首页 怎么生成emf格式的图片 使用 C# 生成 EMF 矢量图形文件

使用 C# 生成 EMF 矢量图形文件

2024-07-10 23:24| 来源: 网络整理| 查看: 265

前言

公众号上有网友询问我如何生成 EMF 文件的问题:

5f7d0227587f40e0211d2ae91a399855.png

本以为非常简单,我快速给出了解决方案:

var bitmap = new Bitmap(640, 480); var g = Graphics.FromImage(bitmap); g.DrawString("My IO", new Font(FontFamily.GenericSerif, 10), Brushes.Blue, 100, 100); bitmap.Save("MyIO.emf",ImageFormat.Emf);

结果,网友告诉我,这是错误的:

1a5abdbc2eb63346bb3b133889e7f0a6.png

用编辑器查看文件内容,发现实际生成的是PNG格式文件:

914587265c6a14bcfed28d8f30272f0a.png

这是怎么回事呢?

原因

在官方文档上找到这样一段话:

当使用Save此方法将图形图像另存为Windows元文件格式 (WMF) 或增强的图元文件格式 (EMF) 文件时,生成的文件将改为保存为可移植网络图形 (PNG) 文件。发生此行为是因为.NET Framework的GDI+组件没有可用于将文件另存为 .wmf 或 .emf 文件的编码器。

不理解这样设计的原因,不支持应该抛出异常吧?!

实现

不过还好,从文档上我们也找到了解决方案,那就是使用Metafile类。

可是在实现时,又踩了不少坑。

创建实例失败

按照示例代码,使用文件名创建实例:

var metafile = new Metafile("MyIO.emf");

结果报了个通用异常,完全没有指导意义:

08d01fe7f9064062e93b75fbe5006fb4.png

只好反编译代码查错。

发现,底层实现使用的GdipCreateMetafileFromFileAPI:

public Metafile(string filename) {  Path.GetFullPath(filename);  SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipCreateMetafileFromFile(filename, out IntPtr metafile));  SetNativeImage(metafile); }

342592d83967975baf933a919caaa446.png

也就是说,参数必须是一个已存在的 EMF 文件名。

查看其他构造函数的实现,发现传递referenceHdc的构造函数使用的是GdipRecordMetafileFileNameAPI:

public Metafile(string fileName, IntPtr referenceHdc, EmfType type, string? description) {  Path.GetFullPath(fileName);  SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipRecordMetafileFileName(fileName, referenceHdc, type, IntPtr.Zero, MetafileFrameUnit.GdiCompatible, description, out IntPtr metafile));  SetNativeImage(metafile); }

6dc170c1a7e34306a22b853c0b657844.png

也就是说,这个 API 可以创建 EMF 文件。看来可以用。

而referenceHdc可以使用Graphics.GetHdc()得到。

于是,实现代码如下:

using (Graphics g1 = Graphics.FromHwnd(IntPtr.Zero)) {     using (var metafile = new Metafile("MyIO.emf", g1.GetHdc()))     {         using (Graphics g2 = Graphics.FromImage(metafile))         {              g2.DrawString("My IO", new Font(FontFamily.GenericSerif, 10), Brushes.Blue, 100, 100);             g2.DrawString("My IO", new Font(FontFamily.GenericSerif, 10), Brushes.Blue, 200, 200);         }     } }

生成的确实是矢量图形文件:

f0e5db2222bd6e1c2a280f4a85d4245a.gif

绘制位置错误

可以明显看到,第一个My IO绘制的位置是错误的,绘制到了左上角,而不是(100, 100)。

再次查找构造函数,发现可以传递Rectangle参数:

6bbcf694086bddc19956a95768c11e6c.png

修改实现代码如下:

using (Graphics g1 = Graphics.FromHwnd(IntPtr.Zero)) {     using (var metafile = new Metafile("MyIO.emf", g1.GetHdc(), new Rectangle(0, 0, 300, 300), MetafileFrameUnit.Pixel))     {         using (Graphics g2 = Graphics.FromImage(metafile))         {             g2.DrawString("My IO", new Font(FontFamily.GenericSerif, 10), Brushes.Blue, 100, 100);             g2.DrawString("My IO", new Font(FontFamily.GenericSerif, 10), Brushes.Blue, 200, 200);         }     } }

这次总算成功了:

0e41c28927ae4b0198a4e8f8478bc985.gif

结论

后来发现,生成的图片实际是375 x 375像素,这应该是因为我的显示属性设置了缩放的原因(375 / 300 = 1.25):

10d195d85c791184fbe659e73e15ee43.png

想了解更多内容,请关注我的个人公众号”My IO“



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有