在Win7中,一些字体不能像Win2K / XP那样工作

我的问题是如何为了在Windows 7下正确工作而改变字体处理方式。我确信我已经对之前有效的东西做了假设,但不再有效。 但我甚至不知道从哪里开始寻找! 我祈祷有人能帮助! 以下是我了解的详细信息(我也在Microsoft Windows开发人员论坛上发布了这个问题,但他们没有回答):

是的,我落后于时代(哎呀,我还是用普通的C编写WIN32代码!)我有一个10年前写的DLL,它模仿了窗口客户区域中一个更旧的DOS屏幕I / O库。 不用说,它只允许使用固定宽度的字体。 当一些使用DLL的程序移动到Windows 7时,使用固定宽度的TRUE TYPE字体时会出现一种奇怪的闪烁现象(位图字体仍能正常工作)。我们已将问题跟踪到事实使用ExtTextOut编写的单个字符比应该宽。 我已经通过三种不同的方式检查了度量(通过对132个字符串使用GetTextExtentPoint32并除以132,通过调用GetTextMetrics ,甚至对所有256个字符使用GetCharABCWidths ),并且他们都同意字体宽度相同。 但ExtTextOut正在渲染比字体宽度宽一个或两个像素的背景矩形。 不管是,还是开始背景渲染一个像素或两个在参数中给出的位置的左侧[我称之为: ExtTextOut( hdc, r.left, r.top, ETO_OPAQUE, &r, &ch, 1, NULL ) 。]请记住,这个EXACT代码在Windows 2000,Windows XP和Windows 7上的位图字体下工作得很完美 - 但它不再适用于Windows 7下的固定宽度真正的字体类型。

对于那些没有掌握我需要做的事情的人:尝试想像在一张方格纸上每个方格写一个字符。 每个方块使用相同的字体,但可能具有不同的前景和/或背景颜色。 我使用TA_TOP|TA_LEFT文本对齐方式,因为它是最简单的,任何一致应用的对齐方式都适用于固定宽度的字体。

我所看到的是ExtTextOut正在发射比我在RECT *参数中指定的更大的背景矩形。 由于我提供的矩形是根据报告的字体大小创建的,因此应该永远不会发生 - 而且它从未发生在Windows XP和更早版本上,并且在Windows 7下不会发生位图(即.FON)字体,无论是。 但它始终在Windows 7下使用固定宽度的TrueType字体。这是在Windows 2000,Windows XP和Windows 7(32和64)上运行的EXACT SAME EXECUTABLE。尽管我只想说Windows 7有一个bug,我更倾向于相信,我在Windows下进行字体处理的一些基本假设不再是事实(在为Windows编写软件20年之后)。

但我不知道如何或在哪里发现可能是什么! 请,请帮助我!

---修正案---

