如果使用 Image.Save 方法将图像保存到 EMF/WMF,则会出现异常 (http://msdn.microsoft.com/en-us/library/ktx83wah.aspx)
是否有其他方法将图像保存为 EMF/WMF? 有可用的编码器吗?
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 文件中,但请注意,这不会神奇地将光栅数据转换为矢量图像。
如果我没记错的话,可以通过 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.
我想这就是我遇到问题时解决问题的方法。
图元文件是记录一系列 GDI 操作的文件。它是可扩展的,因为生成图片的原始操作序列被捕获,因此记录的坐标可以缩放。
我认为,在 .NET 中,您应该创建一个
Metafile
对象,使用 Graphics
创建一个 Graphics.FromImage
对象,然后执行绘图步骤。当您在文件上绘图时,该文件会自动更新。您可以在 Graphics.AddMetafileComment 的文档中找到一个小示例。
如果您确实想在图元文件中存储位图,请使用这些步骤,然后使用
Graphics.DrawImage
绘制位图。但是,当缩放时,它将使用 StretchBlt
进行拉伸。
问题是:“是否有其他方法将图像保存为 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 格式。这是我无法解决的问题:/
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 位直接复制到指定文件中。
您还需要关闭
CopyEnhMetaFile
处理程序:
IntPtr ptr2 = CopyEnhMetaFile(iptrMetafileHandle, "image.emf");
DeleteEnhMetaFile(ptr2);
// Delete the metafile from memory
DeleteEnhMetaFile(iptrMetafileHandle);
否则,您无法删除该文件,因为该文件仍在被进程使用。
我正在寻找一种将图元文件对象中的 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);
我建议避免在托管 .NET 应用程序中出现此类外部和非托管的问题。 相反,我会推荐一些更像此线程中给出的托管解决方案的东西:
附注我回答这个旧线程是因为这是我找到的最佳答案,但最终开发了一个托管解决方案,然后引导我到上面的链接。因此,为了节省其他人的时间,我想我应该将这个指向那个。
矢量与位图之间似乎存在很多混淆。该线程中的所有代码都会生成位图(非矢量)文件 - 它不保留矢量 GDI 调用。要向自己证明这一点,请下载“EMF Parser”工具并检查输出文件:http://downloads.zdnet.com/abstract.aspx?docid=749645.
这个问题引起了很多开发者的苦恼。如果微软能解决这个问题并正确支持他们自己的 EMF 格式,那就太好了。
这是一个公共 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