代码之家  ›  专栏  ›  技术社区  ›  Mike Spross Alex Martelli

是否有一种简单的方法强制Windows使用固定的dpi值而不是当前的dpi设置计算文本范围?

  •  2
  • Mike Spross Alex Martelli  · 技术社区  · 15 年前

    我想知道是否有一种简单的方法来计算字符串的文本范围(类似于 GetTextExtentPoint32 ,但允许我指定要在计算中使用的dpi。换句话说,有没有一个函数可以 获取文本扩展点32 是的,但允许我将dpi作为一个参数或一种“技巧”方式传递 获取文本扩展点32 使用我可以指定的DPI?

    在你问“你到底为什么要这样做”之前?,我会尽力解释,但请原谅,这个请求背后的原因有些牵扯。

    归根结底,这是一个自定义的自动换行算法,它将一个长字符串拆分成更小的文本块,需要整齐地适应具有复杂文本布局要求的水晶报表(它模仿警察用来提交刑事投诉的纸张形式,因此国家负责布局,而不是我们,它必须与几乎完全正确)。

    Crystal Reports不可能在没有帮助的情况下正确布局此文本(文本必须放在一个页面上的一个小方框内,如果文本溢出了小方框,则后面紧跟“标准大小”的续页),因此我编写了代码,将文本拆分为多个“块”,这些“块”存储在报告数据库中,然后单独呈现。根据报告。

    给定所需的尺寸(以逻辑英寸为单位)和字体信息,代码首先通过插入换行符使文本适合所需的宽度,然后根据文本高度将其拆分为正确大小的块。算法使用vb6 德克萨斯 文本宽度 函数计算范围,返回的结果与 获取文本扩展点32 功能会(我检查过)。

    当显示设置为96dpi时,此代码工作得很好,但以120dpi中断:有些行以96dpi处的单词结尾。

    例如,“快速的棕色狐狸跳过懒惰的狗”可能会断开如下:

    在96 dPI

    那只敏捷的棕色狐狸跳了过去。
    懒狗

    在120 dPI

    那只敏捷的棕色狐狸跃过
    懒狗

    此文本随后被Crystal Reports进一步分解,因为第一行不再适合报表上相应的文本字段,因此实际的报表输出如下所示:

    那只敏捷的棕色狐狸跳了过去。
    这个
    懒狗

    起初,我以为我可以通过缩放 德克萨斯 文本宽度 下降了25%,但显然生活并不是那么简单:似乎有很多舍入误差(可能还有其他因素?),以便任何给定字符串的文本范围永远不会 确切地 120 dpi时比96 dpi大25%。我没想到它能完美地缩放,但有时甚至不能接近(在一次测试中,120 dpi的宽度仅比96 dpi的宽度大18%)。

    Crystal Report直接处理的报表中的任何文本都不会发生这种情况:它似乎很好地扩展了所有内容,从而使报表在96 dpi和120 dpi(甚至144 dpi)下的布局完全相同。即使在打印报告时,文本也会完全按照屏幕上显示的方式打印(即,它看起来确实是所见即所得)。

    考虑到所有这些,因为我知道我的代码在96 dpi下工作,所以我应该能够通过计算96 dpi下的所有文本范围来解决这个问题,即使Windows当前使用的是不同的dpi设置。

    换句话说,我想要 FITTEXTCTRIECT 函数返回任何dpi设置下的相同输出,方法是强制使用96 dpi计算文本范围。这一切都可以解决,因为我将范围转换回英寸,然后将它们与所需的宽度和高度(英寸)进行比较。我认为96 dpi在像素和英寸之间来回转换时产生的结果比120 dpi更精确。

    我一直在往窗户上倒 Font and Text Functions ,查看是否可以滚动我自己的函数来计算给定dpi的文本范围,查看 GetTextMetrics 以及其他功能,以了解这可能是多么容易或多么困难。如果有一个更简单的方法来完成这一点,我想知道在我开始创建我自己版本的现有Windows API函数之前!

    2 回复  |  直到 15 年前
        1
  •  1
  •   Ben Voigt    15 年前

    GetTextMetrics接受DC。它使用来自该DC的DPI设置(例如,您不可能使用屏幕设置,并且希望数据输出的格式能够被打印机接受)。

    所以你所需要做的就是给一个直流电提供合适的dpi。 我想你可以直接控制一个元文件DC的dpi。

    元文件是矢量图形,所以它们看起来甚至不像有dpi。

    你可以用 CreateDIBitmap 但是没有办法找到匹配的DC。如果将该DIB选择到内存DC中,您可以看到DPI是否更改。( CreateCompatibleDC )

    或者您可以使用gdi+,创建一个具有所需dpi的位图,使用对图像进行操作的图形构造器,然后图形将具有正确的dpi,这样gdi+文本测量函数将使用您选择的dpi。

        2
  •  1
  •   Mike Spross Alex Martelli    15 年前

    我找到了一个更简单的解决方案。我花了一段时间才让自己相信这确实有道理。解决方案是如此明显,我觉得在这里张贴它几乎是愚蠢的。

    最终,我真正想要的是 FITTEXTCTRIECT 函数生成相同的文本布局 任何 DPI设置。事实证明,为了做到这一点,事实上 更容易的 以像素为单位测量所有内容。因为根据定义,任何其他度量单位都将考虑当前的dpi设置,使用像素以外的任何其他度量单位都可能导致问题的发生。

    接下来的“诀窍”是强制所有的计算得出与96 dpi时相同的结果。但是,如果临时缩放 字体大小 在计算任何文本范围之前向下。请注意,原始字体大小仍在打印预览和打印输出中使用。

    这是因为Windows内部在 设备单元 对于屏幕,表示像素。字体的“点大小”由允许您使用当前dpi设置选择字体的应用程序转换为像素:

    font_height_in_pixels = (point_size * current_dpi / 72)
    

    也就是说,Windows从不直接处理字体的点大小:它总是处理像素。因此,它也会根据像素计算文本范围。

    这意味着您可以根据当前dpi和字体点大小计算比例因子,该比例因子将字体缩小到新的点大小,在任何dpi上始终达到相同的像素数(我使用96作为“基线”dpi):

    scaled_point_size = (point_size * 96 / current_dpi)
    

    通过有效地强制字体在任何dpi上适应相同数量的像素,这可以确保文本范围在任何dpi上都是相同的,因此算法将在任何dpi上以相同的方式排列文本。

    我唯一需要做的是确保传递给函数的高度和宽度参数(以英寸为单位)正确转换为像素。我不能使用vb6现有的英寸到像素的转换函数,因为它考虑到了当前的dpi(这会产生不一致的结果,因为文本的高度和宽度是“标准化的”到96dpi),所以我只是将高度和宽度乘以96,将它们转换为其像素测量值为96dpi。