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

我的问题是如何改变字体处理,以便在Windows 7下正常工作。我相信我已经对之前有效的东西做了一个假设,但不再有效。 但我甚至不知道从哪里开始寻找! 我祈祷有人可以帮忙! 下面是我了解他们的细节(我也在Microsoft Windows开发者论坛上发布了这个问题,但他们没有回答):

是的,我落后于时代(哎,我还是用普通的C编写WIN32代码!)我有一个10年前写的DLL,它模仿一个窗口客户区域内更旧的DOS屏幕I / O库。 不用说,它只允许使用固定宽度的字体。 当使用DLL的某些程序已经移到Windows 7时,使用固定宽度的TRUE TYPE字体时会出现一个奇怪的闪烁(位图字体仍能正常工作)。我们已经将问题追踪到事实用ExtTextOut编写的单个字符比应该宽。 我已经用三种不同的方法检查了测量结果(对132个string使用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文本alignment方式,因为它是最简单的,任何一致应用的alignment方式都适用于固定宽度的字体。

我所看到的是ExtTextOut正在发射一个比我在RECT *参数中指定的更大的背景矩形。 由于我提供的矩形是从报告的字体大小创build的,所以不应该发生这种情况,而且在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年之后)。

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

—修改—

对于任何感兴趣的人,我设法解决了我正在考虑的一个bug – 直到我find相反的文档。 我的解决方法包括对库的两个更改:

  1. 使用“X”的GetTextExtentPoint32()返回的大小而不是TEXTMETRICS的数据。
  2. 在所有ExtTextOut()调用中包含ETO_CLIPPING标志。

以前,我正在使用tmHeight+tmExternalLeading获取连续文本行顶部之间的像素数,如文档所述。 我发现从GetTextExtentPoint32( )返回的size.cy值是不一样的,似乎更准确。 我发现的最糟糕的例子是OCRB真正的字体。 这是我在debugging器中看到的我创build的OCRB字体(使用系统字体select对话框):

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

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

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

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

– 修正案2 –

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

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

– 修正案3 –

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

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

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

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

– 修正案4 –

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

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

我真的想这样做的“正确”的方式,但我找不到任何文件,显示我做错了什么或缺less。 那么,我可能会失去DPI意识的东西在100%以外的比例,但除此之外,我只是困惑。

– 修正案5 –

稍微有点困惑…问题是由ClearType引起的。 closuresClearType使所有的字体再次工作。 在XP下打开ClearType导致同样的问题。 显然,ClearType可以默默地(直到有人告诉我如何检测它)水平地拉伸字符一个像素,以便为阴影像素腾出空间来增加平滑的事情。

是解决这个问题的唯一方法?

– 第6条 –

部分回答我上面的剪辑问题:当创build一个新的字体我现在做以下(伪代码):

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

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

如果有人能find更好的答案,我真的很喜欢听到它! 在此之前,我认为这是一样好。 感谢您阅读这个!

确保您的代码具有较高的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应用程序。 对于这个特定的字体,这可能不是一个美学的选择。