如何使 emf 文件 dpi 感知

Posted

技术标签:

【中文标题】如何使 emf 文件 dpi 感知【英文标题】:How to make emf file dpi aware 【发布时间】:2019-02-02 00:52:02 【问题描述】:

我正在努力尝试在 Windows 上使用可识别 dpi 的 emf 文件。主要问题来自于他们不以 dpi 感知方式处理文本。例如,无论屏幕的分辨率如何,或者内部的电动势如何,从电动势获得的图形对象总是报告 96 dpi。这使得无法正确处理文本。

在我的例子中,我得到了一个以英寸为单位的图像尺寸,我需要使我的绘图适合这个尺寸。绘图是带有图例、标题和轴名称的 X-Y 图形。我使用 MeasureString 的结果为布局设置填充变量。 emf 没有将正确的 dpi 返回给它的 Graphic 对象这一事实导致文本随着缩放/分辨率远离 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,以便使用计算的显示设置的比例临时缩放字体。这并不理想,但却是我能找到的最佳折衷方案。

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);

【讨论】:

以上是关于如何使 emf 文件 dpi 感知的主要内容,如果未能解决你的问题,请参考以下文章

如何创建与 Windows 7 向后兼容的缩放和大小更改的 Per-Monitor DPI 感知应用程序?

创建 DPI 感知应用程序

SetWindowPos()跨进程DPI感知

创建后更改窗口的 dpi 感知

设置进程 DPI 感知,以便系统补偿缩放因子

稍后更改组合框的项目高度(用于 DPI 感知)