我的问题是如何更改字体处理以便在Windows 7下正常工作。我确信我已经做过关于之前有效但不再有效的事情的假设。但我甚至不知道从哪里开始寻找!我祈祷有人可以帮忙!以下是我了解它们的详细信息(我也在Microsoft Windows开发人员论坛上发布了这个问题,但他们没有回答):
是的,我落后于时代(哎呀,我仍然用普通的C编写WIN32代码!)我有一个10年前写的DLL,它模仿了客户区内更老的DOS屏幕I / O库一个窗口。不用说,它只允许使用固定宽度的字体。当一些使用DLL的程序被移动到Windows 7时,使用固定宽度的TRUE TYPE字体时会出现奇怪的闪烁(位图字体仍能正常工作。)我们已将问题跟踪到用ExtTextOut
写的单个字符比它应该更宽的事实。我已经通过三种不同的方式检查了测量结果(在132字符串上使用GetTextExtentPoint32
并除以132,通过调用GetTextMetrics
,甚至使用GetCharABCWidths
表示所有256个字符)并且他们都同意字体宽度相同。但是ExtTextOut
正在渲染背景矩形比字体宽度宽一或两个像素。要么是,要么开始在背景中渲染参数中给出的位置左侧的一个或两个像素[我称之为:ExtTextOut( hdc, r.left, r.top, ETO_OPAQUE, &r, &ch, 1, NULL )
。]请记住,这个完美的代码在Windows 2000下完美运行, Windows XP和Windows 7上的位图字体 - 但它在Windows 7下不再适用于固定宽度的真实字体。
对于任何不了解我需要做的事情的人:试着想象在一张方格纸上每个方格写一个字符。每个方块使用相同的字体,但可能具有不同的前景色和/或背景色。我使用TA_TOP|TA_LEFT
文本对齐,因为它是最简单的,任何一致应用的对齐应该适用于固定宽度的字体。
我看到的是ExtTextOut发出的背景矩形比我在RECT *
参数中指定的更大。由于我提供的矩形是根据报告的字体大小创建的,所以这绝不应该发生 - 它在Windows XP及更早版本中从未发生过,并且不会发生位图(即.FON)字体在Windows 7下,也是。但它总是在Windows 7下使用固定宽度的TrueType字体。这是在Windows 2000,Windows XP和Windows 7(32& 64)上运行的EXACT SAME EXECUTABLE。虽然我想简单地说Windows 7有一个bug ,我更倾向于相信我在Windows下对字体处理做出的一些基本假设已不再适用(经过20年的Windows软件编写。)
但我不知道如何或在哪里发现可能是什么!拜托,请帮助我!
---修正---
对于任何有兴趣的人,我都设法解决了我正在考虑的错误 - 直到我找到相反的文档。我的解决方法包括对我的库进行两处更改:
GetTextExtentPoint32()
返回的尺寸。代替
来自TEXTMETRICS
的数据。ETO_CLIPPING
来电中加入ExtTextOut()
标记。之前,我正在使用tmHeight+tmExternalLeading
来记录连续文本行顶部之间的像素数。我发现从GetTextExtentPoint32(
返回的size.cy值并不相同,看起来更准确。我找到的最糟糕的例子是OCRB真实字体。这是我在调试器中看到的我创建的OCRB字体(使用系统字体选择对话框):
ocrbtm.tmHeight = 11
ocrbtm.tmExternalLeading = 7
ocrbsize.cy = 11
因此,由于某些原因我还没有发现,Windows忽略了为OCRB字体定义的外部主导值。使用size值而不是TM会产生漂亮,整洁,密集的文本,这正是我想要的。
ETO_CLIPPING
标志对我来说不是必需的,因为我将矩形设置为单个字符的尺寸,并使用ETO_OPAQUE
填充背景(并覆盖之前的单元格内容)。 )但是没有剪切标记,单个字符比尺寸,文本度量或ABC宽度都要宽 - 至少,根据我迄今为止发现的所有文档都是如此。
我认为HEIGHT问题已经存在了很长时间,但在我们在Windows 7下运行我们的软件之前,其他问题是不必要的。我将此问题附加到我的问题上,看看是否有人可以解释我明显不喜欢的内容&# 39;理解。
- 修订2 -
1:我能找到的所有文档都说tmHeight+tmExternalLeading
应该生成单行间隔的文本。期。但这并非总是如此,我找不到文档,指出Windows如何确定GetTextExtentPoint32()
有时返回的不同值。
2:在Win7下(可能是Vista)ExtTextOut
开始填充更多背景(通过在右侧添加几个额外像素),但仅在选择了真实类型字体时。即使矩形是 double 字符的预期大小(以BOTH维度),它也会这样做.DPI / Scaling可能是一个因素,但由于我的系统设置为100%,它似乎是Windows在使用1:1比例因子时出现问题,这似乎是一个错误。事实上它只影响真实类型而不影响位图(.FON)字体似乎也排除了缩放(除非 是缩放系统中的一个错误),因为Windows应该尝试扩展所有的文字,而不仅仅是其中的一部分。此外,还有一个灰色(但经过检查)的设置"使用Windows XP风格的DPI缩放"在"自定义DPI设置"对话。最后,整个问题可能是我在Windows Classic主题下运行而不是Aero或其他Win7本机主题的结果。
- 修订3 -
简单地调用SetProcessDPIAware()对我遇到的问题没有影响。由于我的问题存在于100%DPI设置(比例1:1),如果我的问题 与DPI相关,那么我一定发现了DPI虚拟化中的错误,因为这是微软描述的特征:
此功能的工作原理是提供"虚拟化"系统指标和应用程序的UI元素,就好像它以96 DPI运行一样。然后,应用程序将渲染到96-DPI屏幕外表面,桌面Windows管理器会缩放生成的应用程序窗口以匹配DPI设置。
我的所有设置都显示我100%缩放,并且在自定义设置框中查看清楚地表明这意味着96 DPI。因此,如果从96 DPI到96 DPI的DPI虚拟化不能用于我的固定宽度真实类型字体,那么Windows有问题,对吧?或者是否需要调用(或停止调用?)以使DPI虚拟器能够正常工作?
我仍然不相信所谓的缩放问题实际上与我最初想的字体SIZE有很大关系。这是因为问题在背景矩形中被ExtTextOut()
填充,而不是被发出的文本字符。当字体为真类型时,背景矩形会稍微放大。我现在也验证了无论是使用Windows经典主题还是标准Windows Aero主题都会出现此问题。现在建立一个简化的例子,以便其他人可以试验它。
- 修订4 -
我已经创建了一个最小的演示程序,可以显示我所看到的内容(以及我正在做的事情。)Visual Studio 2010项目/来源可以从http://www.svalli.com/files/fwtt.7z下载 - - 我故意不包含可执行文件,因为我不想冒险传播恶意软件。该程序让您选择一个固定宽度的字体,然后将两个5x5字符网格写入客户区,一个使用GetTextExtentPoint32
大小创建,另一个使用Microsoft记录的TEXTMETRIC
大小。网格采用黑色和白色棋盘图案,最后写入中心的黄色红色字符显示重叠效果(您可能需要一个缩放实用程序才能清楚地看到它。)程序还会绘制一个以5 X开头的字符串#39;在网格下方,从相同的左偏移开始,用作我放置单个字符的方法的比较(我匹配字符串。)菜单允许在ExtTextOut
中打开/关闭剪辑和选择其他字体。还有一个命令行选项dpiaware
(区分大小写),它使程序在启动时调用SetProcessDPIAware()
,这样也可以评估该调用的效果。
从创建这个开始我就知道ExtTextOut
正在填充正确的背景矩形,但是使用不透明背景渲染的角色可能比它应该更宽,甚至可能不会在{{1被告知开始画画!我说"应该是"因为我最终得到的字符间距与我ExtTextOut
呈现整个字符串时得到的字符间距相匹配。重叠可能显然位于给定矩形的任一侧或两侧,例如,OCRB在字符单元的左侧和右侧都添加了一个额外的像素,而我检查的其他真实类型字体则添加两个像素右边缘。
我真的想这样做"对"方式,但我找不到任何文件,显示我做错了什么或错过了什么。好吧,我可能在100%以外的尺度上遗漏了DPI Aware的东西,但除此之外,我只是感到困惑。
- 修正案5 -
稍微不那么困惑......问题是由ClearType引起的。关闭ClearType使所有字体再次起作用。在XP下打开ClearType会导致同样的问题。显然ClearType可以默默地(直到有人告诉我如何检测它)水平拉伸几个像素的字符,以便为它添加的阴影像素腾出空间以平滑事物。
是否只能绕过这个问题?
- 修订6 -
上面我的剪辑问题的部分答案:创建新字体时,我现在执行以下操作(伪代码):
ExtTextOut
如果没有启用裁剪,几乎总是适用于我使用的字体大小,但我发现一些仍然会向右(或左)渲染额外像素字符单元格。幸运的是,这些似乎是互联网上的免费字体,因此它们的整体质量可能低于专业字体代工厂的标准。
如果有人能找到更好的答案,我真的,真的喜欢听到它!在那之前,我认为这是最好的。感谢您阅读此内容!
答案 0 :(得分:4)
确保您的代码为high DPI aware,然后tell the OS that your process is DPI aware。
如果你没有告诉操作系统你是否了解DPI,那么一些测量功能将会存在,并根据显示DPI实际上是96 dpi而不管它实际是什么来给你数字。同时,绘图功能将尝试向另一个方向缩放。对于简单的高级绘图,这种方法通常有效(尽管它经常导致模糊文本)。对于小的测量和单个字符的精确放置,这通常会导致导致类似inconsistent font sizes之类的问题。此行为是在Windows Vista中引入的。
您可以在Visual Studio 2010+中随时查看它,因为语法高亮显示颜色文本和单词在您键入时会在此处移动几个像素。真的很讨厌'烦人。
关于修正案:
tmExternalLeading
只是字体设计师提出的关于在文本行之间放置多少额外空间的建议。 MSDN文档通常会说,“应用程序在行之间添加的额外前导(空间)的数量。”嗯,你是应用程序,所以“正确的事情”就是在你自己绘制文本时在行之间添加它,但它真的取决于你。 (我怀疑像DrawText这样的高级函数会使用它。
GetTextExtentPoint32
(以及朋友)返回size.cy
等于tmHeight
并忽略tmExternalLeading
是完全正确的。作为程序员,最终你可以选择实际使用多少。
您可以通过一些简单的绘图代码看到这一点。选择一个非零的字体tmExternalLeading(Arial适用于我)。使用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应用程序。对于这种特殊字体,它可能不是一种美学选择。