C#生成EMF矢量图形文件示例详解
作者:MyIO 发布时间:2022-10-30 02:12:56
前言
公众号上有网友询问我如何生成 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):
来源:https://blog.51cto.com/MyIO/5275077


猜你喜欢
- 本文实例讲述了C#实现日期格式转换的公共方法类。分享给大家供大家参考,具体如下:这里演示了C#中一些日期格式的转换。创建公共方法类(Util
- 企业级项目开发中都会有文件、图片、视频等文件上传并能够访问的场景,对于初学者Demo可能会直接存储在应用服务器上;对于传统项目可能会单独搭建
- Java中Filter、Servlet、Listener的学习资料,希望大家喜欢1、Filter的功能filter功能,它使用户可以改变一个
- 公司app要求做一个扭蛋功能,其实就是一个可拖动层叠卡片列表,原理还是由一个自定义Recyclerview和LayoutManager来实现
- 一. 递归1. 简介所谓的递归,其实是一种解决问题的方式。就是在解决具有既定规律的问题时,在方法内部调用方法自身的一种编程方式。 即方法在运
- 场景:简单工厂时候,我设计了一个场景,有三种剑去打怪,这时候,需求变化了,我三种剑变成了,匕首、剑以及木棒,想要用工厂方法来实现,怎么弄?1
- 波浪球的效果一直都是想模仿的对象,在最近一段时间里模仿了这一界面,其实所用知识并不多。1)、波浪的效果是利用三角函数来实现的,在自定义vie
- 在 Android 的一些界面中,有时候我们需要为一副图片生成大小为 n * n 的缩略图,有时候需要的缩略图特殊一些,比如:1、带圆角的缩
- 一、题目给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公
- 编译Android系统APK1、设置环境export ANDROID_HOME=/home/administrator/soft/andro
- 一、概述1、事务ACID特性事务将一系列的工作视为一个工作单元,它具有 ACID 特性:A:Atomicity 不可分性 也就是说
- 记得在学习数据结构的时候一味的想用代码实现算法,重视的是写出来的代码有一个正确的输入,然后有一个正确的输出,那么就很满足了。从网上看了许多的
- 本文实例为大家分享了Winform实现导入导出Excel文件的具体代码,供大家参考,具体内容如下/// <summary> &n
- 使用GroupingSearch对搜索结果进行分组Package org.apache.lucene.search.grouping Des
- 1.PDF文件简介PDF是可移植文档格式,是一种电子文件格式,具有许多其他电子文档格式无法相比的优点。PDF文件格式可以将文字、字型、格式、
- 本文实例为大家分享了java实现银行管理系统的具体代码,供大家参考,具体内容如下Bank类package First;import java
- Synchronized的用法在多线程并发问题中,常用Synchronized锁解决问题。Synchronized锁通常用于同步示例方法,同
- 什么是OKHttp一般在Java平台上,我们会使用Apache HttpClient作为Http客户端,用于发送 HTTP 请求,并对响应进
- /** * 朴素字符串算法通过两层循环来寻找子串, * 好像是一个包含模式的“模板”沿待查文本滑动。 * 算法的思想是:从主串S的第p
- LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量