Excel如何计算复制范围时生成的元文件的分辨率“如屏幕上所示”?

时间:2011-11-29 20:30:16

标签: c# excel copy metafile

我有一些来自http://bytes.com/topic/c-sharp/answers/572657-net-clipboard-metafiles的C#代码,它在以下两个设置下复制一个单元格范围:

  • 如屏幕所示,
  • 如打印时所示

当我查看生成的图元文件的分辨率(记录为Gets the resolution, in pixels-per-inch, of this Image object)时,我会根据复制方法获得不同的值。

  • 使用打印时显示选项,分辨率为600,我相信这与我在Excel中的DPI设置相对应。

  • 使用屏幕上显示的设置,会显示Metafile.VerticalResolution = 72.08107Metafile.HorizontalResolution = 71.95952之类的内容。在其他机器上,我看到这个值变化很大(值大约在111,130等)。

缩放级别似乎不会影响这一点。根据我的观察,这些值在一台机器上保持一致,但可能因机器而异。

任何人都可以解释在计算中图元文件的分辨率时Excel遵循的逻辑如屏幕模式所示?

更改Windows分辨率并测量元文件分辨率后,这是我生成的表格(希望它看起来格式正确):

Width   Height  HorizontalResolution    VerticalResolution
1680    1050        71.95952                72.08107
1600    1024        72.05672                72.04874
1600    900         72.05672                71.88678
1360    768         71.96666                71.98228
1280    1024        71.9292                 72.04874
1280    960         71.9292                 71.9292
1280    800         71.9292                 72.05672
1280    768         71.9292                 71.98228
1280    720         71.9292                 71.99999
1152    864         72.07093                71.95278
1088    612         71.96666                71.96666
1024    768         72.04874                71.98228
960     600         71.9292                 71.88678
800     600         72.05672                71.88678

在虚拟机(同一物理机器)上运行类似的过程后,结果就是这些。比物理机本身更具波动性。这些数据可能没有用,但我想我还是会提供它。

Width   Height  HorizontalResolution    VerticalResolution
1680    1050    133.35                  111.125
1280    800     101.6                   84.66666
1024    768     81.27999                81.27999
800     600     63.5                    63.5

1 个答案:

答案 0 :(得分:4)

我的假设

我认为这与在液晶显示器上运行非原生分辨率有关。在“过去”中,CRT本身没有原生分辨率。因此,计算机不知道给定监视器大小或宽高比的任何首选分辨率。使用较新的数字显示器(LCD),如果正确安装,计算机现在可以了解显示器的首选分辨率和宽高比。我的Windows 7机器在我的LCD原始分辨率旁边显示“推荐”,然后以黑色显示其他2个相等的宽高比分辨率,剩余的“不匹配”未标记但可选择(导致我不喜欢看到的压扁或拉伸的外观在其他人的电脑上!)。

Windows中默认的DPI为96和120是在CRT日期建立的。我的Windows 7机器甚至不再说DPI它只是说“更小”,“中等”,“更大”。

无论哪种方式,当您购买LCD显示器(例如1920x1080或1920x1200)但将显示分辨率设置为更小时,您会产生转换因子。如果不匹配的水平和垂直分辨率接近72,则非原生显示分辨率可能与垂直分割因子不完全相同,因为它是水平的,导致这种小的差异。

如何测试我的假设

在每台测试计算机上记录操作系统配置的分辨率并显示原始分辨率。看看这两者之间的比率是否接近你的图元文件'在屏幕上'与96或120dpi之间的比率。我希望您在物理机上进行此测试,以简单地排除使用远程桌面或虚拟机驱动程序进一步扩展的可能性。

如果解决方案没有立即显现,请更进一步记录DPI或“较小”,“中等”和“较大”的操作和控制面板设置。 Windows XP的行为可能与Windows Vista / Windows 7不同。

您还可以在同一台物理计算机上多次重新运行测试,调整测试之间配置的显示分辨率并观察任何更改。如果我的假设是正确的,你应该在同一个物理机器/显示器组合上看到每个配置的显示分辨率的不同元文件分辨率,这个结果应该是可预测和可重复的(返回第一个分辨率应该返回相同的元文件分辨率)

编辑#1

我发现了一篇很好的文章,讨论了物理DPI与逻辑DPI。请仔细阅读:Where does 96 DPI come from in Windows?

所以现在我推荐的下一个测试是改变显示!您是否有可用于测试的不同品牌/尺寸/分辨率LCD显示器?你不需要像上面第一次测试那么多线,因为我们已经建立了各种分辨率,往往会为同一个显示产生非常相似的DPI。只测试一些常见的分辨率,包括显示器的原始分辨率和1024x768作为比较的“基线”。

另外,在Windows 7中,我确实发现控制面板中的“设置自定义文本大小(DPI)”链接 - >显示,其中包括“使用Windows XP样式DPI缩放”选项。虽然我不认为这是主要关注点,但好奇心让我对它的影响感兴趣,所以我想我会提到它。

编辑#2 - 已解决!

您在元文件中看到的分辨率是显示器的物理DPI。我将在这里发布一些C#代码供您自己测试:

