我现在已经学到比我想知道的更多关于元文件(metafile)了。
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;
}
如果您传递了大小为 { 8.5, 11 } 的 SizeF,您可能希望得到一个具有 rclFrame { 21590, 27940 } 的 Metafile。毕竟,将英寸转换为毫米并不难。但实际上可能不会这样。根据您的分辨率,GDI+在转换英寸参数时似乎会使用截断的 DPI 值。为了做到正确,我必须以百分之一毫米的方式自己完成,因为 GDI+ 只是通过元文件头原生存储百分之一毫米来传递它。
this.currentMetafile = new Metafile(
this.currentStream,
deviceContextHandle,
new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
MetafileFrameUnit.GdiCompatible,
EmfType.EmfOnly);
解决了舍入误差问题——我的元文件的rclFrame
现在是正确的。
2. 绘制到Metafile
实例的Graphics
对象上时,DPI总是错误的。
看到我通过对元文件调用Graphics.FromImage()
设置的 metafileGraphics
变量吗?嗯,好像这个Graphics
实例始终具有96 DPI。(如果要猜测,它始终设置为逻辑DPI而不是物理DPI。)
当您在一个操作96 DPI并记录到其标题中具有87.9231 DPI“记录”的Metafile
实例上的Graphics
对象上进行绘制时,可能会发生一些滑稽的情况(请记住,存储在元文件中的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。
szlMillimeters
可能会有很大变化;远程桌面带来了很多乐趣。所以我们发现(根据马克的答案),有时Windows会查询您的显示器的EDID,并实际上知道其物理大小。GDI+在填写
szlMillimeters
属性时会用到这个信息(
HORZSIZE
等)。现在想象一下,你回家调试这个远程桌面的代码。假设你的家用电脑碰巧有一个16:9的宽屏显示器。显然,Windows无法查询远程显示器的EDID。因此,它使用古老的默认值320 x 240 mm,这本来没问题,但它碰巧是4:3的长宽比,现在完全相同的代码正在产生一个元文件,在一个被认为具有非正方形物理像素的显示器上:水平DPI和垂直DPI是不同的,我记不得上次看到这种情况是什么时候了。我的临时解决方法是:“好吧,不要在远程桌面下运行它。”
我使用的EMF-to-PDF工具在查看
rclFrame
标头时存在舍入误差。这是导致我问题的主要原因,触发了这个问题。我的元文件一直是“正确”的(在我修复前两个问题后),所有寻找创建“高分辨率”元文件的搜索都是一个误导。当在低分辨率显示设备上记录元文件时,可能会丢失一些保真度;这是因为元文件中指定的GDI命令是以像素为单位指定的。无论它是矢量格式并可以放大或缩小,有些信息在GDI+决定将操作捕捉到哪个“像素”时
“在实际记录期间”丢失了。
我联系了供应商他们给了我一个更正版本,解决了舍入错误#3。
Windows Explorer中的“摘要”面板恰好在显示计算DPI时截断值。除此之外,这种怪癖对讨论没有任何有意义的贡献。
结论:由于我的问题与设备上下文中的DPI有关,马克的答案是一个好答案。