GDI+ / C#:如何将图像另存为 EMF?

问题描述 投票:0回答:10

如果使用 Image.Save 方法将图像保存到 EMF/WMF,则会出现异常 (http://msdn.microsoft.com/en-us/library/ktx83wah.aspx)

是否有其他方法将图像保存为 EMF/WMF? 有可用的编码器吗?

c# gdi+ .emf
10个回答
23
投票

Image
是一个抽象类:你想要做什么取决于你正在处理的是
Metafile
还是
Bitmap

使用 GDI+ 创建图像并将其保存为 EMF 非常简单,只需使用

Metafile
。根据迈克的帖子

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

这是您大多数时候想要做的事情,因为这就是 EMF 的用途:以 GDI+ 绘图命令的形式保存矢量图像。

您可以使用上述方法并调用

Bitmap
ig.DrawImage(your_bitmap)
保存到 EMF 文件中,但请注意,这不会神奇地将光栅数据转换为矢量图像。


12
投票

如果我没记错的话,可以通过 Metafile.GetHenhmetafile()、API GetEnhMetaFileBits() 和 Stream.Write() 的组合来完成,类似

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

我想这就是我遇到问题时解决问题的方法。


3
投票

图元文件是记录一系列 GDI 操作的文件。它是可扩展的,因为生成图片的原始操作序列被捕获,因此记录的坐标可以缩放。

我认为,在 .NET 中,您应该创建一个

Metafile
对象,使用
Graphics
创建一个
Graphics.FromImage
对象,然后执行绘图步骤。当您在文件上绘图时,该文件会自动更新。您可以在 Graphics.AddMetafileComment 的文档中找到一个小示例。

如果您确实想在图元文件中存储位图,请使用这些步骤,然后使用

Graphics.DrawImage
绘制位图。但是,当缩放时,它将使用
StretchBlt
进行拉伸。


3
投票

问题是:“是否有其他方法将图像保存为 EMF/WMF?”不是“什么是图元文件”或“如何创建图元文件”或“如何将图元文件与图形一起使用”。

我也在寻找这个问题的答案“如何保存 EMF/WMF” 事实上,如果你使用:

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

在这两种情况下,图像都保存为 png 格式。这是我无法解决的问题:/


2
投票

erikkallen 的答案是正确的。我从 VB.NET 尝试了这个,并且必须使用 2 个不同的 DllImports 才能让它工作:

<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

第一次导入用于第一次调用以获取电动势大小。第二次导入以获取实际位。 或者你可以使用:

Dim h As IntPtr = mf.GetHenhmetafile()
CopyEnhMetaFileW(h, FileName)

这会将 emf 位直接复制到指定文件中。


2
投票

您还需要关闭

CopyEnhMetaFile
处理程序:

IntPtr ptr2 = CopyEnhMetaFile(iptrMetafileHandle, "image.emf");
DeleteEnhMetaFile(ptr2);

// Delete the metafile from memory
DeleteEnhMetaFile(iptrMetafileHandle);

否则,您无法删除该文件,因为该文件仍在被进程使用。


1
投票

我正在寻找一种将图元文件对象中的 GDI 指令保存到 EMF 文件的方法。韩老师的帖子帮我解决了这个问题。这是我加入 SOF 之前的事。谢谢你,韩。这是我尝试过的

 [DllImport("gdi32.dll")]
    static extern IntPtr CopyEnhMetaFile( // 将 EMF 复制到文件
        IntPtr hemfSrc, // EMF 句柄
        String lpszFile // 文件
    );

    [DllImport(“gdi32.dll”)]
    static extern int DeleteEnhMetaFile( // 删除 EMF
        IntPtr hemf // EMF 句柄
    );

   // 创建图元文件的代码
   // 图元文件 图元文件 = ...

   // 获取图元文件的句柄
   IntPtr iptrMetafileHandle = metafile.GetHenhmetafile();

   // 将图元文件导出到图像文件
   复制EnhMetaFile(
         iptr元文件句柄,
          “图像.emf”);

   // 从内存中删除图元文件
   删除EnhMetaFile(iptrMetafileHandle);


1
投票

我建议避免在托管 .NET 应用程序中出现此类外部和非托管的问题。 相反,我会推荐一些更像此线程中给出的托管解决方案的东西:

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

附注我回答这个旧线程是因为这是我找到的最佳答案,但最终开发了一个托管解决方案,然后引导我到上面的链接。因此,为了节省其他人的时间,我想我应该将这个指向那个。


0
投票

矢量与位图之间似乎存在很多混淆。该线程中的所有代码都会生成位图(非矢量)文件 - 它不保留矢量 GDI 调用。要向自己证明这一点,请下载“EMF Parser”工具并检查输出文件:http://downloads.zdnet.com/abstract.aspx?docid=749645.

这个问题引起了很多开发者的苦恼。如果微软能解决这个问题并正确支持他们自己的 EMF 格式,那就太好了。


0
投票

这是一个公共 EMF 类,用于将位图保存为 EMF,但不幸的是,不起作用..返回错误。

Public Class EMF
' This routine demonstrates how to create an Enhanced Metafile (EMF) from a
' bitmap image, contained in a PictureBox.
' You must use a PictureBox control, since the Image control doesn't support
' the hDC property, needed to create the image file.
' Draw a PictureBox on a form, and insert a supported image like Bmp,
' Jpg or Gif. Don't use a WMF or EMF image. Useful for imaging apps that need
' to save images to different formats than Bitmap.


' ========== API DECLARATIONS ==========

'Bitmap properties structure

Private Structure BITMAP
    Dim bmType As Long
    Dim bmWidth As Long
    Dim bmHeight As Long
    Dim bmWidthBytes As Long
    Dim bmPlanes As Integer
    Dim bmBitsPixel As Integer
    Dim bmBits As Long
End Structure


'Rectagle structure, needed to "build" the EMF
Private Structure RECT
    Dim Left As Long
    Dim Top As Long
    Dim Right As Long
    Dim Bottom As Long
End Structure


'API Functions for drawing graphics
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As _
  Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long,
  ByVal hObject As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long,
  ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long,
  ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long,
  ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As _
  Long
Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal _
  hObject As Long, ByVal nCount As Long, lpObject As Object) As Long
