首页 文章

指定GDI设备上下文的DPI

提问于
浏览
12

我有一个生成元文件(EMF)的应用程序 . 它使用参考设备(也就是屏幕)来渲染这些元文件,因此元文件的DPI会根据运行代码的机器而改变 .

假设我的代码打算创建一个8.5英寸x 11英寸的图元文件 . 使用我的开发工作站作为参考,我最终获得了一个EMF

  • {0,0,21590,27940}的rclFrame(图元文件的尺寸,以千分之一毫米为单位)

  • 一个{1440,900}的szlDevice(参考设备的尺寸,以像素为单位)

  • 一个{416,260}的szlMillimeters(参考设备的尺寸,单位mm)

好的,所以rclFrame告诉我EMF的大小应该是

  • 21590/2540 = 8.5宽

  • 27940/2540 = 11英寸高

好吧 . 如果我的数学是正确的,使用这些信息,我们也可以确定我的显示器的物理DPI:

  • (1440 * 25.4)/ 416 = 87.9231水平dpi

  • (900 * 25.4)/ 260 = 87.9231垂直dpi

问题

播放此元文件的任何内容 - EMF到PDF转换,在Windows资源管理器中右键单击EMF时的“摘要”页面等 - 似乎截断计算的DPI值,显示87而不是87.9231(甚至88会很好) .

这样,当播放元文件时,页面的物理大小为8.48 x 10.98 in(使用87 dpi)而不是8.5 in x 11 in(使用88 dpi) .

  • 是否可以更改参考设备的DPI,以便存储在用于计算DPI的图元文件中的信息出现一个很好的整数?

  • 我可以创建自己的设备上下文并指定其DPI吗?或者我真的必须使用打印机来做到这一点?

感谢您的任何见解 .

