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

RTF 转 HTML - VB.NET

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (3投票s)

2015年7月22日

CPOL

4分钟阅读

viewsIcon

24133

仍在开发中,但这是一个简单的 HTML 到 RTF 转换器

引言

我需要将 HTML 转换为 RTF,因为我想将 RTF 输出用于另一个不支持 HTML 但支持 RTF 的应用程序。

背景

我的客户希望能够打印动态生成的邮件合并文档,但报表编写软件组件只支持 RTF,而 Web 界面的编辑器只生成 HTML。

我研究了一些可能的解决方案

  1. 使用 Word - 这并不可行,因为这意味着要在 Web 服务器上安装 Office,使用 OLE 自动化,并希望您的 Web 服务器不会耗尽内存。费用约为 250 美元。
  2. 查找可以添加到 Bin 文件夹的 DLL - 只找到了一些解决方案,但每个解决方案的开发者许可证和服务器许可证加起来都要大约 299 美元。
  3. 更改为 Cute-Editor,因为它除了 HTML 格式外,还能保存 RTF 格式,费用为 250 美元。

告诉客户他们的软件需求将花费更多钱,这并不是一个很好的选择,因为您可能会失去业务和推荐,所以我必须从头开始重新思考这个问题。

经过更多的研究,我开始看到一些固定的模式和一些变化的模式,我还参考了维基百科,他们提供了一个简单的 RTF 格式示例。

{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard
This is some {\b bold} text.\par
}

上面的 RTF 文本是简化的,分解如下:

  • \rtf1 = 告知您文档是 rtf 格式
  • \ansi = 使用 ANSI 字符集
  • {\fonttbl\f0\fswiss Helvetica;} = 将字体 0 定义为 Swiss(无衬线)Helvetica(如果可用)
  • \f0 = 使用字体 0
  • \pard = 段落开始标记
  • {\b bold} = 只将此词加粗
  • \par = 段落结束标记
  • 第一个 { 和最后一个 } 是文本字符串的分隔符。

在此之后,我决定我可以看到我能够玩弄 RTF 并看到每次更改的结果。

我从 Wordpad 中的简单“Hello World”开始,然后用记事本打开它(RTF 就像 HTML 一样,是以人类可读的格式保存的 - 这最终看起来像这样:

{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\*\generator Riched20 6.3.9600}\viewkind4\uc1
\pard\sa200\sl276\slmult1\qj\f0\fs22\lang9 Hello World\par
}

然后我将文本更改为粗体、斜体和下划线,并查看了更改。

我注意到 there were extra items like \viewkind4\uc1, \sa200\s1276\slmult1\qj & \fs22\lang9.

回过头来研究这些附加项,发现其中一些很重要,有些则不那么重要。

  • \*\ - 这是一个注释条目
  • \viewkind4 - 如果在 Word 中打开,显示为“普通视图”
  • \uc1 - 使用主 Unicode 字符集
  • \sa200 - 后续间距
  • \s1276 - 与样式有关,但我还没有完全弄清楚
  • \slmult1 - 单倍行距
  • \qj - 两端对齐文本(\ql, \qr, \qc\qd 是其他可用选项)
  • \fs22 - Windows 字体大小 11(大小 * 2)
  • \lang9 - 与语言有关,但我还没有弄清楚。

进展

至此,我已经掌握了客户要求的基本内容,并开始编码。在这个项目中,我只需要支持粗体、斜体和下划线文本,所以一开始我就专注于此。我也想要一些东西,即使存在其他不受支持的 HTML 元素,也不会崩溃。

代码

' =======================================================================
' = Define constants that are need to build the outline RTF file format =
' =======================================================================