Private Declare Function StretchBlt Lib "gdi32" (ByVal hdc As Long,
  ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long,
  ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long,
  ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long,
  ByVal dwRop As Long) As Long

'API Functions for creating metafiles
Private Declare Function CreateEnhMetaFile Lib "gdi32" Alias _
  "CreateEnhMetaFileA" (ByVal hdcRef As Long, ByVal lpFileName As String,
  lpRect As RECT, ByVal lpDescription As String) As Long
Private Declare Function CloseEnhMetaFile Lib "gdi32" (ByVal hdc As Long) As _
  Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32" (ByVal hEmf As Long) As _
  Long
Private Declare Function SetMapMode Lib "gdi32" (ByVal hdc As Long,
  ByVal nMapMode As Long) As Long


'This Enum is needed to set the "Mapping" property for EMF images
Public Enum MMETRIC
    MM_HIMETRIC = 3
    MM_LOMETRIC = 2
    MM_LOENGLISH = 4
    MM_ISOTROPIC = 7
    MM_HIENGLISH = 5
    MM_ANISOTROPIC = 8
    MM_ADLIB = 9
End Enum

'è una costante in decimale 13369370 
'https://www.tapatalk.com/groups/visualbasicexplorer/what-s-the-difference-vbsrccopy-or-t4443.html
Public Const vbSrcCopy = &HCC0020

' ==========================================

' This function creates an EMF image.
' Parameters:
'  SourceImage: Must be a picturebox
'  FileName: full pathname for Enhanced Metafile on disk
'  Metrics: a value from MMETRIC Enum
'  Comments (optional): You can add your own comments to an Enhanced Metafile
'
' Example:
'  Dim RetVal As Long
'  ' The best way for creating an EMF image is to use the MM_ADLIB mapping mode
'  RetVal = CreateEmf(Picture1, "image1.emf", MM_ADLIB,
' "Enhanced Metafile Demonstration Usage")

Public Function CreateEmf(ByRef SourceImage As Object, ByVal FileName As String,
  ByVal Metrics As MMETRIC, Optional ByVal Comments As String = Nothing) As Long
    'Variables and types
    Dim bm As BITMAP
    Dim hdcMem As Long   'Temporary Compatible Device Context


    Dim hdc As Long     'EMF Device Context
    Dim hEmf As Long    'Will get the returned value by CloseEnhMetafile API
    Dim R As RECT      'A rectangle that will enclose the EMF image
    Dim OldScale As Integer 'Used to maintain Picturebox ScaleMode property
    Dim HoldBitmap As Long 'Keeps the bitmap onto memory

    Comments = Comments & vbNullChar 'You can add comments to Metafiles. A NULL
    ' char is needed

    GetObject(SourceImage, Len(bm), bm) 'Reads image properties and puts them
    ' in a Bitmap structure

    R.Top = SourceImage.Top       'Creates a rectangle using bitmap
    ' properties
    R.Left = SourceImage.Left
    R.Right = SourceImage.Picture.Width
    R.Bottom = SourceImage.Picture.Height

    'Sets the Picturebox Scalemode properties to Pixels.
    OldScale = SourceImage.ScaleMode
    'SourceImage.ScaleMode = vbPixels   'commanetato perché è gia tutto in pixel

    'Creates the metafile to disk reading the picturebox device context thru
    ' the GetDC Api
    'FileName is a string containing the full pathname for the image
    'R is the rectangle structure as shown before
    'Some comments are added.
    hdc = CreateEnhMetaFile(SourceImage.hdc, FileName, R, Comments)

    '...sets the mapping property
    SetMapMode(hdc, Metrics)

    'Since Bitmap and Metafile are different, a new compatible device context
    ' must be created
    'with a reference to the EMF device context
    hdcMem = CreateCompatibleDC(hdc)

    'Takes the bitmap....
    HoldBitmap = SelectObject(hdcMem, SourceImage)

    '...and copies first to intermediate device context reading data from the
    ' bitmap
    BitBlt(hdcMem, 0, 0, SourceImage.ScaleWidth, SourceImage.ScaleHeight, SourceImage.hdc, 0, 0, vbSrcCopy)
    'and then to the EMF device context
    BitBlt(hdc, 0, 0, SourceImage.ScaleWidth, SourceImage.ScaleHeight, hdcMem, 0, 0, vbSrcCopy)

    'Reassigns bitmap previous value to DC before deleting
    SelectObject(hdcMem, HoldBitmap)
    'Next step is disposing objects
    DeleteDC(hdcMem)
    DeleteObject(SelectObject(hdcMem, SourceImage))

    'Closes the new metafile
    hEmf = CloseEnhMetaFile(hdc)

    If DeleteEnhMetaFile(hEmf) = 1 Then
        CreateEmf = 0  'No errors
    Else
        CreateEmf = 1  'If an error occurred,
        ' returns 1
    End If

    'sets the PictureBox Scalemode property to the previous mode
    SourceImage.ScaleMode = OldScale
    Return CreateEmf
End Function End Class
© www.soinside.com 2019 - 2024. All rights reserved.