The Enhanced Metafile (EMF) Vector Image Format

It looks like we have some support in Windows for vector images using the EMF file format. EMFs (Enhanced Metafiles) let you save both vectors and bitmaps within them. However, if you use the Metafile constructor properly, you can save to disk and load from disk the entire sequence of operations that are a part of this vector format. Here is an example of how you can get started with Metafiles:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing.Text;

namespace ExtendedMetafileApplication
{
    class Program
    {

        [DllImport("gdiplus.dll")]
        private static extern uint GdipEmfToWmfBits(IntPtr hemf, uint cbData16, byte[] pData16, int iMapMode, int eFlags);

        static void Main(string[] args)
        {
            // Create an EMF vector image stream in memory.
            using (var metaStream = CreateMemoryEMF())
            {
                // Saving the vector image as an EMF file to disk.
                CreateDiskEMF("Image.emf", metaStream);
                // Save the vector image as its rasterized PNG counterparts at different scales.
                CreateDiskPNG("Image.png", metaStream);
                CreateDiskPNG("Image5X.png", metaStream, 5);
                CreateDiskPNG("Image10X.png", metaStream, 10);
                // Save the vector image to disk as a WMF (also a vector format for images).
                CreateDiskWMF("Image.wmf", metaStream);
            }
        }

        static void CreateDiskWMF(string path, Stream metaStream)
        {
            using (var metafile = new Metafile(metaStream))
            {
                var iMapMode = 3; // MM_HIMETRIC
                var eFlags = 2; // EmfToWmfBitsFlagsIncludePlaceable
                var hemf = metafile.GetHenhmetafile();
                // The stream of our EMF image must be created using EmfPlusDual in order for the following function to work.
                var size = GdipEmfToWmfBits(hemf, 0, null, iMapMode, eFlags);
                var buffer = new byte[size];
                GdipEmfToWmfBits(hemf, size, buffer, iMapMode, eFlags);
                File.WriteAllBytes(path, buffer);
            }
            metaStream.Seek(0, SeekOrigin.Begin);
        }

        static void SetGraphicsQuality(Graphics graphics)
        {
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.CompositingMode = CompositingMode.SourceOver;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
        }

        static void CreateDiskPNG(string path, Stream metaStream, float scale = 1)
        {
            var image = Image.FromStream(metaStream);
            metaStream.Seek(0, SeekOrigin.Begin);
            var bitmap = new Bitmap(image, (int)(image.Size.Width * scale), (int)(image.Size.Height * scale));
            using (var graphics = Graphics.FromImage(bitmap))
            {
                SetGraphicsQuality(graphics);
                graphics.DrawImage(image, 0, 0, bitmap.Width, bitmap.Height);
                bitmap.Save(path, ImageFormat.Png);
            }
        }

        static void DrawGraphics(Metafile metafile)
        {
            var brush = Brushes.BlueViolet;
            var pen = new Pen(brush, 3);
            using (var graphics = Graphics.FromImage(metafile))
            {
                graphics.Clear(Color.White);
                SetGraphicsQuality(graphics);
                graphics.DrawEllipse(pen, 2.5f, 2.5f, 45, 45);
                graphics.DrawEllipse(pen, 52.5f, 2.5f, 45, 45);
                graphics.FillEllipse(brush, 4f, 15f, 25, 25);
                graphics.FillEllipse(brush, 54f, 15f, 25, 25);
                graphics.DrawArc(pen, 30, 55, 50, 35, 90, 180);
                graphics.DrawArc(pen, 5, 45, 90, 70, 0, 180);
                graphics.DrawString("Hello, world.", new Font(FontFamily.GenericSerif, 14), brush, new Rectangle(0, 120, 150, 30));
            }
        }

        static Stream CreateMemoryEMF()
        {
            var result = new MemoryStream();
            Metafile metafile;
            using (var graphics = Graphics.FromHwnd(IntPtr.Zero))
            {
                SetGraphicsQuality(graphics);
                var context = graphics.GetHdc();
                metafile = new Metafile(result, context, EmfType.EmfPlusDual);
                graphics.ReleaseHdc(context);
            }
            DrawGraphics(metafile);
            result.Flush();
            result.Seek(0, SeekOrigin.Begin);
            return result;
        }

        static void CreateDiskEMF(string path, Stream metaStream)
        {
            using (var fileStream = File.Create(path))
                metaStream.CopyTo(fileStream);
            metaStream.Seek(0, SeekOrigin.Begin);
        }
    }
}

The code above outputs the following set of images:

Image.emf

Image.png

Image5X.png

Image10X.png

Image.wmf

If you’re looking for a nice way to do some simple graphics in Windows applications for business or otherwise, this is probably one of the best formats to use if you want to be able to scale.

Alexandru

"To avoid criticism, say nothing, do nothing, be nothing." - Aristotle

"It is wise to direct your anger towards problems - not people; to focus your energies on answers - not excuses." - William Arthur Ward

"Science does not know its debt to imagination." - Ralph Waldo Emerson

"Money was never a big motivation for me, except as a way to keep score. The real excitement is playing the game." - Donald Trump

"All our dreams can come true, if we have the courage to pursue them." - Walt Disney

"Mitch flashes back to a basketball game held in the Brandeis University gymnasium in 1979. The team is doing well and chants, 'We're number one!' Morrie stands and shouts, 'What's wrong with being number two?' The students fall silent." - Tuesdays with Morrie

I'm not entirely sure what makes me successful in general programming or development, but to any newcomers to this blood-sport, my best guess would be that success in programming comes from some strange combination of interest, persistence, patience, instincts (for example, someone might tell you that something can't be done, or that it can't be done a certain way, but you just know that can't be true, or you look at a piece of code and know something doesn't seem right with it at first glance, but you can't quite put your finger on it until you think it through some more), fearlessness of tinkering, and an ability to take advice because you should be humble. Its okay to be wrong or to have a bad approach, realize it, and try to find a better one, and even better to be wrong and find a better approach to solve something than to have had a bad approach to begin with. I hope that whatever fragments of information I sprinkle across here help those who hit the same roadblocks.

Leave a Reply

Your email address will not be published. Required fields are marked *