2008-09-30 44 views

Respuesta

2

Un metarchivo es un archivo que registra una secuencia de operaciones GDI. Es escalable porque se captura la secuencia original de operaciones que generó la imagen y, por lo tanto, las coordenadas que se grabaron se pueden escalar.

Creo que, en .NET, debe crear un objeto Metafile, crear un objeto Graphics usando Graphics.FromImage, luego realice los pasos de su dibujo. El archivo se actualiza automáticamente a medida que dibujas sobre él. Puede encontrar una pequeña muestra en la documentación para Graphics.AddMetafileComment.

Si realmente desea almacenar un mapa de bits en un metarchivo, siga estos pasos y luego use Graphics.DrawImage para pintar el mapa de bits. Sin embargo, cuando se escala, se extenderá usando StretchBlt.

2

La pregunta era: "¿Hay alguna otra forma de guardar la imagen en un EMF/WMF?" No "qué es un metarchivo" o "cómo crear un metarchivo" o "cómo usar un metarchivo con gráficos".

Yo también busco respuesta a esta pregunta "cómo ahorrar EMF/WMF" De hecho si se utilizan:

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

En ambos casos la imagen se guarda como formato PNG. Y este es el problema que no puedo resolver:/

+0

tenga en cuenta que debe utilizar 'System.Drawing.Imaging' para' Metafile' y 'System.IO' para' MemoryStream'. –

9

Si no recuerdo mal, se puede hacer con una combinación de la Metafile.GetHenhmetafile(), los GetEnhMetaFileBits API() y Stream.Write(), algo así como

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

Creo que así es como resolví el problema cuando lo tenía.

+0

Si bien parece que esto debería funcionar, me parece que genera la versión rasterizada del metarchivo, no el vector GDI. Además, debe llamar a DeleteEnhMetaFile (h) y tener en cuenta que llamar a GetHenhMetaFile() coloca el objeto de metarchivo en un estado no válido. –

+0

Necesita llamar a DeleteEnhMetafile (h) cuando haya terminado y el objeto Metafile se ponga en un estado no válido mediante la llamada GetHenhMetafile. Si el metarchivo es un metarchivo basado en ráster, esto le dará un .emf basado en ráster en el disco. Si se trata de un metarchivo basado en vectores, obtendrá un .emf basado en vectores en el disco. Normalmente, al llamar a metarchivo.Guardar ("nombre de archivo", System.Drawing.Imaging.ImageFormat.Emf) obtendrá un archivo .png. –

2

La respuesta de erikkallen es correcta. He intentado esto desde VB.NET, y tuvo que usar 2 DllImports diferentes para conseguir que funcione:

<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 

La primera importación se utiliza para la primera llamada para obtener el tamaño fem. La segunda importación para obtener los bits reales. otra posibilidad es utilizar:

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

Esto copia los bits fem directamente al archivo llamado.

18

Image es una clase abstracta: lo que quiere hacer depende de si se trata de un Metafile o un Bitmap.

Crear una imagen con GDI + y guardarla como un EMF es simple con Metafile. de post por Mike:

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

Esto es lo que quiere hacer la mayor parte del tiempo, ya que es lo EMF es para: guardar las imágenes vectoriales en forma de órdenes de dibujo de GDI +.

Puede guardar Bitmap en un archivo EMF utilizando el método anterior y llamando al ig.DrawImage(your_bitmap), pero tenga en cuenta que esto no encubierta mágicamente sus datos ráster en una imagen vectorial.

+1

Maravilloso, había pasado por alto por completo esta forma fácil de crear un metarchivo basado en el constructor de metarchivos. Debo admitir que tener que pasar un HDC original es un poco confuso, pero sin embargo, está funcionando. –

0

Estaba buscando una forma de guardar las instrucciones GDI en un objeto Metafile en un archivo EMF. La publicación de Han me ayudó a resolver el problema. Esto fue antes de unirme a SOF. Gracias, Han. Esto es lo que I tried.

 
    [DllImport("gdi32.dll")] 
    static extern IntPtr CopyEnhMetaFile( // Copy EMF to file 
     IntPtr hemfSrc, // Handle to EMF 
     String lpszFile // File 
    ); 

    [DllImport("gdi32.dll")] 
    static extern int DeleteEnhMetaFile( // Delete EMF 
     IntPtr hemf // Handle to EMF 
    ); 

    // Code that creates the metafile 
    // Metafile metafile = ... 

    // Get a handle to the metafile 
    IntPtr iptrMetafileHandle = metafile.GetHenhmetafile(); 

    // Export metafile to an image file 
    CopyEnhMetaFile(
     iptrMetafileHandle, 
      "image.emf"); 

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

0

Parece que hay mucha confusión sobre el vector frente al mapa de bits. Todo el código en este subproceso genera archivos de mapa de bits (no vectoriales); no conserva las llamadas vectoriales GDI. Para demostrarte esto, descarga la herramienta "EMF Parser" e inspecciona los archivos de salida: http://downloads.zdnet.com/abstract.aspx?docid=749645.

Este problema ha causado que muchos desarrolladores consideren la angustia. Seguro que estaría bien si Microsoft solucionara esto y respaldara adecuadamente su propio formato EMF.

+1

Estás equivocado. La respuesta de user120789, por ejemplo, crea gráficos vectoriales. – reinierpost

+0

-1: como se mencionó 'reinierpost', la solución de user120789 crea un gráfico vectorial – user1027167

2

También es necesario cerrar el manejador CopyEnhMetaFile:

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

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

De lo contrario, no se puede eliminar el archivo, ya que sigue siendo utilizado por el proceso.

1

Recomendaría evitar tales extern y no administrados cruft en una aplicación .NET administrada. En su lugar, me gustaría recomendar algo un poco más como la solución administrada dada en este tema:

Convert an image into WMF with .NET?

P. S. Estoy respondiendo este hilo viejo porque esta fue la mejor respuesta que encontré, pero luego terminé desarrollando una solución administrada, que luego me llevó al enlace de arriba. Entonces, para salvar a los demás esa vez, pensé que podría apuntar este a esa.

+0

gracias por el enlace, había estado buscando una solución durante casi una semana ... – David

Cuestiones relacionadas