自动化文档发布工具的开发与实践

在这个数字化时代,文档的电子化和网络化发布变得越来越重要。为了简化和自动化公司文档的发布流程,开发了一款工具,旨在将Word文档转换为HTML格式,以便在网页上发布。

公司积累了大量的Word文档,需要发布到网站上。虽然Microsoft Word提供了导出为HTML的功能,但需要应用自定义的样式表,并且根据不同的文档类型应用不同的样式。基于这些需求,开发了一个原型工具,希望能够进一步改进。

架构设计

这个原型工具虽然功能有限,但已经能够处理不同样式的文档,并取得了令人满意的结果。转换过程包括扫描Word文档,按阅读顺序提取每个单词或段落的样式属性,找到匹配的HTML标签,然后渲染成HTML文件。

技术选型

阅读一个docx文档并不简单,它是一个XML格式,虽然可读,但规格庞大。经过几天的研究,理解了一些基本概念,并取得了一些成果。

首先,docx格式是一个压缩的zip包。可以使用自定义库打开它,也可以使用.NET Framework 3.0提供的“System.IO.Packaging”命名空间中的类,后者非常有用,因为它还实现了管理检索包“关系”的所有功能。

其次,想要分析的主要文件是包含文本的文件,即“\word\document.xml”。这个文件逐个元素描述了文档的内容。

第三,根据OOXML规范:“WordprocessingML文档的基础是其实际的文本内容。这些文本内容可以存储在许多上下文中(表格、文本框等),但WordprocessingML中最基本的文本内容形式是段落,使用p元素(§2.3.1.22)指定。在段落内,所有丰富的段落级格式都存储在pPr元素(§2.3.1.25;§2.3.1.26)中。[注意:一些段落属性的例子包括对齐、边框、连字符覆盖、缩进、行距、阴影、文本方向和寡/孤控制。]在段落内,文本被分组到一个或多个运行中,由r元素(§2.3.2.23)表示,它定义了一个具有一组共同属性的文本区域。”

功能实现

段落可以有自己的样式,但包含的运行可以覆盖该样式,具有更具体的样式。因此,存在两个级别的样式。区分应用的样式(正常、标题、列表编号...)和文本修饰符(粗体、下划线、斜体...)是非常有用的。

程序在控制台输出所有在转换过程中未匹配的样式名称。

创建了一个结构,它将具有相同格式的所有文本保存在缓冲区中,并在格式更改时刷新缓冲区。StyleClass类执行此操作。它有两个公共属性StyleName和Modifiers,通过设置这些属性,可以检查格式何时更改。它覆盖了ToString()函数,返回一个包含其属性值的唯一字符串。每个段落都有自己的StyleClass,每个子元素也有。如果子元素指定了不同的样式或添加了文本修饰符,它将设置一个StyleClass,当样式更改时,它会使缓冲区刷新。

Private Class StyleClass Public StyleName As String Private p_modifiers As Hashtable Public Sub New() p_modifiers = New Hashtable End Sub Public Sub AddModifier(ByVal modifier As String) If p_modifiers(modifier) Is Nothing Then Me.p_modifiers.Add(modifier, modifier) End If End Sub Public ReadOnly Property Modifiers() As Object Get Return p_modifiers.Values End Get End Property Public Shadows Function ToString() As String Dim tmp As String = "" For Each m As String In p_modifiers.Values tmp &= m Next Return StyleName & "|" & tmp End Function End Class

刷新缓冲区也意味着向文本添加HTML标签。创建了一个表格,其中包含Word样式及其匹配的“html开始标签”和“html结束标签”:

Style | Start tag | End tag

Normal | <p> | </p>

Heading1 | <h1> | </h1>

Code | <pre> | </pre>

...

以及一个用于文本修饰符的表格:

Modifier | Start tag | End tag

bold | <b> | </b>

Italic | <i> | </i>

Underline | <u> | </u>

...

从外部XML文件中加载这个匹配表到内存中,然后使用它根据规范渲染文本。

<?xml version="1.0" encoding="utf-8"?> <conversion_map> <styles> <style name="Normale"> <start_ctag>[p]</start_ctag> <end_ctag>[/p]</end_ctag> </style> <style name="Paragrafoelenco_l0"> <start_ctag>[li style='list-style-type: circle; margin: 5px 0 5px 15px;']</start_ctag> <end_ctag>[/li]</end_ctag> </style> <style name="Code"> <start_ctag>[pre lang="VB.NET"]</start_ctag> <end_ctag>[/pre]</end_ctag> </style> <style name="Grigliatabella"> <start_ctag>[table class='feature' cellspacing='0' cellpadding='0' style='width:100%;']</start_ctag> <end_ctag>[/table]</end_ctag> </style> ... </styles> <modifiers> <modifer name="b"> <start_ctag>[b]</start_ctag> <end_ctag>[/b]</end_ctag> </modifer> <modifer name="c"> <start_ctag>[text style='color: #{0};']</start_ctag> <end_ctag>[/text]</end_ctag> </modifer> <modifer name="h"> <start_ctag>[text style='background-color: {0};']</start_ctag> <end_ctag>[/text]</end_ctag> </modifer> ... </modifiers> </conversion_map>

写入HTML文档的函数使用一个栈来添加开始标签和结束标签,以确保正确的顺序:

Dim tmp As String = "" Dim cs As ConvertionClass = ht_Style(style.StyleName) If cs Is Nothing Then Console.WriteLine("Style not found: " & style.StyleName) cs = New ConvertionClass() With {.StartTag = "", .EndTag = ""} End If Dim cm As ConvertionClass Dim s As New Stack tmp = cs.StartTag For Each m In style.Modifiers cm = ht_Mod(m) If Not cm Is Nothing Then s.Push(cm) tmp &= cm.StartTag End If Next tmp &= buffer While s.Count > 0 AndAlso Not s.Peek Is Nothing cm = s.Pop tmp &= cm.EndTag End While tmp &= cs.EndTag Return tmp
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485