tags:

views:

4855

answers:

6

If you use Image.Save Method to save an image to a EMF/WMF, you got an exceptoin (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?

Thanks Eric

+1  A: 

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.

Mike Dimmick
+2  A: 

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

+5  A: 

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")] uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer);


IntPtr h = metafile.GetHenhMetafile();
int size = GetEnhMetaFileBits(h, 0, 0);
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.

erikkallen
While it looks like this should work I'm finding that it outputs the rasterized version of the metafile, not the vector GDI records. Additionally, you need to call DeleteEnhMetaFile(h) and be aware that calling GetHenhMetaFile() puts the metafile object into an invalid state.
Jason Harrison
You need to call DeleteEnhMetafile(h) when you are done, and the Metafile object is put into an invalid state by the GetHenhMetafile call.If the Metafile is a raster based metafile this will give you a raster based .emf on disk. If it is a vector based metafile, then you will get a vector based .emf on disk. Typically calling metafile.Save("filename", System.Drawing.Imaging.ImageFormat.Emf) will give you a .png file.
Jason Harrison
+1  A: 

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.

Han
+4  A: 

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.

tim
Wonderful, I had completely overlooked this easy way of creating a metafile based on the Metafile constructor. I must admit that having to pass in an original HDC is a bit confusing, but nevertheless, it is working.
Pierre
A: 

How would you parse a WMF or EMF file? By parse I mean break it down into lines, arcs, etc. I tried using a EnumerateMetafileProc but there are no types for the EmfPlusRecordType enum.

FlaCracker