GDI+ / C#:如何将图像保存为 EMF?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/152729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:49:45  来源:igfitidea点击:

GDI+ / C#: How to save an image as EMF?

提问by EricSch

If you use Image.Save Method to save an image to a EMF/WMF, you get an exception (http://msdn.microsoft.com/en-us/library/ktx83wah.aspx)

如果使用 Image.Save 方法将图像保存到 EMF/WMF,则会出现异常 ( http://msdn.microsoft.com/en-us/library/ktx83wah.aspx)

Is there another way to save the image to an EMF/WMF? Are there any encoders available?

还有另一种方法可以将图像保存到 EMF/WMF 吗?有没有可用的编码器?

回答by Mike Dimmick

A metafile is a file which records a sequence of GDI operations. It is scalable because the original sequence of operations that generated the picture are captured, and therefore the co-ordinates that were recorded can be scaled.

图元文件是记录 GDI 操作序列的文件。它是可缩放的,因为生成图片的原始操作序列被捕获,因此记录的坐标可以缩放。

I think, in .NET, that you should create a Metafileobject, create a Graphicsobject using Graphics.FromImage, then perform your drawing steps. The file is automatically updated as you draw on it. You can find a small sample in the documentation for Graphics.AddMetafileComment.

我认为,在 .NET 中,您应该创建一个Metafile对象,Graphics使用 来创建一个对象Graphics.FromImage,然后执行您的绘图步骤。当您在文件上绘图时,该文件会自动更新。您可以在Graphics.AddMetafileComment的文档中找到一个小示例。

If you really want to store a bitmap in a metafile, use these steps then use Graphics.DrawImageto paint the bitmap. However, when it is scaled it will be stretched using StretchBlt.

如果您真的想将位图存储在图元文件中,请使用这些步骤然后使用Graphics.DrawImage来绘制位图。但是,当它被缩放时,它将使用StretchBlt.

回答by Mike Dimmick

The question was: "Is there another way to save the image to an EMF/WMF?" Not "what is metafile" or "how to create metafile" or "how to use metafile with Graphics".

问题是:“还有另一种方法可以将图像保存到 EMF/WMF 吗?” 不是“什么是图元文件”或“如何创建图元文件”或“如何将图元文件与图形一起使用”。

I also look for answer for this question "how to save EMF/WMF" In fact if you use:

我也在寻找这个问题的答案“如何保存 EMF/WMF” 事实上,如果你使用:

  Graphics grfx = CreateGraphics();
  MemoryStream ms = new MemoryStream();
  IntPtr ipHdc = grfx.GetHdc();

  Metafile mf = new Metafile(ms, ipHdc);

  grfx.ReleaseHdc(ipHdc);
  grfx.Dispose();
  grfx = Graphics.FromImage(mf);

  grfx.FillEllipse(Brushes.Gray, 0, 0, 100, 100);
  grfx.DrawEllipse(Pens.Black, 0, 0, 100, 100);
  grfx.DrawArc(new Pen(Color.Red, 10), 20, 20, 60, 60, 30, 120);
  grfx.Dispose();

  mf.Save(@"C:\file.emf", ImageFormat.Emf);
  mf.Save(@"C:\file.png", ImageFormat.Png);

In both cases image is saved as format png. And this is the problem which I cannot solve :/

在这两种情况下,图像都保存为 png 格式。这是我无法解决的问题:/

回答by erikkallen

If I remember correctly, it can be done with a combination of the Metafile.GetHenhmetafile(), the API GetEnhMetaFileBits() and Stream.Write(), something like

如果我没记错的话,它可以通过 Metafile.GetHenhmetafile()、API GetEnhMetaFileBits() 和 Stream.Write() 的组合来完成,例如

[DllImport("gdi32")] static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer);


IntPtr h = metafile.GetHenhMetafile();
int size = GetEnhMetaFileBits(h, 0, null);
byte[] data = new byte[size];
GetEnhMetaFileBits(h, size, data);
using (FileStream w = File.Create("out.emf")) {
    w.Write(data, 0, size);
}
// TODO: I don't remember whether the handle needs to be closed, but I guess not.

I think this is how I solved the problem when I had it.

我想这就是我解决问题时的方式。

回答by Han

The answer by erikkallen is correct. I tried this from VB.NET, and had to use 2 different DllImports to get it to work:

erikkallen 的回答是正确的。我从 VB.NET 尝试了这个,并且不得不使用 2 个不同的 DllImports 来让它工作:

<System.Runtime.InteropServices.DllImportAttribute("gdi32.dll", EntryPoint:="GetEnhMetaFileBits")> _
    Public Shared Function GetEnhMetaFileBits(<System.Runtime.InteropServices.InAttribute()> ByVal hEMF As System.IntPtr, ByVal nSize As UInteger, ByVal lpData As IntPtr) As UInteger
End Function

    <System.Runtime.InteropServices.DllImportAttribute("gdi32.dll", EntryPoint:="GetEnhMetaFileBits")> _
    Public Shared Function GetEnhMetaFileBits(<System.Runtime.InteropServices.InAttribute()> ByVal hEMF As System.IntPtr, ByVal nSize As UInteger, ByVal lpData() As Byte) As UInteger
End Function

The first import is used for the first call to get the emf size. The second import to get the actual bits. Alternatively you could use:

第一次导入用于第一次调用以获取电动势大小。第二次导入以获取实际位。或者,您可以使用:

Dim h As IntPtr = mf.GetHenhmetafile()
CopyEnhMetaFileW(h, FileName)

This copies the emf bits directly to the named file.

这会将 emf 位直接复制到命名文件。

回答by Han

Imageis an abstract class: what you want to do depends on whether you are dealing with a Metafileor a Bitmap.

Image是一个抽象类:你想要做什么取决于你是在处理 aMetafile还是 a Bitmap

Creating an image with GDI+ and saving it as an EMF is simple with Metafile. Per Mike's post:

使用 GDI+ 创建图像并将其保存为 EMF 很简单Metafile。根据迈克的帖子

var path = @"c:\foo.emf"
var g = CreateGraphics(); // get a graphics object from your form, or wherever
var img = new Metafile(path, g.GetHdc()); // file is created here
var ig = Graphics.FromImage(img);
// call drawing methods on ig, causing writes to the file
ig.Dispose(); img.Dispose(); g.ReleaseHdc(); g.Dispose();

This is what you want to do most of the time, since that is what EMF is for: saving vector images in the form of GDI+ drawing commands.

这就是您大部分时间想要做的事情,因为这就是 EMF 的用途:以 GDI+ 绘图命令的形式保存矢量图像。

You can save a Bitmapto an EMF file by using the above method and calling ig.DrawImage(your_bitmap), but be aware that this does not magically covert your raster data into a vector image.

您可以Bitmap使用上述方法并调用将 a 保存到 EMF 文件ig.DrawImage(your_bitmap),但请注意,这不会神奇地将您的光栅数据转换为矢量图像。

回答by BZ1

I was looking for a way to save the GDI instructions in a Metafile object to a EMF file. Han's post helped me solve the problem. This was before I joined SOF. Thank you, Han. Here is what I tried.

我一直在寻找一种将 Metafile 对象中的 GDI 指令保存到 EMF 文件的方法。韩的帖子帮我解决了这个问题。这是在我加入特种作战部队之前。谢谢你,韩。这是我尝试过的

    [DllImport("gdi32.dll")]
    static extern IntPtr CopyEnhMetaFile(  // Copy EMF to file
        IntPtr hemfSrc,   // Handle to EMF
        String lpszFile // File
    );

    [DllImport("gdi32.dll")]
    static extern int DeleteEnhMetaFile(  // Delete EMF
        IntPtr hemf // Handle to EMF
    );

   // Code that creates the metafile 
   // Metafile metafile = ...

   // Get a handle to the metafile
   IntPtr iptrMetafileHandle = metafile.GetHenhmetafile();

   // Export metafile to an image file
   CopyEnhMetaFile(
         iptrMetafileHandle, 
          "image.emf");

   // Delete the metafile from memory
   DeleteEnhMetaFile(iptrMetafileHandle);

回答by Roger

It appears there is much confusion over vector vs. bitmap. All of the code in this thread generates bitmap (non-vector) files - it does not preserve the vector GDI calls. To prove this to yourself, download the "EMF Parser" tool and inspect the output files: http://downloads.zdnet.com/abstract.aspx?docid=749645.

看起来矢量与位图存在很多混淆。此线程中的所有代码都会生成位图(非矢量)文件 - 它不保留矢量 GDI 调用。要向自己证明这一点,请下载“EMF Parser”工具并检查输出文件:http: //downloads.z​​dnet.com/abstract.aspx?docid=749645

This issue has caused many developers considering anguish. Sure would be nice if Microsoft would fix this and properly support their own EMF format.

这个问题引起了许多开发人员的苦恼。如果微软能解决这个问题并正确支持他们自己的 EMF 格式,那肯定会很好。

回答by Alex

You also need to close the CopyEnhMetaFilehandler:

您还需要关闭CopyEnhMetaFile处理程序:

IntPtr ptr2 = CopyEnhMetaFile(iptrMetafileHandle, "image.emf");
DeleteEnhMetaFile(ptr2);

// Delete the metafile from memory
DeleteEnhMetaFile(iptrMetafileHandle);

Otherwise, you cannot delete the file because it's still used by the process.

否则,您无法删除该文件,因为它仍在被进程使用。

回答by Brian Kennedy

I would recommend avoiding such extern's and unmanaged cruft in a managed .NET app. Instead, I'd recommend something a bit more like the managed solution given in this thread:

我建议在托管的 .NET 应用程序中避免此类外部和非托管垃圾。相反,我会推荐一些更像这个线程中给出的托管解决方案的东西:

Convert an image into WMF with .NET?

使用 .NET 将图像转换为 WMF?

P.S. I am answering this old thread because this was the best answer I had found, but then ended up developing a managed solution, which then lead me to the link above. So, to save others that time, I figured I'd point this one to that one.

PS 我正在回答这个旧线程,因为这是我找到的最佳答案,但最终开发了一个托管解决方案,然后将我带到了上面的链接。所以,为了节省其他人的时间,我想我会把这个指向那个。