' Define Start and End strings for RTF Formatting
Const RTF_START = "{\rtf1\ansi\ansicpg1252\deff0\deflang1033" 
Const RTF_END = "}"
' Define Start and End strings for FONT tables
Const FONT_TABLE_START = "{\fonttbl"
Const FONT_TABLE_END = "}"
' Define Start and End strings for COLOR tables (Next version maybe)
'Const COLOR_TABLE_START = "{\colortbl "
'Const COLOR_TABLE_END = ";}"
' Define End Paragraph constant
Const LINE_BREAK = "\par" & vbCrLf
' Define Indent constant - No HTML equivalent ;-)
'Const RTF_TAB = "\tab "
' Define New Paragraph constant
Const NEW_PARAGRAPH = "\pard"
' Define Bold constants, as unlikely that you are only enboldening one character
Const BOLD_START = "\b "
Const BOLD_END = "\b0 "
' Define Underline constants
Const UNDERLINE_START = "\ul "
Const UNDERLINE_END = "\ul0 "
' Define Italic constants
Const ITALIC_START = "\i "
Const ITALIC_END = "\i0 "
' Define View and charset
Const DOCUMENT_START = "\viewkind4\uc1" 
' ======================================================
' = RenderHTML                                         =
' =                                                    =
' = Input : HTML encoded string (Content of body only) =
' =                                                    =
' = Output : RTF encoded string                        =
' =                                                    = 
' ======================================================
Private Function RenderHTML(ByVal strInput As String) As String
  Dim intPos As Integer
  Dim strText As String

  ' Create initial header strings for the RTF format - Set font to Arial
  strText = RTF_START + FONT_TABLE_START + "{\f0\fnil\fcharset0 Arial;}" + FONT_TABLE_END + vbCrLf
  ' Add view and charset
  strText += DOCUMENT_START
  ' Start the document
  strText += NEW_PARAGRAPH
  ' Set the font to Arial 11 and Justify the text
  strText += "\sa200\sl276\slmult1\qj\f0\fs22\lang9 "

  ' Start Processing the HTML string
  Do
    ' Check for < in HTML string
    If Mid(strInput, 1, 1) = "<" Then
      ' Look for different tags and move input to next element in HTML
      If Mid(strInput, 1, 3) = "<p>" Or Mid(strInput, 1, 3) = "<P>" Then
        strInput = Mid(strInput, 4)
      ElseIf Mid(strInput, 1, 4) = "</p>" Or Mid(strInput, 1, 3) = "</P>" Then
        strText += LINE_BREAK
        strInput = Mid(strInput, 5)
      ElseIf Mid(strInput, 1, 3) = "<b>" Or Mid(strInput, 1, 3) = "<B>" Then
        strText += BOLD_START
        strInput = Mid(strInput, 4)
      ElseIf Mid(strInput, 1, 4) = "</b>" Or Mid(strInput, 1, 3) = "</B>" Then
        strText += BOLD_END
        strInput = Mid(strInput, 5)
      ElseIf Mid(strInput, 1, 3) = "<i>" Or Mid(strInput, 1, 3) = "<I>" Then
        strText += ITALIC_START
        strInput = Mid(strInput, 4)
      ElseIf Mid(strInput, 1, 4) = "</i>" Or Mid(strInput, 1, 3) = "</I>" Then
        strText += ITALIC_END
        strInput = Mid(strInput, 5)
      ElseIf Mid(strInput, 1, 8) = "<strong>" Or Mid(strInput, 1, 8) = "<STRONG>" Then
        strText += BOLD_START
        strInput = Mid(strInput, 9)
      ElseIf Mid(strInput, 1, 9) = "</strong>" Or Mid(strInput, 1, 9) = "</STRONG>" Then
        strText += BOLD_END
        strInput = Mid(strInput, 10)
      ElseIf Mid(strInput, 1, 4) = "<em>" Or Mid(strInput, 1, 4) = "<EM>" Then
        strText += ITALIC_START
        strInput = Mid(strInput, 5)
      ElseIf Mid(strInput, 1, 5) = "</em>" Or Mid(strInput, 1, 5) = "</EM>" Then
        strText += ITALIC_END
        strInput = Mid(strInput, 6)
      ElseIf Mid(strInput, 1, 3) = "<u>" Or Mid(strInput, 1, 3) = "<U>" Then
        strText += UNDERLINE_START
        strInput = Mid(strInput, 4)
      ElseIf Mid(strInput, 1, 4) = "</u>" Or Mid(strInput, 1, 3) = "</U>" Then
        strText += UNDERLINE_END
        strInput = Mid(strInput, 5)
      Else
        ' ============================================================================
        ' = Catch all remaining HTML and show on the browser the unsupported element = 
        ' ============================================================================
        intPos = InStr(strInput, ">")
        HttpContext.Current.Response.Write("UNSUPPORTED : " + Mid(strInput, 1, intPos) + "<br/>")
        strInput = Mid(strInput, intPos + 1)
      End If
    Else
      ' Check for & in the HTML input and replace
      If Mid(strInput, 1, 1) = "&" Then
        If Mid(strInput, 1, 6) = "&nbsp;" Then
          strText += " "
          strInput = Mid(strInput, 7)
        ElseIf Mid(strInput, 1, 5) = "&" Then
          strText += "&"
          strInput = Mid(strInput, 6)
        ElseIf Mid(strInput, 1, 4) = "&lt;" Then
          strText += "<"
          strInput = Mid(strInput, 5)
        ElseIf Mid(strInput, 1, 4) = "&gt;" Then
          strText += ">"
          strInput = Mid(strInput, 5)
        ElseIf Mid(strInput, 1, 6) = "&copy;" Then
          strText += "\'a9"
          strInput = Mid(strInput, 7)
        ElseIf Mid(strInput, 1, 5) = "&reg;" Then
          strText += "\'ae"
          strInput = Mid(strInput, 6)
        ElseIf Mid(strInput, 1, 7) = "&trade;" Then
          strText += "\'99"
          strInput = Mid(strInput, 8)
        ElseIf Mid(strInput, 1, 7) = "&pound;" Then
          strText += "£"
          strInput = Mid(strInput, 8)
        ElseIf Mid(strInput, 1, 6) = "&euro;" Then
          strText += "\'80"
          strInput = Mid(strInput, 7)
        ElseIf Mid(strInput, 1, 2) = "&#" Then
          ' Handle &# 
          If CType(Mid(strInput, 3, InStr(strInput, ";") - 1), Integer) <= 127 Then
            strText += Chr(CType(Mid(strInput, 3, InStr(strInput, ";") - 1), Integer))
          ElseIf CType(Mid(strInput, 3, InStr(strInput, ";") - 1), Integer) <= 255 Then
            strText += "\'" + Hex(CType(Mid(strInput, 3, InStr(strInput, ";") - 1), Integer))
          Else
            strText += "\u" + Hex(CType(Mid(strInput, 3, InStr(strInput, ";") - 1), Integer))
          End If
          strInput = Mid(strInput, 3, InStr(strInput, ";") + 1)
        Else
        ' ============================================================================
        ' = Catch all remaining HTML and show on the browser the unsupported element = 
        ' ============================================================================
          intPos = InStr(strInput, ";")
          HttpContext.Current.Response.Write("UNSUPPORTED : " + Mid(strInput, 1, intPos) + "<br/>")
          strInput = Mid(strInput, intPos + 1)
        End If
      Else
        strText += Mid(strInput, 1, 1)
        strInput = Mid(strInput, 2)
      End If
    End If
  Loop Until strInput = ""
  strText += RTF_END
  Return strText
End Function

当前状态

正如你所见,存在一些不足之处

  • 没有额外的字体支持
  • 没有颜色支持
  • 表格支持不足

我将在有时间和足够多的人感兴趣时开始研究这些问题,但由于其中大多数都嵌入在样式表或样式标签中,使用当前的编码模型,我需要多次解析 HTML 文件才能仅获取字体和颜色表,然后再获取实际文本,因此我正在开发一个更优雅的解决方案来解决这些问题,而且字体可能有多个选项,例如“Arial, Verdana, Helvetica, sans-serif”可能是样式属性,颜色可以是 #RRGGBB 或命名颜色,所以当我查看仅这些更改的时间尺度时,很明显需要采取额外的步骤。

我希望这段代码能帮助到其他人,如果您想以建设性的方式为这个技巧做出贡献,请随时与我联系并提出您的建议。

© . All rights reserved.