4 回答

  • 0

    请注意,我一直在WXP上以120 dpi运行(大字体),这意味着metafileGraphics.DpiX将返回120 .

    EMF文件似乎不记录参考上下文的dpi(在这种情况下为120,对于大多数其他人来说为96) .

    为了使事情更有趣,可以通过绘制到已将SetResolution()设置为300dpi的内存位图来创建EMF . 在这种情况下,我认为缩放因子必须是300而不是监视器(86.x)或Windows(120)可能使用的 .

  • 16

    似乎摘要页面上的值是错误的 . 他们计算如下:

    Size = round(precize_size)+1
    Resolution = trunc(precize_resolution)
    

    在没有舍入或截断的情况下计算精确值的位置 .

  • 0

    我现在学到的东西比我关心的metafiles还要多 .

    1.某些Metafile类的构造函数重载效果不佳,并且会在截断的DPI值上运行 .

    考虑以下:

    protected Graphics GetNextPage(SizeF pageSize)
    {
        IntPtr deviceContextHandle;
        Graphics offScreenBufferGraphics;
        Graphics metafileGraphics;
        MetafileHeader metafileHeader;
    
        this.currentStream = new MemoryStream();
        using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
        {
            deviceContextHandle = offScreenBufferGraphics.GetHdc();
            this.currentMetafile = new Metafile(
                this.currentStream,
                deviceContextHandle,
                new RectangleF(0, 0, pageSize.Width, pageSize.Height),
                MetafileFrameUnit.Inch,
                EmfType.EmfOnly);
    
            metafileGraphics = Graphics.FromImage(this.currentMetafile);
    
            offScreenBufferGraphics.ReleaseHdc();
        }
    
        return metafileGraphics;
    }
    

    如果你传入 SizeF {8.5,11},你可能会得到 Metafile ,其中 rclFrame 为{21590,27940} . 毕竟,将英寸转换为毫米并不难 . 但是你可能会赢得't. Depending on your resolution, GDI+, it seems, will use a truncated DPI value when converting the inches parameter. To get it right, I have to do it myself in hundredths of a millimeter, which GDI+ just passes through since that'它如何在图元文件头中原生存储:

    this.currentMetafile = new Metafile(
        this.currentStream,
        deviceContextHandle,
        new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
        MetafileFrameUnit.GdiCompatible,
        EmfType.EmfOnly);
    

    舍入错误#1已解决 - 我的元文件的 rclFrame 现在是正确的 .

    2.记录到图元文件的Graphics实例上的DPI始终是错误的 .

    通过在元文件上调用 Graphics.FromImage() 来查看我设置的 metafileGraphics 变量?好吧,似乎 Graphics 实例的DPI总是为96 dpi . (如果我不得不猜测,它总是设置为逻辑DPI,而不是物理DPI . )

    你可以想象当你在96dpi下运行 Graphics 实例并且在其 Headers 中记录到具有87.9231 dpi "recorded"的 Metafile 实例时发生的欢闹 . (我说"recorded"因为它是根据其他值计算出来的 . )元文件的"pixels"(记住,存储在图元文件中的GDI命令以像素为单位指定)更大,所以你诅咒并嘀咕为什么你要调用一英寸长的东西超长一英寸 .

    解决方案是缩小 Graphics 实例:

    metafileGraphics = Graphics.FromImage(this.currentMetafile);
    metafileHeader = this.currentMetafile.GetMetafileHeader();
    metafileGraphics.ScaleTransform(
        metafileHeader.DpiX / metafileGraphics.DpiX,
        metafileHeader.DpiY / metafileGraphics.DpiY);
    

    不是那个叫声吗?但似乎有效 .

    “舍入”错误#2解决了 - 当我说在88 dpi以“1英寸”画出一些东西时,那个像素最好是$%$ ^!记录为像素#88 .

    3. szlMillimeters可以变化很大;远程桌面带来很多乐趣 .

    因此,我们发现(根据Mark的回答),有时,Windows会查询显示器的EDID,并且实际上知道它的物理尺寸 . 填写 szlMillimeters 属性时,GDI可以有用地使用它( HORZSIZE 等) .

    现在想象一下,你回家调试这个远程桌面代码 . 假设您的家用电脑配备了16:9宽屏显示器 .

    显然,Windows无法查询远程显示器的EDID . 因此它使用了320 x 240 mm的古老默认值,这很好,除了它恰好是4:3的宽高比,现在完全相同的代码在显示器上生成一个元文件,该文件应该是非方形物理像素:水平DPI和垂直DPI是不同的,我不记得上次我看到的那种情况发生了 .

    我现在的解决方法是:“好吧,不要在远程桌面下运行它 . ”

    4.我正在使用的EMF-to-PDF工具在查看rclFrame标头时出现舍入错误 .

    这是引发这个问题的问题的主要原因 . 我的元文件一直是"correct"(好吧,在修复前两个问题之后正确),并且所有这些创建"high-resolution"元文件的搜索都是一个红色的鲱鱼 . 确实,在低分辨率显示设备上录制图元文件时会丢失一些保真度 . 's because the GDI commands specified in the metafile are specified in pixels. It doesn' t无论它是矢量格式还是可以向上或向下缩放,当GDI决定将哪个"pixel"捕捉到操作时,实际记录期间会丢失一些信息 .

    我联系了供应商,他们给了我一个更正的版本 .

    舍入错误#3已解决 .

    5. Windows资源管理器中的“摘要”窗格恰好在显示计算的DPI时截断值 .

    恰好这个截断的值表示EMF-to-PDF工具在内部使用的相同错误值 . 除此之外,这个怪癖对讨论没有任何意义 .

    结论

    由于我的问题是关于在设备环境中使用DPI进行预测,Mark's是一个很好的答案 .

  • 3

    我很好奇Windows如何知道显示器的物理尺寸 . 您必须在某处更改配置?也许您可以将其更改为更方便的值,可以很好地划分 .

    如名称所暗示的,必须将“设备上下文”连接到系统设备 . 但是,这不需要是硬件驱动程序,它可以是设备模拟器,例如PDF写入器打印驱动程序 . 我见过至少一个让你设置任意DPI的东西 .

相关问题