locked
WMF / EMF File Format - Graphics.EnumerateMetafile? GDI Expert needed RRS feed

  • Question

  • In my program, I have a requirement to "playback" or "parse" windows metafiles (WMF and EMF). I have dug through MSDN and Google, and the closest I have come is the Graphics. EnumerateMetafile method. I can get it to work, in that my EnumerateMetafileProc callback is called, and I can then call PlayRecord. What is missing, is how to get usefull data out of that callback.

    Example I looked at: http://msdn.microsoft.com/en-us/library/ms142060.aspx

    The callback has a recordType parameter, which is an ENUM. Well this looks useful, except their seems to be no way to cast the data into any useful type.

    My goal is to play back the creation of the WMF/EMF, so that I can make function calls to a specialized graphics class which has methods like (DrawLine, DrawPoint, DrawArc). In a sense, I am re-creating the WMF/EMF drawing in a completely different format (Converting).

    Any help in this is extremely appreciated.
    Thursday, November 19, 2009 1:48 PM

Answers

  • The same as the GDI method.  If the method expects an int as the first argument, then the first 4 bytes in the data array should be an int, etc.  This is not a simple task.  I'd opt for the easy way.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 6:24 PM
  • It's not a .NET call.  They are GDI and GDI+ calls.  All of the calls are documented in the Platform SDK.  The playback is the method called and the data for the method.  The called method parses the data.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 6:03 PM
  • is there perhaps source code for the PlayRecord() method that this data is being passed to?

    The Reference Source is freely available as is RedGates's reflector.  PlayRecord simply calls PlayRecord in the gdiplus.dll which is a part of the OS and that code isn't available.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 7:17 PM
  • I had a look. Where do I find the C++ types so I can marshal them into C#?

    They are in the gdiplus header files.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 10:23 PM

