Как правильно масштабировать рисунок для EMF, используя gdi+
Моя программа Windows C++ создает EMF (расширенный формат метафайлов) для экспорта в буфер обмена, Word и Excel.
В следующем примере кода создается EMF-прямоугольник (ширина = высота =25), который составляет всего 12x12, а холст - 25x25 (примечание: разрешение экрана моего ноутбука составляет 3600x1800). При других разрешениях экрана возникают похожие аномалии масштабирования (слишком большие / слишком маленькие). Казалось бы, масштабирование графического рисунка должно быть установлено как функция разрешения. У меня явно есть пробел в моих знаниях здесь... любая помощь приветствуется.
HDC ref_dc = GetDC(NULL);
Rect r(0, 0, 25, 25);
Metafile* emf = new Metafile(ref_dc, r, MetafileFrameUnitPixel, EmfTypeEmfPlusDual, L"Drawing");//to HDC
Graphics* g = new Graphics(emf);
//draw a simple box
Gdiplus::Pen* pen = new Pen(Color(0, 255, 0), 1.0f);
pen->SetDashStyle(DashStyleSolid);
pen->SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g->DrawRectangle(pen, r); // DrawMyObject(g);
// code here to put on clipboard
2 ответа
Хотя это не имеет отношения к вашей реальной проблеме, я чувствую себя очень вынужденным отметить, что ваш стиль программирования создания всех объектов в куче довольно странный. Нет причин использовать new
как это. Просто создайте временные объекты в стеке. Это удерживает вас от утечки памяти и других ресурсов, таких как сито. В качестве иллюстрации, все, что вам нужно, это:
// Create Graphics object
Graphics g(emf);
// Draw a simple box
Gdiplus::Pen pen(Color(0, 255, 0), 1.0f);
pen.SetDashStyle(DashStyleSolid);
pen.SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g.DrawRectangle(pen, r);
// g and pen automatically go out of scope here, implicitly calling the destructor
// and freeing their resources. No need to call delete.
Что касается вашего реального вопроса, метафайлы не имеют фиксированного размера. Они в основном просто инкапсулируют серию инструкций рисования GDI, которые могут быть пересмотрены по желанию.
Обычный способ поместить расширенный метафайл в буфер обмена - это вызвать SetClipboardData
функция, используя CF_ENHMETAFILE
формат. Тип ручки, очевидно, будет HENHMETAFILE
, Вам нужно получить GDI+, чтобы дать вам один из них, вероятно, с помощью Metafile::GetHENHMETAFILE
метод после завершения загрузки / создания объекта метафайла.
За масштабирование / определение размера отвечает клиентский код, который получает ваш метафайл из буфера обмена и пытается его отобразить. Заголовок метафайла содержит запись, которая определяет его горизонтальное и вертикальное разрешение. Это можно затем масштабировать с точки зрения DPI дисплея. В GDI + что-то вроде:
Graphics g(...);
Metafile mf(L"MyFile.emf");
MetafileHeader mfh;
mf->GetMetafileHeader(&mfh);
REAL xScale = mfh->GetDpiX() / g.GetDpiX();
REAL yScale = mfh->GetDpiY() / g.GetDpiY();
g.ScaleTransform(xScale, yScale);
Самостоятельный ответ:
Графика создается с "новым", потому что она должна быть уничтожена до того, как EMF будет "записано". В противном случае эдс не "записывает" шаги, выполняемые графическим объектом. Возможно, мы должны поставить дополнительный набор скобок, но это было бы одинаково неловко. Согласно документации: "Запись заканчивается, когда графический объект удаляется или выходит из области видимости". Отсюда явные new(s) и delete(s). EMF выталкивают в буфер обмена после уничтожения графики и до уничтожения EMF.
* Ошибка / проблема, кажется, вызвана использованием конструктора для EMF, который включает структуру Rect в качестве аргумента. Измерения Rect, по-видимому, не имеют заметной связи с координатами объекта графов, используемыми для рисования, и, таким образом, он обрезает результирующую ЭДС непредсказуемым образом. Использование конструктора только с HDC или HDC и именем файла решает эту проблему, по крайней мере, в моих руках.
Наконец, добавление строк кода
REAL xScale = mfh->GetDpiX() / g.GetDpiX(); REAL yScale = mfh->GetDpiY() / g.GetDpiY(); g.ScaleTransform(xScale, yScale);
обеспечивает базовое масштабирование таким образом, что размер эдс более или менее одинаковый независимо от разрешения экрана. Это очень полезно, чтобы дать согласованный / разумный размер по умолчанию для экспорта на стороне пользователя.