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

4
有很多将wmf转换为位图的例子,如: 可靠的.wmf / wmf到基于像素的图像转换 但我需要相反的操作。我不需要矢量化器。我只想在不必担心wmf格式的位和字节的情况下嵌入图片到wmf文件中。我需要一个.NET解决方案,最好是C#。
我最初认为这会完成工作:
using (Image img = Image.FromFile (path)) {
    img.Save (myStream, System.Drawing.Imaging.ImageFormat.Wmf);
}

但在运行时,它抱怨编码器为空。我应该在哪里/如何构建这样的编码器?我不需要一个复杂的编码器,只需要将图像包装成wmf格式。WMF对支持的格式有一些要求吗?我想png和bmp是支持的,但gif也支持吗?

4个回答

7

以下是完整的答案及我的修正。Vincent的答案是完全正确的,只缺少了一些定义和一个枚举。因此我在这里发布“清洁”的工作代码,希望它对其他人有用。

        [Flags]
        private enum EmfToWmfBitsFlags {
            EmfToWmfBitsFlagsDefault = 0x00000000,
            EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
            EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
            EmfToWmfBitsFlagsNoXORClip = 0x00000004
        }

        private static int MM_ISOTROPIC = 7;
        private static int MM_ANISOTROPIC = 8;

        [DllImport ("gdiplus.dll")]
        private static extern uint GdipEmfToWmfBits (IntPtr _hEmf, uint _bufferSize,
            byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
        [DllImport ("gdi32.dll")]
        private static extern IntPtr SetMetaFileBitsEx (uint _bufferSize,
            byte[] _buffer);
        [DllImport ("gdi32.dll")]
        private static extern IntPtr CopyMetaFile (IntPtr hWmf,
            string filename);
        [DllImport ("gdi32.dll")]
        private static extern bool DeleteMetaFile (IntPtr hWmf);
        [DllImport ("gdi32.dll")]
        private static extern bool DeleteEnhMetaFile (IntPtr hEmf);

        private static MemoryStream MakeMetafileStream (Bitmap image)
        {
            Metafile metafile = null;
            using (Graphics g = Graphics.FromImage (image)) {
                IntPtr hDC = g.GetHdc ();
                metafile = new Metafile (hDC, EmfType.EmfOnly);
                g.ReleaseHdc (hDC);
            }

            using (Graphics g = Graphics.FromImage (metafile)) {
                g.DrawImage (image, 0, 0);
            }
            IntPtr _hEmf = metafile.GetHenhmetafile ();
            uint _bufferSize = GdipEmfToWmfBits (_hEmf, 0, null, MM_ANISOTROPIC,
                EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
            byte[] _buffer = new byte[_bufferSize];
            GdipEmfToWmfBits (_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
                    EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
            IntPtr hmf = SetMetaFileBitsEx (_bufferSize, _buffer);
            string tempfile = Path.GetTempFileName ();
            CopyMetaFile (hmf, tempfile);
            DeleteMetaFile (hmf);
            DeleteEnhMetaFile (_hEmf);

            var stream = new MemoryStream ();
            byte[] data = File.ReadAllBytes (tempfile);
            //File.Delete (tempfile);
            int count = data.Length;
            stream.Write (data, 0, count);
            return stream;
        }

谢谢您的发布!我觉得我的“答案”更像是一系列基于一些有根据的谷歌搜索的提示 :p - Vincent Vancalbergh
伙计,我现在真的爱你... 我已经尝试了将近一周使用WPF创建与WordPad兼容的RTF文件,并且一直被“从图像创建wmetafile”这一部分难住...谢谢! - David

6
发布了一个改进版本(顺便称赞一下他和Vincent)
    [Flags]
    private enum EmfToWmfBitsFlags
    {
        EmfToWmfBitsFlagsDefault = 0x00000000,
        EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
        EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
        EmfToWmfBitsFlagsNoXORClip = 0x00000004
    }

    private static int MM_ISOTROPIC = 7;
    private static int MM_ANISOTROPIC = 8;

    [DllImport("gdiplus.dll")]
    private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
        byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
    [DllImport("gdi32.dll")]
    private static extern IntPtr SetMetaFileBitsEx(uint _bufferSize,
        byte[] _buffer);
    [DllImport("gdi32.dll")]
    private static extern IntPtr CopyMetaFile(IntPtr hWmf,
        string filename);
    [DllImport("gdi32.dll")]
    private static extern bool DeleteMetaFile(IntPtr hWmf);
    [DllImport("gdi32.dll")]
    private static extern bool DeleteEnhMetaFile(IntPtr hEmf);

    public static MemoryStream MakeMetafileStream(System.Drawing.Bitmap image)
    {
        Metafile metafile = null;
        using (Graphics g = Graphics.FromImage(image))
        {
            IntPtr hDC = g.GetHdc();
            metafile = new Metafile(hDC, EmfType.EmfOnly);
            g.ReleaseHdc(hDC);
        }

        using (Graphics g = Graphics.FromImage(metafile))
        {
            g.DrawImage(image, 0, 0);
        }
        IntPtr _hEmf = metafile.GetHenhmetafile();
        uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
            EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
        byte[] _buffer = new byte[_bufferSize];
        GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
                EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
        DeleteEnhMetaFile(_hEmf);

        var stream = new MemoryStream();
        stream.Write(_buffer, 0, (int)_bufferSize);
        stream.Seek(0, 0);

        return stream;
    }

这个方法不会留下临时文件,也避免了将_bufferSize复制到一个临时文件,然后再复制到另一个缓冲区。

感谢大家。

6

这里得知:

当您使用Save方法将图形图像保存为Windows Metafile Format(WMF)或Enhanced Metafile Format(EMF)文件时,生成的文件将被保存为Portable Network Graphics(PNG)文件。这种行为是由于.NET Framework的GDI+组件没有编码器可用于将文件保存为.wmf或.emf文件。

但我猜你已经了解到这一点了 :)

这里有人将位图放入FileStream中。

metafileStream = MakeMetafileStream(gdiBitmap);

使用MakeMetafileStream()函数:

private static MemoryStream MakeMetafileStream(Bitmap image)
{
  Graphics graphics = null;
  Metafile metafile= null;
  var stream = new MemoryStream();
  try
  {
    using (graphics = Graphics.FromImage(image))
    {
      var hdc = graphics.GetHdc();
      metafile= new Metafile(stream, hdc);
      graphics.ReleaseHdc(hdc);
    }
    using (graphics = Graphics.FromImage(metafile))
    { graphics.DrawImage(image, 0, 0); }
  }
  finally
  {
    if (graphics != null)
    { graphics.Dispose(); }
    if (metafile!= null)
    { metafile.Dispose(); }
  }
  return stream;
}

有趣的东西。 但是关于编码器的事情...

这里,微软的Peter Huang发布了这个非托管的方法:

        [DllImport("gdiplus.dll")]
        private static extern uint GdipEmfToWmfBits (IntPtr _hEmf, uint _bufferSize,
            byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
        [DllImport("gdi32.dll")]
        private static extern IntPtr SetMetaFileBitsEx (uint _bufferSize,
            byte[] _buffer);
        [DllImport("gdi32.dll")]
        private static extern IntPtr CopyMetaFile (IntPtr hWmf,
            string filename);
        [DllImport("gdi32.dll")]
        private static extern bool DeleteMetaFile (IntPtr hWmf);
        [DllImport("gdi32.dll")]
        private static extern bool DeleteEnhMetaFile (IntPtr hEmf);
        private void button4_Click(object sender, System.EventArgs e)
        {
            Graphics g= this.CreateGraphics();
            IntPtr hDC = g.GetHdc();
            Metafile mf = new Metafile(hDC,EmfType.EmfOnly);
            g.ReleaseHdc(hDC);
            g.Dispose();
            g=Graphics.FromImage(mf);
            //Pen p = new Pen(Color.White,5);
            g.DrawArc(Pens.Black,0,0,200,200,0,360);
            //g.DrawImage(Bitmap.FromFile(@"c:\temp\test.bmp"),0,0);
            g.Dispose();
            IntPtr _hEmf= mf.GetHenhmetafile();
            uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
                EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
            byte[] _buffer = new byte[_bufferSize];
            GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
                    EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
            IntPtr hmf = SetMetaFileBitsEx(_bufferSize, _buffer);
            CopyMetaFile(hmf, "C:\\ConvertedMetafile.wmf");
            DeleteMetaFile(hmf);
            DeleteEnhMetaFile(_hEmf);
        }

希望这能帮助你到达目的地 :)

非常感谢您提供这些链接,不幸的是在我的情况下它们并没有起作用... - jdehaan
似乎 WMF 已经“走向末路”。也许这是你必须转换到另一种格式的理由? :p - Vincent Vancalbergh
是的,我并不真的喜欢那些玩意儿,相信我...我只是需要向一款老旧工具提供数据,但它对支持的格式非常犹豫。 - jdehaan
非托管方法经过一些更改后成功了,感谢提示! - jdehaan
有没有可能是指“老牌工具”Crystal Reports?这就是我来到这里的原因。 - Grinn
1
对我来说,“老工具”就是 .Net 中的 RichTextBox。令人烦恼的是它不支持 jpeg 或 png 图像。基本上,使用 .Net 无法创建一个包含可在 .Net 内置的 RTF 控件中查看的图像的 RTF 文件。 - Steve Hiner

0
这是一个Win32 GDI+示例,对我很有用(感谢http://www.codeproject.com/Articles/6879/How-to-use-GDI-to-save-image-in-WMF-EXIF-or-EMF-fo)。
Bitmap *image;
image = Bitmap::FromFile(L"in.jpg");                // read in the JPG
HDC hdc = GetDC(hwnd);                              // parent window
Metafile *metafile = new Metafile(L"out.wmf", hdc);
Graphics *graphics = new Graphics(metafile);
graphics->DrawImage(image, 0, 0, image->GetWidth(), image->GetHeight());
delete graphics; delete metafile; delete image;
ReleaseDC(hwnd, hdc);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接