Как сделать файл EMF dpi осведомленным
Я пытаюсь работать с файлами EMF, которые поддерживают dpi в Windows. Основная проблема заключается в том, что они не обрабатывают текст в формате dpi. Например, независимо от того, какое разрешение экрана или внутренняя эдс, графический объект, полученный из эдс, всегда сообщает 96 dpi. Это делает невозможным правильную обработку текста.
В моем случае мне дают размер изображения в дюймах, и мне нужно, чтобы мой рисунок соответствовал этому размеру. Чертеж представляет собой график XY с легендой, названием и названиями осей. Я использую результаты MeasureString, чтобы установить переменную заполнения для макета. Тот факт, что эдс не возвращает правильное значение dpi для своего графического объекта, приводит к тому, что текст постепенно ухудшается по мере того, как масштабирование / разрешение смещается дальше от 96, а также изменяется компоновка, потому что отступы не совпадают.
Вот код:
Сначала рассчитайте масштабный коэффициент настройки дисплея
// Calculate the scale setting
// Note: Changing the Display Settings scale changes the screen pixel resolution (HORZRES).
// The desktop res is the physical numbers of pixels, and the logical pixel is set at boot up and never changes.
System.Drawing.Graphics pGFX = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
IntPtr hemfDC = pGFX.GetHdc();
int dtX = GetDeviceCaps(hemfDC, DeviceCap.DESKTOPHORZRES);
int dtY = GetDeviceCaps(hemfDC, DeviceCap.DESKTOPVERTRES);
int scX = GetDeviceCaps(hemfDC, DeviceCap.HORZRES);
int scY = GetDeviceCaps(hemfDC, DeviceCap.VERTRES);
int lpX = GetDeviceCaps(hemfDC, DeviceCap.LOGPIXELSX);
int lpY = GetDeviceCaps(hemfDC, DeviceCap.LOGPIXELSY);
pGFX.ReleaseHdc();
int dpiX = (int)(((float)dtX / (float)scX) * lpX);
int dpiY = (int)(((float)dtY / (float)scY) * lpY);
float scaleX = ((((float)dtX / (float)scX) * lpX) / pGFX.DpiX);
float scaleY = ((((float)dtY / (float)scY) * lpY) / pGFX.DpiY);
Далее создайте метафайл
Metafile image = null;
MetafileHeader mfh = null;
SizeF emfSize = new SizeF(pageSize);
using (MemoryStream stream = new MemoryStream())
{
IntPtr hDC = gfx.GetHdc();
try
{
image = new Metafile(
stream,
hDC,
new RectangleF(new PointF(0, 0), emfSize),
MetafileFrameUnit.Inch,
EmfType.EmfPlusOnly);
}
finally
{
gfx.ReleaseHdc();
}
}
Наконец, вызовите код рисования. Код для рисования принимает размер в пикселях, а не в дюймах. Обратите внимание, что размер файла в пикселях - это размер определяемого пользователем масштабного коэффициента, где 100% предполагается равным g.Dpi (т.е. 96). Это предположение оказалось верным для всех мониторов, которые я тестировал.
if (image != null)
{
using (Graphics g = Graphics.FromImage(image))
{
mfh = image.GetMetafileHeader();
emfSize.Width *= g.DpiX * scaleX;
emfSize.Height *= g.DpiY * scaleY;
g.PageUnit = GraphicsUnit.Pixel;
g.ScaleTransform(mfh.DpiX / g.DpiX, mfh.DpiY / g.DpiY);
DrawGraph(g, ref emfSize);
}
}
1 ответ
Я обернул Graphic DrawString и MeasureString, чтобы временно масштабировать шрифт, используя рассчитанную шкалу настроек Display. Это не идеал, но лучший компромисс, который я могу найти.
private void DrawString(Graphics g, string s, Font font, Brush brush, PointF point)
{
Font f = new Font(font.FontFamily, font.SizeInPoints * Graph.FontScale, font.Style, GraphicsUnit.Point);
g.DrawString(s, f, brush, point);
}