All replies

  • In a sense, I am re-creating the WMF/EMF drawing in a completely different format (Converting).

    Code always helps.  What are you trying to do?  You got two choices:  EnumerateMetafile or DrawImage.
    Thursday, November 19, 2009 3:28 PM
  • Thank you
    A simple example:

            private Metafile mf;
            private Graphics.EnumerateMetafileProc metafileDelegate;
            private Point destPoint;
    
    public Test()
    {
    string file_name = "CRANE.WMF";
                mf = new Metafile(file_name);
                Graphics gr = picOrig.CreateGraphics();
    metafileDelegate = new Graphics.EnumerateMetafileProc(MetafileCallback);
                destPoint = new Point(20, 10);
                gr.EnumerateMetafile(mf, destPoint, metafileDelegate);
    }
    
    
            private bool MetafileCallback(
                                           EmfPlusRecordType recordType,
                                           int flags,
                                           int dataSize,
                                           IntPtr data,
                                           PlayRecordCallback callbackData)
            {
                byte[] dataArray = null;
                if (data != IntPtr.Zero)
                {
                    // Copy the unmanaged record to a managed byte buffer 
                    // that can be used by PlayRecord.
                    dataArray = new byte[dataSize];
                    Marshal.Copy(data, dataArray, 0, dataSize);
                }
    
                string l_strData = String.Empty;
                try
                {
                    System.Text.Encoding enc = System.Text.Encoding.UTF8;
                    l_strData = enc.GetString(dataArray);
                }
                catch
                {
                    l_strData = "N/A";
                }
                lbDebug.Items.Add(recordType.ToString() + "- Data:" + l_strData);
    
                mf.PlayRecord(recordType, flags, dataSize, dataArray);
    
                return true;
            }
    
    
    Above I am enumerating the EMF file, and that seems to work well. The callback is executed many times, I can see WmfPolygons, WmfPolylines and other eum types. My issue is how to cast the dataArray variable into something I can use.

    Essentially, I want to re-create the WMF/EMF step by step. For example if the callback returns a WmfPolyline, how do i get the details of that wmfpolyline? Location, size?

    What I do with that data, is recreate the basic shapes using a set of custom classes which have the usual drawing methods (DrawLine, DrawArc, etc.).

    How can I do that? This seems like the correct approach, but incomplete. My search of the MSDN was fruitless. Thank you for your help.

    Thursday, November 19, 2009 4:59 PM
  • I don't know why you would want to do this, but if you study "Metafiles" under "Win32 and COM Development.GraphicsandMultimedia.GDI.WindowsGDI" in the Platform SDK, you can, with a lot of effort, convert the metafile records to .NET drawing functions.
    Friday, November 20, 2009 7:07 AM
  • Because I need to convert these wmf/emf files into a particular format for which there us no converter. My only option is to recreate the drawing steps taken to create the wmf/emf. So there is no way to break one of these files into their primitive shapes? If I could simply get a list of lines, arcs and polygons that would suffice.

    Thank you for the help.
    Friday, November 20, 2009 12:52 PM
  • If I could simply get a list of lines, arcs and polygons that would suffice.
    Store the metafile records in a list.  What file format do you want?
    Friday, November 20, 2009 1:01 PM
  • Let me rephrase the requirement down to it's core. I need to take a single WMF/EMF image file, and break it into the shapes that comprise it, so I may recreate those shapes using a proprietary set of functions (.DrawLine(), .DrawArc(), .DrawPixel()).

    I had thought EnumerateMetafile would do that, but it seems one would have to marshal a dozen GDI classes to have a chance at using that method. What is an easy way? How are all of the image converter apps getting at the underlying shapes that comprise a WMF/EMF?

    Thank you.
    Friday, November 20, 2009 2:17 PM
  • Let me rephrase the requirement down to it's core. I need to take a single WMF/EMF image file, and break it into the shapes that comprise it, so I may recreate those shapes using a proprietary set of functions (.DrawLine(), .DrawArc(), .DrawPixel()).

    I had thought EnumerateMetafile would do that, but it seems one would have to marshal a dozen GDI classes to have a chance at using that method. What is an easy way? How are all of the image converter apps getting at the underlying shapes that comprise a WMF/EMF?

    Thank you.

    You've already said that.  What are you trying to do?  Are you trying to create a vector file format or a raster file format?  An easy way would be to buy a converter program.   
    Friday, November 20, 2009 2:38 PM
  • I am creating a vector file format. This format is quite proprietary as it relates to a prototype printer.
    Friday, November 20, 2009 3:48 PM
  • A metafile is simply a list of calls to the Windows GDI and GDI+ APIs.  They're covered in detail in the Platform SDK.  You'll have to translate them to calls to your interface. 

    Friday, November 20, 2009 4:36 PM
  • Do you know of any example projects showing the parsing of those calls? For example, the EnumerateMetafileProc seems to play them back, but the value is just a byte[]. I cannot locate any .net examples of this type of operation.
    Friday, November 20, 2009 5:34 PM
  • It's not a .NET call.  They are GDI and GDI+ calls.  All of the calls are documented in the Platform SDK.  The playback is the method called and the data for the method.  The called method parses the data.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 6:03 PM
  • Ok, so how would I parse that data in C#? Thank you for the lengthy help in this matter.
    Friday, November 20, 2009 6:09 PM
  • The same as the GDI method.  If the method expects an int as the first argument, then the first 4 bytes in the data array should be an int, etc.  This is not a simple task.  I'd opt for the easy way.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 6:24 PM
  • Has anyone tried parsing this data, perhaps examples? is there perhaps source code for the PlayRecord() method that this data is being passed to?
    Friday, November 20, 2009 7:04 PM
  • is there perhaps source code for the PlayRecord() method that this data is being passed to?

    The Reference Source is freely available as is RedGates's reflector.  PlayRecord simply calls PlayRecord in the gdiplus.dll which is a part of the OS and that code isn't available.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 7:17 PM
  • I had a look. Where do I find the C++ types so I can marshal them into C#?
    Friday, November 20, 2009 9:14 PM
  • I had a look. Where do I find the C++ types so I can marshal them into C#?

    They are in the gdiplus header files.
    • Marked as answer by eryang Wednesday, November 25, 2009 9:58 AM
    Friday, November 20, 2009 10:23 PM