[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

public enum DeviceCap
{
    /// <summary>
    /// Device driver version
    /// </summary>
    DRIVERVERSION = 0,
    /// <summary>
    /// Device classification
    /// </summary>
    TECHNOLOGY = 2,
    /// <summary>
    /// Horizontal size in millimeters
    /// </summary>
    HORZSIZE = 4,
    /// <summary>
    /// Vertical size in millimeters
    /// </summary>
    VERTSIZE = 6,
    /// <summary>
    /// Horizontal width in pixels
    /// </summary>
    HORZRES = 8,
    /// <summary>
    /// Vertical height in pixels
    /// </summary>
    VERTRES = 10,
    /// <summary>
    /// Number of bits per pixel
    /// </summary>
    BITSPIXEL = 12,
    /// <summary>
    /// Number of planes
    /// </summary>
    PLANES = 14,
    /// <summary>
    /// Number of brushes the device has
    /// </summary>
    NUMBRUSHES = 16,
    /// <summary>
    /// Number of pens the device has
    /// </summary>
    NUMPENS = 18,
    /// <summary>
    /// Number of markers the device has
    /// </summary>
    NUMMARKERS = 20,
    /// <summary>
    /// Number of fonts the device has
    /// </summary>
    NUMFONTS = 22,
    /// <summary>
    /// Number of colors the device supports
    /// </summary>
    NUMCOLORS = 24,
    /// <summary>
    /// Size required for device descriptor
    /// </summary>
    PDEVICESIZE = 26,
    /// <summary>
    /// Curve capabilities
    /// </summary>
    CURVECAPS = 28,
    /// <summary>
    /// Line capabilities
    /// </summary>
    LINECAPS = 30,
    /// <summary>
    /// Polygonal capabilities
    /// </summary>
    POLYGONALCAPS = 32,
    /// <summary>
    /// Text capabilities
    /// </summary>
    TEXTCAPS = 34,
    /// <summary>
    /// Clipping capabilities
    /// </summary>
    CLIPCAPS = 36,
    /// <summary>
    /// Bitblt capabilities
    /// </summary>
    RASTERCAPS = 38,
    /// <summary>
    /// Length of the X leg
    /// </summary>
    ASPECTX = 40,
    /// <summary>
    /// Length of the Y leg
    /// </summary>
    ASPECTY = 42,
    /// <summary>
    /// Length of the hypotenuse
    /// </summary>
    ASPECTXY = 44,
    /// <summary>
    /// Shading and Blending caps
    /// </summary>
    SHADEBLENDCAPS = 45,

    /// <summary>
    /// Logical pixels inch in X
    /// </summary>
    LOGPIXELSX = 88,
    /// <summary>
    /// Logical pixels inch in Y
    /// </summary>
    LOGPIXELSY = 90,

    /// <summary>
    /// Number of entries in physical palette
    /// </summary>
    SIZEPALETTE = 104,
    /// <summary>
    /// Number of reserved entries in palette
    /// </summary>
    NUMRESERVED = 106,
    /// <summary>
    /// Actual color resolution
    /// </summary>
    COLORRES = 108,

    // Printing related DeviceCaps. These replace the appropriate Escapes
    /// <summary>
    /// Physical Width in device units
    /// </summary>
    PHYSICALWIDTH = 110,
    /// <summary>
    /// Physical Height in device units
    /// </summary>
    PHYSICALHEIGHT = 111,
    /// <summary>
    /// Physical Printable Area x margin
    /// </summary>
    PHYSICALOFFSETX = 112,
    /// <summary>
    /// Physical Printable Area y margin
    /// </summary>
    PHYSICALOFFSETY = 113,
    /// <summary>
    /// Scaling factor x
    /// </summary>
    SCALINGFACTORX = 114,
    /// <summary>
    /// Scaling factor y
    /// </summary>
    SCALINGFACTORY = 115,

    /// <summary>
    /// Current vertical refresh rate of the display device (for displays only) in Hz
    /// </summary>
    VREFRESH = 116,
    /// <summary>
    /// Horizontal width of entire desktop in pixels
    /// </summary>
    DESKTOPVERTRES = 117,
    /// <summary>
    /// Vertical height of entire desktop in pixels
    /// </summary>
    DESKTOPHORZRES = 118,
    /// <summary>
    /// Preferred blt alignment
    /// </summary>
    BLTALIGNMENT = 119
}

private void GetScreenInfo()
{
    IntPtr sdc = IntPtr.Zero;
    try
    {
        //Get the Screen Device Context
        sdc = GetDC(IntPtr.Zero);

        // Get the Screen Devive Context Capabilities Information
        Console.WriteLine(string.Format("Size: {0} mm X {1} mm", GetDeviceCaps(sdc, (int)DeviceCap.HORZSIZE), GetDeviceCaps(sdc, (int)DeviceCap.VERTSIZE)));
        Console.WriteLine(string.Format("Desktop Resolution: {0}x{1}", GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPHORZRES), GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPVERTRES)));
        Console.WriteLine(string.Format("Logical DPI: {0}x{1}", GetDeviceCaps(sdc, (int)DeviceCap.LOGPIXELSX), GetDeviceCaps(sdc, (int)DeviceCap.LOGPIXELSY)));

        //Remember: Convert Millimeters to Inches 25.4mm = 1 inch
        double PhsyicalDPI_X = GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPHORZRES) * 25.4 / GetDeviceCaps(sdc, (int)DeviceCap.HORZSIZE);
        double PhsyicalDPI_Y = GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPVERTRES) * 25.4 / GetDeviceCaps(sdc, (int)DeviceCap.VERTSIZE);
        Console.WriteLine(string.Format("Physical DPI: {0}x{1}", PhsyicalDPI_X, PhsyicalDPI_Y));

    }
    finally
    {
        ReleaseDC(IntPtr.Zero, sdc);
    }
}

我的显示屏上的此代码输出以下内容:

  • 尺寸:677毫米×381毫米
  • 桌面分辨率:1920x1080
  • 逻辑DPI:96x96
  • 物理DPI:72.0354505169867x72

注意逻辑和物理DPI?这个物理DPI看起来很熟悉吗?在阅读有关反映1pt = 1px的72dpi的文章后,这一切都有意义。试试这个代码你的各种测试机器,让我知道它是怎么回事! (仅供参考,我在C#winforms应用程序中运行此代码,控制台应用能够获取屏幕设备上下文,但可能不会......)