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

 更新时间:2022年05月20日 09:02:13   作者:MyIO  
这篇文章主要为大家介绍了C# 生成 EMF矢量图形文件详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

公众号上有网友询问我如何生成 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);

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

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

这是怎么回事呢?

原因

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

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

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

实现

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

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

创建实例失败

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

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

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

只好反编译代码查错。

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

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

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

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

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

绘制位置错误

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

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

修改实现代码如下:

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

这次总算成功了:

结论

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

以上就是C#生成EMF矢量图形文件示例详解的详细内容,更多关于C#生成EMF矢量图形文件的资料请关注脚本之家其它相关文章!

相关文章

  • C#中抛出异常用法实例

    C#中抛出异常用法实例

    这篇文章主要介绍了C#中抛出异常用法,实例分析了C#使用throw抛出异常的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-04-04
  • C#把数字转换成大写金额的代码实例

    C#把数字转换成大写金额的代码实例

    这篇文章主要介绍了C#把数字转换成大写金额的代码实例,例如把200转换成“贰佰元”,需要的朋友可以参考下
    2014-05-05
  • 解决C# winForm自定义鼠标样式的两种实现方法详解

    解决C# winForm自定义鼠标样式的两种实现方法详解

    本篇文章是对在C#中winForm自定义鼠标样式的两种实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C#根据http和ftp图片地址获取对应图片

    C#根据http和ftp图片地址获取对应图片

    这篇文章主要为大家详细介绍了C#根据http和ftp图片地址获取对应图片,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • winform实现限制及解除鼠标移动范围的方法

    winform实现限制及解除鼠标移动范围的方法

    这篇文章主要介绍了winform实现限制及解除鼠标移动范围的方法,涉及C#控制WinForm鼠标事件属性的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-09-09
  • C# Winform实现表格复制粘贴效果

    C# Winform实现表格复制粘贴效果

    这篇文章主要为大家学习介绍了如何通过C# Winform实现表格复制粘贴效果,文中的示例代码讲解详细,具有一定的参考价值,需要的可以了解一下
    2023-07-07
  • C#绘制椭圆的方法

    C#绘制椭圆的方法

    这篇文章主要介绍了C#绘制椭圆的方法,涉及C#图形绘制的相关技巧,需要的朋友可以参考下
    2015-06-06
  • C# Linq延迟查询的执行实例代码

    C# Linq延迟查询的执行实例代码

    这篇文章主要介绍了C# Linq延迟查询执行的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • C#使用符号表实现查找算法

    C#使用符号表实现查找算法

    本文详细讲解了C#使用符号表实现查找算法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • C#实现将商品金额小写转换成大写的方法

    C#实现将商品金额小写转换成大写的方法

    这篇文章主要介绍了C#实现将商品金额小写转换成大写的方法,涉及C#数组与字符串的相关操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-08-08

最新评论