C#生成EMF矢量圖形文件示例詳解
前言
公眾號(hào)上有網(wǎng)友詢問(wèn)我如何生成 EMF 文件的問(wèn)題:
本以為非常簡(jiǎn)單,我快速給出了解決方案:
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);
結(jié)果,網(wǎng)友告訴我,這是錯(cuò)誤的:
用編輯器查看文件內(nèi)容,發(fā)現(xiàn)實(shí)際生成的是PNG格式文件:
這是怎么回事呢?
原因
在官方文檔上找到這樣一段話:
當(dāng)使用Save此方法將圖形圖像另存為Windows元文件格式 (WMF) 或增強(qiáng)的圖元文件格式 (EMF) 文件時(shí),生成的文件將改為保存為可移植網(wǎng)絡(luò)圖形 (PNG) 文件。發(fā)生此行為是因?yàn)?NET Framework的GDI+組件沒有可用于將文件另存為 .wmf 或 .emf 文件的編碼器。
不理解這樣設(shè)計(jì)的原因,不支持應(yīng)該拋出異常吧?!
實(shí)現(xiàn)
不過(guò)還好,從文檔上我們也找到了解決方案,那就是使用Metafile類。
可是在實(shí)現(xiàn)時(shí),又踩了不少坑。
創(chuàng)建實(shí)例失敗
按照示例代碼,使用文件名創(chuàng)建實(shí)例:
var metafile = new Metafile("MyIO.emf");
結(jié)果報(bào)了個(gè)通用異常,完全沒有指導(dǎo)意義:
只好反編譯代碼查錯(cuò)。
發(fā)現(xiàn),底層實(shí)現(xiàn)使用的GdipCreateMetafileFromFileAPI:
public Metafile(string filename) { Path.GetFullPath(filename); SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipCreateMetafileFromFile(filename, out IntPtr metafile)); SetNativeImage(metafile); }
也就是說(shuō),參數(shù)必須是一個(gè)已存在的 EMF 文件名。
查看其他構(gòu)造函數(shù)的實(shí)現(xiàn),發(fā)現(xiàn)傳遞referenceHdc的構(gòu)造函數(shù)使用的是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); }
也就是說(shuō),這個(gè) API 可以創(chuàng)建 EMF 文件??磥?lái)可以用。
而referenceHdc可以使用Graphics.GetHdc()得到。
于是,實(shí)現(xiàn)代碼如下:
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); } } }
生成的確實(shí)是矢量圖形文件:
繪制位置錯(cuò)誤
可以明顯看到,第一個(gè)My IO繪制的位置是錯(cuò)誤的,繪制到了左上角,而不是(100, 100)。
再次查找構(gòu)造函數(shù),發(fā)現(xiàn)可以傳遞Rectangle參數(shù):
修改實(shí)現(xiàn)代碼如下:
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); } } }
這次總算成功了:
結(jié)論
后來(lái)發(fā)現(xiàn),生成的圖片實(shí)際是375 x 375像素,這應(yīng)該是因?yàn)槲业娘@示屬性設(shè)置了縮放的原因(375 / 300 = 1.25):
以上就是C#生成EMF矢量圖形文件示例詳解的詳細(xì)內(nèi)容,更多關(guān)于C#生成EMF矢量圖形文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#判斷一個(gè)字符串是否是數(shù)字或者含有某個(gè)數(shù)字的方法
這篇文章主要介紹了C#判斷一個(gè)字符串是否是數(shù)字或者含有某個(gè)數(shù)字的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06C#請(qǐng)求http向網(wǎng)頁(yè)發(fā)送接收數(shù)據(jù)的方法
這篇文章主要為大家詳細(xì)介紹了C#請(qǐng)求http向網(wǎng)頁(yè)發(fā)送數(shù)據(jù)、網(wǎng)頁(yè)接收的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07用幾行C#代碼實(shí)現(xiàn)定時(shí)關(guān)機(jī)/重啟(超詳細(xì)!建議新手練習(xí))
有很多的軟件都實(shí)現(xiàn)了自動(dòng)關(guān)機(jī)這樣的功能,下面這篇文章主要給大家介紹了關(guān)于利用幾行C#代碼實(shí)現(xiàn)定時(shí)關(guān)機(jī)/重啟的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12c#中SqlHelper封裝SqlDataReader的方法
這篇文章主要介紹了c#中SqlHelper封裝SqlDataReader的方法,涉及C#針對(duì)數(shù)據(jù)庫(kù)相關(guān)操作封裝與使用的技巧,需要的朋友可以參考下2015-05-05WPF中的ListBox實(shí)現(xiàn)按塊顯示元素的方法
這篇文章主要介紹了WPF中的ListBox實(shí)現(xiàn)按塊顯示元素的方法,涉及ListBox屬性設(shè)置相關(guān)操作技巧,需要的朋友可以參考下2016-09-09picturebox加載圖片的三種方法與網(wǎng)站驗(yàn)證碼的抓取
這篇文章主要介紹了picturebox加載圖片的三種方法與網(wǎng)站驗(yàn)證碼的抓取,需要的朋友可以參考下2015-03-03