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

筅森魡賤 提交于 2019-11-27 04:31:14

Image is an abstract class: what you want to do depends on whether you are dealing with a Metafile or a Bitmap.

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

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.

You can save a Bitmap to 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.

erikkallen

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

[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.

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.

I think, in .NET, that you should create a Metafile object, create a Graphics object 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.

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

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".

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

  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 :/

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

<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.

Alex

You also need to close the CopyEnhMetaFile handler:

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.

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:

Convert an image into WMF with .NET?

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.

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.

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

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.

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!