对于任何感兴趣的人,我设法解决了我正在考虑的一个错误 - 直到我找到相反的文档。 我的解决方法包括对我的库进行两项更改:

  • 使用“X”的GetTextExtentPoint32()返回的大小,而不是TEXTMETRICS的数据。
  • 在所有ExtTextOut()调用中包含ETO_CLIPPING标志。
  • 以前,我正在使用tmHeight+tmExternalLeading获取连续文本行顶部之间的像素数,如文档所述。 我发现从GetTextExtentPoint32( )返回的size.cy值不一样,看起来更准确。 我发现的最糟糕的例子是OCRB真正的字体。 这是我在调试器中看到的我创建的OCRB字体(使用系统字体选择对话框):

    ocrbtm.tmHeight          = 11
    ocrbtm.tmExternalLeading =  7
    
    ocrbsize.cy = 11
    

    所以,由于某些原因,我还没有发现,Windows忽略了为OCRB字体定义的外部主导值。 使用尺寸值而不是TM会产生漂亮,整齐,紧密的文本,这正是我想要的。

    ETO_CLIPPING标志对于我来说不应该是必需的,因为我将矩形设置为单个字符的尺寸,并使用ETO_OPAQUE填充背景(并覆盖之前的单元格内容)。但是,如果没有剪切标志,则单个字符是宽度大于文本大小,文本度量或ABC宽度将表示 - 至少,根据我迄今发现的所有文档,这是真实的。

    我认为HEIGHT问题已经存在了很长一段时间,但其余的是没有必要的,直到我们在Windows 7下运行我们的软件。我将这附加到我的问题,看看有没有人能解释我显然不明白的东西。

    - 修正案2 -

    1:我能找到的所有文档都说tmHeight+tmExternalLeading应该产生单行间隔的文本。 期。 但这并非总是如此,我无法找到文档指出Windows如何确定有时由GetTextExtentPoint32()返回的不同值。

    2:在Win7下(也许Vista) ExtTextOut开始填充比它应该多一点的背景(通过向右添加几个额外的像素),但只有当选择了真正的字体时。 即使矩形是字符的预期大小的两倍 (在这两个维度中),它也这样做。DPI /缩放比例可能是一个因素,但由于我的系统设置为100%,因此Windows似乎遇到了1 :1比例因子,这似乎是一个错误。 它不仅影响真正的类型,而不是位图的事实(.FON)字体也似乎排除缩放( 除非在缩放系统中的错误),因为Windows应尝试扩展所有的文字,不只是它的一些。 此外,在“自定义DPI设置”对话框中有“使用Windows XP样式DPI缩放”的灰色(但已选中)设置。 最后,这整个问题可能是我在Windows Classic主题下运行的结果,而不是Aero或其他Win7本机主题之一。

    - 修正案3 -

    简单地调用SetProcessDPIAware()对我遇到的问题没有影响。 由于我的问题存在于100%DPI设置(比例1:1),如果我的问题 DPI相关,那么我必须在DPI虚拟化中发现一个错误,因为这是Microsoft描述该功能的方式:

    此功能通过为应用程序提供“虚拟化”系统指标和UI元素而起作用,就好像它运行在96 DPI一样。 然后应用程序呈现96-DPI离屏表面,并且桌面Windows管理器缩放最终的应用程序窗口以匹配DPI设置。

    我的所有设置都显示我的缩放比例为100%,并且在自定义设置框中清楚地显示这意味着96 DPI。 因此,如果从96 DPI到96 DPI的DPI虚拟化不适用于我的固定宽度的真实字体,那么Windows有问题,对吧? 或者是否有一些功能需要调用(或停止调用?)以便DPI虚拟器正常工作?

    我仍然不相信所谓的缩放问题实际上与我原先认为的字体大小有关。 这是因为问题显示在由ExtTextOut()填充的背景矩形中,而不是正在发射的文本字符。 当字体为真类型时,背景矩形会稍微放大。 我现在也已验证,无论使用Windows经典主题还是标准Windows Aero主题都会发生此问题。 现在创建一个简化的示例,以便其他人可以对其进行试验。

    - 修正案4 -

    我创建了一个最小的演示程序,显示我所看到的(以及我在做什么)。Visual Studio 2010项目/源代码可以从http://www.svalli.com/files/fwtt.7z下载 - 我故意没有包含可执行文件,因为我不想冒传播恶意软件的风险。 该程序可以选择固定宽度的字体,然后向客户区写入两个5x5字符的网格,一个是使用GetTextExtentPoint32大小创建的,一个是使用Microsoft记录的TEXTMETRIC大小创建的。 网格呈黑白棋盘格式,最后一个红色字符写入中心以显示重叠效果(您可能需要使用缩放实用程序才能清楚地看到它)。该程序还绘制了一个字符串,以5 X开始该网格从相同的左偏移量开始,用于比较我放置单个字符的方法(我匹配字符串)。该菜单允许在ExtTextOut打开/关闭剪辑并选择其他字体。 还有一个命令行选项dpiaware (区分大小写),它使程序在启动时调用SetProcessDPIAware() ,以便也可以评估该调用的效果。

    从创建这个我了解到, ExtTextOut正在填充正确的背景矩形,但用不透明背景呈现的角色可能比它应该更宽,甚至可能不会在ExtTextOut被告知开始绘制的地方开始! 我说“应该是”,因为我结尾的字符间距与ExtTextOut呈现整个字符串时得到的字符间距相匹配。 重叠可能显然位于给定矩形的任一侧或两侧,例如,OCRB向字符单元格的左侧和右侧添加了一个额外的像素,而我检查的其他真正的字体字体向右侧添加了两个像素边缘。

    我真的想要这样做的“正确”的方式,但我找不到任何文件,显示我做错了什么或缺少。 那么,我可能在100%以外的尺度上错过了DPI Aware,但除此之外,我只是感到困惑。

    - 修正案5 -

    稍微有点困惑......这个问题是由ClearType引起的。 关闭ClearType后,所有字体都会再次运行。 在XP下打开ClearType会导致同样的问题。 显然,ClearType可以默默地(直到有人告诉我如何检测它)水平地拉伸字符一对像素,以便为阴影像素留出空间以增加平滑效果。

    是削减这个问题的唯一方法?

    - 第6条 -

    部分回答我上面的剪辑问题:当创建一个新的字体时,我现在执行以下操作(在伪代码中):

    CreateFontIndirect
    SelectFont
    GetTextMetrics
    if( (tmPitchAndFamily & TMPF_TRUETYPE) && Win6.x or above )
       if( SystemParametersInfo( SPI_GETCLEARTYPE ) )
            lfQuality = NONANTIALIASED_QUALITY
            DeleteObject( font )
            CreateFontIndirect
    

    没有启用裁剪,这几乎总是与我正在使用的字体大小一致,但我发现一些仍然会在字符单元的右侧(或左侧)渲染一个额外的像素。 幸运的是,那些似乎是互联网上的免费字体,所以他们的整体质量可能低于专业字体铸造厂的标准。

    如果任何人都可以找到一个更好的答案,我真的, 真的喜欢听! 在此之前,我认为这是一样好。 感谢您阅读这些!


    确保您的代码具有较高的DPI感知能力,然后告诉操作系统您的过程是DPI感知的。

    如果您不告诉操作系统您是DPI,那么某些测量功能就会存在,并根据显示器DPI实际上为96 dpi的假设给出数字,而不管它是真实的。 同时,绘图功能将尝试向另一个方向缩放。 对于简单的高级绘图,这种方法通常可行(尽管它通常导致模糊文本)。 对于单个字符的小尺寸和精确放置,这通常会导致导致字体大小不一致等问题。 此行为是在Windows Vista中引入的。

    您可以随时在Visual Studio 2010+中看到它,因为语法突出显示器会在输入时为文本添加颜色,并且字符在这里和那边移动几个像素。 真的很frickin'烦人。

    关于修正案:

    tmExternalLeading仅仅是字体设计师对文本行间多余空间的推荐。 MSDN文档通常会说,“应用程序在行之间添加额外领先(空间)的数量。” 那么,你是应用程序,所以“正确的事情”是在你自己绘制文本时在行之间添加它,但这取决于你。 (我怀疑像DrawText这样的更高层次的功能会使用它。

    GetTextExtentPoint32 (和朋友)返回一个等于tmHeightsize.cy并忽略tmExternalLeading是完全正确的。 作为程序员,最终您的选择将导致实际使用的程度。

    你可以通过一些简单的绘制代码来看到这一点。 选择一个非零的字体tm​​ExternalLeading(宋体适用于我)。 使用TextOut和独特的背景颜色绘制一些文本。 然后使用GetTextExtentPoint32测量文本,并根据返回的值绘制一些行。 你会看到背景颜色矩形排除了外部领先。 外部领先就是这样:外部。 它不在字符单元格的范围内。

      // Draw the sample text with an opaque background.
      assert(::GetMapMode(ps.hdc) == MM_TEXT);
      assert(::GetBkMode(ps.hdc) == OPAQUE);
      assert(::GetTextAlign(ps.hdc) == TA_TOP);
      COLORREF rgbOld = ::SetBkColor(ps.hdc, RGB(0xC0, 0xFF, 0xC0));
      ::TextOutW(ps.hdc, x, y, pszText, cchText);
      ::SetBkColor(ps.hdc, rgbOld);
    
      // This vertical line at the right side of the text shows that opaque
      // background is exactly the height returned by GetTextExtentPoint32.
      SIZE size = {0};
      if (::GetTextExtentPoint32W(ps.hdc, pszText, cchText, &size)) {
        ::MoveToEx(ps.hdc, x + size.cx, y, NULL);
        ::LineTo(ps.hdc, x + size.cx, y + size.cy);
      }
    
      // These horizontal lines show the normal line spacing, taking into
      // account tmExternalLeading.
      assert(tm.tmExternalLeading > 0);  // ensure it's an interesting case
      ::MoveToEx(ps.hdc, x, y, NULL);
      ::LineTo(ps.hdc, x + size.cx, y);  // top of this line
      const int yNext = y + tm.tmHeight + tm.tmExternalLeading;
      ::MoveToEx(ps.hdc, x, yNext, NULL);
      ::LineTo(ps.hdc, x + size.cx, yNext);  // top of next line
    

    彩色矩形底部和下一行顶部之间的间隙表示外部前导,它始终在字符单元之外。

    OCR-B设计用于银行设备中可靠的光学字符识别。 具有较大的外部领先(相对于实际文本的高度)可能适用于某些OCR应用程序。 对于这种特殊的字体,这可能不是一种美学选择。

    链接地址: http://www.djcxy.com/p/69615.html

    上一篇: In Win7 some fonts don't work like they did in Win2K/XP

    下一篇: Visual Studio 2012 "Smart" Indentation Customization