65.9K
CodeProject 正在变化。 阅读更多。
Home

DrawHTML

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (44投票s)

2004年8月10日

6分钟阅读

viewsIcon

106939

downloadIcon

1644

DrawText() SDK函数的即插即用替换,支持最小的HTML。

引言

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,它实际上仍然很健壮:许多人忘记将 "&" 替换为 "&",将 "ü" 替换为 "&uuml;",将 "<" 替换为 "&lt;"。浏览器可能会跳过代码或显示错误的字符,但如果 "&"、"ü" 和 "<" 出现在 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() 周围有一些辅助代码,用于将文本与窗口边框分开并选择更大的字体。字体 hfontBaseCls_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 标签时,它会将其视为一个单词打印出来。
  • 任何特殊字符,如 &lt;&agrave; 都不受支持,您必须直接输入正确的字符。也就是说,您可以在文本中直接使用字符 "à" 和 "&",以及 "<"。

关注点

DrawHTML() 兼容 Unicode,但方式与网页浏览器不同:不是使用 8 位编码来处理 Unicode 数据(UTF-8),而是直接传递一个“宽字符”string。要获得 Unicode 支持,您应该使用已定义的 UNICODE_UNICODE 宏来编译 DrawHTML() 源代码。

DrawHTML() 的代码由三个部分组成:

  1. 有一个简单的 HTML 解析器。该解析器相当严格,并且具有回退机制,即任何它不识别的内容都将视为“纯文本”。这包括未知标签,DrawHTML() 在这方面与浏览器不同,浏览器会忽略未知 HTML 标签。
  2. 文本绘制函数,它逐字输出文本,并处理换行和边界框大小的计算。
  3. 一个简单的颜色堆栈,用于存储通过 <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_BOTTOMDT_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 日:首次发布。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.