I am struggling trying to work with emf files that are dpi aware on Windows. The main problem, comes from the fact that they do not handle text in a dpi aware manner. For instance, no matter what the resolution of the screen, or the emf internally, the graphics object obtained from the emf always reports 96 dpi. This makes it impossible to handle text correctly.
In my case I am given an image size in inches and I need to make my drawing fit this size. The drawing is an X-Y graph with a legend, title and axis names. I use the results of MeasureString to set a padding variable for the layout. The fact that the emf is not returning the correct dpi to it's Graphic object results in text that progressively gets worse as the scaling/resolution moves farther from 96 and the layout changes as well because the padding is not the same.
Here is the code:
First calculate the Display Setting scale factor
// 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);
Next create the metafile
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();
}
}
Finally, call the drawing code. The drawing code takes a size in pixels, not inches. Note that the size of the file, in pixels, is the size of the user defined scale factor, where 100% is assumed to be g.Dpi (i.e. 96). This assumption has proven to be correct for all the monitors I have tested.
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);
}
}