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

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

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

9 ответов

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

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

Я думаю, что именно так я решил проблему, когда у меня это было.

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

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

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

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

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

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

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

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

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

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

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

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

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

Я искал способ сохранить инструкции 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();

   // Экспорт метафайла в файл изображения
   CopyEnhMetaFile(
         iptrMetafileHandle, 
          "Image.emf");

   // Удалить метафайл из памяти
   DeleteEnhMetaFile(iptrMetafileHandle);

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

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

Другие вопросы по тегам