GDI + / C #: как сохранить изображение в формате EMF? - PullRequest
19 голосов
/ 30 сентября 2008

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

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

Ответы [ 9 ]

21 голосов
/ 11 июня 2009

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

12 голосов
/ 09 апреля 2009

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

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

3 голосов
/ 30 сентября 2008

Метафайл - это файл, в котором записана последовательность операций GDI. Это масштабируемо, потому что исходная последовательность операций, которая генерировала изображение, захвачена, и, следовательно, записанные координаты могут быть масштабированы.

Я думаю, что в .NET вы должны создать объект Metafile, создать объект Graphics, используя Graphics.FromImage, а затем выполнить шаги рисования. Файл автоматически обновляется, когда вы рисуете на нем. Вы можете найти небольшой образец в документации для Graphics.AddMetafileComment .

Если вы действительно хотите сохранить растровое изображение в метафайле, используйте эти шаги, затем используйте Graphics.DrawImage для рисования растрового изображения. Однако, когда он масштабируется, он растягивается с помощью StretchBlt.

2 голосов
/ 09 мая 2012

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

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

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

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

2 голосов
/ 22 мая 2009

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

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

1 голос
/ 17 августа 2012

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

Преобразование изображения в WMF с помощью .NET?

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

0 голосов
/ 01 апреля 2011

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

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

0 голосов
/ 03 марта 2011

Я искал способ сохранить инструкции GDI в объекте метафайла в файл 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);

...