Конвертировать изображение в WMF с помощью.NET?
Существует множество примеров преобразования wmf в растровое изображение, например: Надежное.wmf/wmf для преобразования изображений на основе пикселей
Но мне нужна обратная операция. Я не ищу векторизатор. Я просто хочу встроить картинку в файл wmf, не беспокоясь о битах и байтах формата wmf. Мне нужно решение для.NET, предпочтительно в C#.
Сначала я подумал, что это сделает работу:
using (Image img = Image.FromFile (path)) {
img.Save (myStream, System.Drawing.Imaging.ImageFormat.Wmf);
}
Но это жалуется во время выполнения, что кодировщик нулевой. Где / Как я могу построить такой кодировщик? Мне не нужен сложный, просто тот, который оборачивает изображение в wmf. Есть ли какие-то требования к поддерживаемым форматам в WMF? Я полагаю, png и bmp поддерживаются, но также поддерживается gif?
4 ответа
При использовании метода "Сохранить" для сохранения графического изображения в виде файла формата метафайла Windows (WMF) или расширенного метафайла (EMF) полученный файл сохраняется в виде файла Portable Network Graphics (PNG). Это происходит потому, что компонент GDI+.NET Framework не имеет кодировщика, который можно использовать для сохранения файлов в формате.wmf или.emf.
Но я думаю, вы уже зашли так далеко:)
Здесь кто-то помещает растровое изображение в FileStream.
metafileStream = MakeMetafileStream(gdiBitmap);
с MakeMetafileStream(), являющимся:
private static MemoryStream MakeMetafileStream(Bitmap image)
{
Graphics graphics = null;
Metafile metafile= null;
var stream = new MemoryStream();
try
{
using (graphics = Graphics.FromImage(image))
{
var hdc = graphics.GetHdc();
metafile= new Metafile(stream, hdc);
graphics.ReleaseHdc(hdc);
}
using (graphics = Graphics.FromImage(metafile))
{ graphics.DrawImage(image, 0, 0); }
}
finally
{
if (graphics != null)
{ graphics.Dispose(); }
if (metafile!= null)
{ metafile.Dispose(); }
}
return stream;
}
Интересные вещи. Но что касается вещи с кодировщиком...
Вот Питер Хуанг из MS опубликовал этот неуправляемый подход:
[DllImport("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits (IntPtr _hEmf, uint _bufferSize,
byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
[DllImport("gdi32.dll")]
private static extern IntPtr SetMetaFileBitsEx (uint _bufferSize,
byte[] _buffer);
[DllImport("gdi32.dll")]
private static extern IntPtr CopyMetaFile (IntPtr hWmf,
string filename);
[DllImport("gdi32.dll")]
private static extern bool DeleteMetaFile (IntPtr hWmf);
[DllImport("gdi32.dll")]
private static extern bool DeleteEnhMetaFile (IntPtr hEmf);
private void button4_Click(object sender, System.EventArgs e)
{
Graphics g= this.CreateGraphics();
IntPtr hDC = g.GetHdc();
Metafile mf = new Metafile(hDC,EmfType.EmfOnly);
g.ReleaseHdc(hDC);
g.Dispose();
g=Graphics.FromImage(mf);
//Pen p = new Pen(Color.White,5);
g.DrawArc(Pens.Black,0,0,200,200,0,360);
//g.DrawImage(Bitmap.FromFile(@"c:\temp\test.bmp"),0,0);
g.Dispose();
IntPtr _hEmf= mf.GetHenhmetafile();
uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
byte[] _buffer = new byte[_bufferSize];
GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
IntPtr hmf = SetMetaFileBitsEx(_bufferSize, _buffer);
CopyMetaFile(hmf, "C:\\ConvertedMetafile.wmf");
DeleteMetaFile(hmf);
DeleteEnhMetaFile(_hEmf);
}
Надеюсь, что вы попадете туда:)
Вот полный ответ на вопрос, включая мои модификации. Ответ Винсента полностью правильный. Только некоторые определения и одно перечисление отсутствовали. Вот почему я публикую здесь "чистый" рабочий код в надежде, что он будет полезен для кого-то еще.
[Flags]
private enum EmfToWmfBitsFlags {
EmfToWmfBitsFlagsDefault = 0x00000000,
EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
EmfToWmfBitsFlagsNoXORClip = 0x00000004
}
private static int MM_ISOTROPIC = 7;
private static int MM_ANISOTROPIC = 8;
[DllImport ("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits (IntPtr _hEmf, uint _bufferSize,
byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
[DllImport ("gdi32.dll")]
private static extern IntPtr SetMetaFileBitsEx (uint _bufferSize,
byte[] _buffer);
[DllImport ("gdi32.dll")]
private static extern IntPtr CopyMetaFile (IntPtr hWmf,
string filename);
[DllImport ("gdi32.dll")]
private static extern bool DeleteMetaFile (IntPtr hWmf);
[DllImport ("gdi32.dll")]
private static extern bool DeleteEnhMetaFile (IntPtr hEmf);
private static MemoryStream MakeMetafileStream (Bitmap image)
{
Metafile metafile = null;
using (Graphics g = Graphics.FromImage (image)) {
IntPtr hDC = g.GetHdc ();
metafile = new Metafile (hDC, EmfType.EmfOnly);
g.ReleaseHdc (hDC);
}
using (Graphics g = Graphics.FromImage (metafile)) {
g.DrawImage (image, 0, 0);
}
IntPtr _hEmf = metafile.GetHenhmetafile ();
uint _bufferSize = GdipEmfToWmfBits (_hEmf, 0, null, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
byte[] _buffer = new byte[_bufferSize];
GdipEmfToWmfBits (_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
IntPtr hmf = SetMetaFileBitsEx (_bufferSize, _buffer);
string tempfile = Path.GetTempFileName ();
CopyMetaFile (hmf, tempfile);
DeleteMetaFile (hmf);
DeleteEnhMetaFile (_hEmf);
var stream = new MemoryStream ();
byte[] data = File.ReadAllBytes (tempfile);
//File.Delete (tempfile);
int count = data.Length;
stream.Write (data, 0, count);
return stream;
}
Улучшенная версия того, что опубликовал jdehaan (спасибо ему и Винсенту)
[Flags]
private enum EmfToWmfBitsFlags
{
EmfToWmfBitsFlagsDefault = 0x00000000,
EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
EmfToWmfBitsFlagsNoXORClip = 0x00000004
}
private static int MM_ISOTROPIC = 7;
private static int MM_ANISOTROPIC = 8;
[DllImport("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
[DllImport("gdi32.dll")]
private static extern IntPtr SetMetaFileBitsEx(uint _bufferSize,
byte[] _buffer);
[DllImport("gdi32.dll")]
private static extern IntPtr CopyMetaFile(IntPtr hWmf,
string filename);
[DllImport("gdi32.dll")]
private static extern bool DeleteMetaFile(IntPtr hWmf);
[DllImport("gdi32.dll")]
private static extern bool DeleteEnhMetaFile(IntPtr hEmf);
public static MemoryStream MakeMetafileStream(System.Drawing.Bitmap image)
{
Metafile metafile = null;
using (Graphics g = Graphics.FromImage(image))
{
IntPtr hDC = g.GetHdc();
metafile = new Metafile(hDC, EmfType.EmfOnly);
g.ReleaseHdc(hDC);
}
using (Graphics g = Graphics.FromImage(metafile))
{
g.DrawImage(image, 0, 0);
}
IntPtr _hEmf = metafile.GetHenhmetafile();
uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
byte[] _buffer = new byte[_bufferSize];
GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
DeleteEnhMetaFile(_hEmf);
var stream = new MemoryStream();
stream.Write(_buffer, 0, (int)_bufferSize);
stream.Seek(0, 0);
return stream;
}
Этот не оставляет временных файлов позади, а также избегает копирования _bufferSize во временный файл только для того, чтобы затем скопировать его в другой буфер. Еще раз спасибо, ребята.
Вот пример Win32 GDI+, который работал для меня (см. http://www.codeproject.com/Articles/6879/How-to-use-GDI-to-save-image-in-WMF-EXIF-or-EMF-fo)
Bitmap *image;
image = Bitmap::FromFile(L"in.jpg"); // read in the JPG
HDC hdc = GetDC(hwnd); // parent window
Metafile *metafile = new Metafile(L"out.wmf", hdc);
Graphics *graphics = new Graphics(metafile);
graphics->DrawImage(image, 0, 0, image->GetWidth(), image->GetHeight());
delete graphics; delete metafile; delete image;
ReleaseDC(hwnd, hdc);