GDI +/С#: как сохранить изображение как EMF?

Если вы используете Image.Save Method для сохранения изображения в EMF/WMF, вы получаете исключение (http://msdn.microsoft.com/en-us/library/ktx83wah.aspx)

Есть ли другой способ сохранить изображение в EMF/WMF? Доступны ли какие-либо кодеры?

Ответы

Ответ 1

Image - абстрактный класс: то, что вы хотите сделать, зависит от того, имеете ли вы дело с Metafile или Bitmap.

Создание изображения с помощью GDI + и сохранение его как EMF прост с помощью Metafile. Per Mike post:

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 в файл EMF с помощью вышеуказанного метода и вызвать ig.DrawImage(your_bitmap), но имейте в виду, что это не волшебным образом скрывает ваши растровые данные в векторное изображение.

Ответ 2

Если я правильно помню, это можно сделать с помощью комбинации 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.

Ответ 4

Вопрос был: "Есть ли другой способ сохранить изображение в 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. И это проблема, которую я не могу решить:/

Ответ 5

Ответ 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

Первый импорт используется для первого вызова, чтобы получить размер emf. Второй импорт, чтобы получить фактические биты. В качестве альтернативы вы можете использовать:

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

Это копирует emf-биты непосредственно в указанный файл.

Ответ 6

Вам также нужно закрыть обработчик CopyEnhMetaFile:

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

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

В противном случае вы не сможете удалить файл, поскольку он все еще используется процессом.

Ответ 7

Я бы рекомендовал избегать таких внешних и неуправляемых ошибок в управляемом приложении .NET. Вместо этого я бы рекомендовал нечто большее, чем управляемое решение, данное в этом потоке:

Преобразование изображения в WMF с .NET?

P.S. Я отвечаю на эту старую нить, потому что это был лучший ответ, который я нашел, но затем закончил разработку управляемого решения, которое затем привело меня к ссылке выше. Таким образом, чтобы сохранить других в то время, я решил, что я укажу этот на этот.

Ответ 8

Я искал способ сохранить инструкции GDI в объекте Metafile в файле EMF. Хан пост помог мне решить эту проблему. Это было до того, как я присоединился к SOF. Спасибо, Хан. Вот что Я пробовал.

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

Ответ 9

Похоже, что существует много путаницы в отношении векторного и битового карт. Весь код в этом потоке генерирует растровые (не-векторные) файлы - он не сохраняет векторные вызовы GDI. Чтобы доказать это самому себе, загрузите инструмент "EMF Parser" и проверьте выходные файлы: http://downloads.zdnet.com/abstract.aspx?docid=749645.

Эта проблема заставила многих разработчиков рассказать о мучениях. Конечно, было бы неплохо, если бы Microsoft исправила это и правильно поддержала свой собственный формат EMF.