DrawHTML






4.80/5 (44投票s)
2004年8月10日
6分钟阅读

106939

1644
DrawText() SDK函数的即插即用替换,
引言
DrawHTML()
函数几乎是标准 DrawText()
函数的即插即用替代品,但对 HTML 格式化标签的支持有限。它之所以仅仅是 DrawText()
函数的“几乎”替代品,是因为真正的 DrawText()
函数的一些格式化标志不受支持。更具限制性的是,它只支持一小部分 HTML 标签。
DrawHTML()
的灵感来源于 Petter Hesselberg 在《Windows Developer Journal》2000 年 2 月刊上发表的 **Pocket HTML**。然而,其实现完全由我完成,因为我认为 Pocket HTML 中的 HTML 解析器还有待改进。我的实现非常有限,但已经比 Pocket HTML 的功能更完善;它更易于扩展;并且通过按需分配资源而不是在函数启动时就获取所有“可能需要”的资源,从而提高了可扩展性。
为什么在已有功能齐全、支持所有标签的 HTML 解析器的情况下还要开发 DrawHTML()
?我实现它的原因如下:
- Windows API 函数根本不支持标记代码;即使是非常有限的标记支持也已经非常有帮助。这就是 Petter Hesselberg 发布 Pocket HTML 的原因(我的实现对其进行了改进)。
DrawHTML()
由一个单独的、相当小的文件组成(不到 400 行)。因此,将其添加到项目中非常容易。应用程序不需要额外的组件或 DLL,因为DrawHTML()
可以轻松地静态链接。DrawHTML()
只遍历一次string
;除了获取所需的字体资源外,DrawHTML()
不会分配内存或其他资源。因此,DrawHTML()
轻量级且快速。- 如果传递给
DrawHTML()
的内容不是完全有效的 HTML,它实际上仍然很健壮:许多人忘记将 "&" 替换为 "&
",将 "ü" 替换为 "ü
",将 "<" 替换为 "<
"。浏览器可能会跳过代码或显示错误的字符,但如果 "&"、"ü" 和 "<" 出现在string
中,DrawHTML()
就会显示它们。这样,您就可以使用DrawHTML()
显示string
,而无需特别注意像 "&" 和 "<" 这样的保留字符。
Using the Code
DrawHTML()
的函数原型与标准的 Win32 SDK 函数 DrawText()
相同。在您的程序中,您将像使用 DrawText()
一样使用 DrawHTML()
。
源代码档案(请参阅本文顶部)包含一个小型演示程序。相关的 Cls_OnPaint()
函数在下面重现
static void Cls_OnPaint(HWND hwnd)
{
PAINTSTRUCT PaintStruct;
BeginPaint(hwnd, &PaintStruct);
HFONT hfontOrg = (HFONT)SelectObject(PaintStruct.hdc,
hfontBase);
RECT rc;
GetClientRect(hwnd, &rc);
SetRect(&rc, rc.left + Margin, rc.top + Margin,
rc.right - Margin, rc.bottom - Margin);
DrawHTML(PaintStruct.hdc,
"<p>Beauty, success, truth ..."
"<br><em>He is blessed who has two.</em>"
"<br><font color='#C00000'><b>Your program"
" has none.</b></font>"
"<p><em>Ken Carpenter</em>",
-1,
&rc,
DT_WORDBREAK);
SelectObject(PaintStruct.hdc, hfontOrg);
EndPaint(hwnd, &PaintStruct);
}
调用 DrawHTML()
周围有一些辅助代码,用于将文本与窗口边框分开并选择更大的字体。字体 hfontBase
在 Cls_OnCreate()
函数(未显示)中创建。我使用了 20 像素高的 TrueType
字体,以更好地显示斜体和粗体的效果。
正如我之前所写,DrawHTML()
对 HTML 的支持非常有限
- 唯一支持的标签是
<p>
、<br>
、<font>..</font>
、<b>..</b>
、<i>..</i>
、<u>..</u>
、<strong>..</strong>
、<em>..</em>
、<sub>..</sub>
和<sup>..</sup>
。标签<strong>..</strong>
等同于<b>..</b>
,而<em>..</em>
则映射到<i>..</i>
。 <font>
标签仅支持更改文本颜色。它是唯一可以接受参数的标签,该参数应该是 "color
",其值采用众所周知的 HTML 十六进制表示法。例如,“<font color='#ffff00'>
”(顺便说一句,这是黄色)。- 除了带有参数的标签(目前只有
<font>
标签)之外,标签中不能有空格;<p>
是可以的,但<p align='right'>
将被视为两个单词“<p
”和“align='right'>
”。没错:当DrawHTML()
认为某内容不是有效 HTML 标签时,它会将其视为一个单词打印出来。 - 任何特殊字符,如
<
和à
都不受支持,您必须直接输入正确的字符。也就是说,您可以在文本中直接使用字符 "à" 和 "&",以及 "<"。
关注点
DrawHTML()
兼容 Unicode,但方式与网页浏览器不同:不是使用 8 位编码来处理 Unicode 数据(UTF-8),而是直接传递一个“宽字符”string
。要获得 Unicode 支持,您应该使用已定义的 UNICODE
和 _UNICODE
宏来编译 DrawHTML()
源代码。
DrawHTML()
的代码由三个部分组成:
- 有一个简单的 HTML 解析器。该解析器相当严格,并且具有回退机制,即任何它不识别的内容都将视为“纯文本”。这包括未知标签,
DrawHTML()
在这方面与浏览器不同,浏览器会忽略未知 HTML 标签。 - 文本绘制函数,它逐字输出文本,并处理换行和边界框大小的计算。
- 一个简单的颜色堆栈,用于存储通过
<font>
标签设置的颜色。更改颜色时,必须将旧颜色保存在某处,以便</font>
标签可以恢复它。因此,需要堆栈。
DrawHTML()
不支持 DrawText()
函数的一些“格式化标志”。这些标志与对齐(水平和垂直)以及设置 TAB 停止有关。支持水平和垂直对齐需要对文本进行额外的遍历,以获取每一行的总高度和宽度。具体来说,DrawText()
函数的以下格式化标志*不*受支持:
标志 | 描述 |
DT_CENTER | 水平居中文本行 |
DT_RIGHT | 文本行右对齐 |
DT_TABSTOP | 展开 TAB 字符(替换为 8 个空格) |
如果设置了这三个标志,它们将被忽略。 "&" 字符在 DrawHTML()
中*从不*是“前缀字符”,因此不需要 DT_NOPREFIX
标志。
更值得注意的是,实际上所有其他标志*都*受支持,特别是 DT_SINGLELINE
标志,它会导致 <p>
和 <br>
标签被忽略,以及 DT_CALCRECT
标志,它会在不实际绘制文本的情况下计算文本的边界矩形。通过在后端使用 DrawText()
来在解析 HTML 代码后实际*绘制*文本,进一步提高了与 DrawText()
的兼容性。
历史
- 2004 年 10 月 26 日
DT_BOTTOM
和DT_VCENTER
是允许的,因为原始函数DrawText()
仅为单行文本定义了它们,这与DrawHTML()
兼容。- 下划线样式(
<u>...</u>
)不再导致单词之间的下划线出现间隙;即,单词之间的空格字符现在也下划线了。 - 实现了一个针对负高度的修复(感谢“Sims”,请参见本文底部的评论)。
- 我添加了对 HTML 标签
<sup>...</sup>
和<sub>...</sub>
的支持,分别用于上标和下标。然而,只支持这两种标签的一个级别(因此<sub>b<sub>c</sub></sub>
会将 "b
" 和 "c
" 放在同一基线上)。
- 2004 年 8 月 11 日:文章增加了屏幕截图和函数使用示例。存档现在包含一个演示程序。
- 2004 年 8 月 10 日:首次发布。
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。