C#生成EMF矢量图形文件示例详解

作者:MyIO 时间:2022-10-30 02:12:56 

前言

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

C#生成EMF矢量图形文件示例详解

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

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);

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

C#生成EMF矢量图形文件示例详解

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

C#生成EMF矢量图形文件示例详解

这是怎么回事呢?

原因

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

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

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

实现

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

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

创建实例失败

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

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

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

C#生成EMF矢量图形文件示例详解

只好反编译代码查错。

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

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

C#生成EMF矢量图形文件示例详解

也就是说,参数必须是一个已存在的 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);
}

C#生成EMF矢量图形文件示例详解

也就是说,这个 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);
       }
   }
}

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

C#生成EMF矢量图形文件示例详解

绘制位置错误

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

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

C#生成EMF矢量图形文件示例详解

修改实现代码如下:

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);
       }
   }
}

这次总算成功了:

C#生成EMF矢量图形文件示例详解

结论

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

C#生成EMF矢量图形文件示例详解

来源:https://blog.51cto.com/MyIO/5275077

标签:C#,EMF,矢量图
0
投稿

猜你喜欢

  • SpringMVC+EasyUI实现页面左侧导航菜单功能

    2022-09-30 17:11:01
  • Android实现页面跳转的全过程记录

    2023-08-16 21:06:08
  • 解决jmap命令打印JVM堆信息异常的问题

    2023-11-05 09:07:02
  • Android实现单页面浮层可拖动view的示例代码

    2023-05-25 16:41:03
  • C#窗体实现酒店管理系统

    2023-05-23 18:58:47
  • asp.net core项目mvc权限控制:分配权限

    2023-09-06 04:24:13
  • C#实现炫酷启动图-动态进度条效果

    2022-01-04 22:22:10
  • C# 特殊的string类型详解

    2022-02-10 14:11:59
  • C#清除WebBrowser中Cookie缓存的方法

    2022-01-13 20:01:08
  • JAVA算法起步之插入排序实例

    2021-05-30 15:52:30
  • Java实现按行读取大文件

    2022-11-05 13:56:57
  • 三道java新手入门面试题,通往自由的道路--多线程

    2023-05-24 23:12:51
  • C#基于UDP进行异步通信的方法

    2022-03-20 18:23:55
  • SpringMVC实现文件上传和下载功能

    2022-10-03 18:22:27
  • SpringBoot深入探究四种静态资源访问的方式

    2021-11-30 02:42:46
  • c#异步操作后台运行(backgroundworker类)示例

    2021-12-18 15:54:10
  • Android GPS详解及示例代码

    2021-11-12 08:27:28
  • JAVA SFTP文件上传、下载及批量下载实例

    2023-02-11 14:31:46
  • Java编程实现五子棋人人对战代码示例

    2021-12-27 12:58:51
  • Android开发之ListView的head消失页面导航栏的渐变出现和隐藏

    2022-03-02 14:25:24
  • asp之家 软件编程 m.aspxhome.com