Docx 转换器





5.00/5 (2投票s)
这是一个支持 CSS 样式自定义的 DOCX 到 HTML 转换工具。
目录
引言
架构
参考文献
注释
技术选型
关于语言/本地化
关注点
限制
结论
引言
这个项目的初衷是为了简化和自动化公司文档的发布。
我们有很多 Word 文档需要发布到网上,但必须先进行转换。Microsoft Word 有导出为 HTML 的功能,但我们需要应用自己的样式表,而且对于不同的主题需要应用不同的样式表。
基于这些需求,我实现了一个小原型,它可能还有改进的空间。
架构
这只是一个原型,与 Microsoft Word 的所有功能相比,它的功能有限,但我已经用不同的样式转换了多个文档,结果是令人满意的。我也用它来编写要发布到 CMS 或提交到 CodeProject 的文档 :-)
转换过程包括扫描 Word 文档,按阅读顺序提取每个单词或段落的样式属性,找到匹配的 HTML 标签,然后渲染成 HTML 文件。
参考资料
读取 docx 文档并不简单,它是一个 XML 文件,因此是可读的,但……看看规范的大小。
非常大。但在研究了几天之后,我能够理解一些东西并取得了一些好结果。
首先,docx 格式是一个 zip 压缩包。你可以使用自定义库来打开它,但也可以使用 .net Framework 3.0 提供的 PresentationCore 程序集中的 “System.IO.Packaging” 命名空间中的类。后者非常有用,也是我在本项目中使用的,因为它还实现了管理和检索包“关系”的所有功能。
实际上,这个包的结构是一个目录树,其中包含不同的文件夹和文件。
文件可以相互关联,这种关系也在包中定义和存储。
我解释这一点是因为在我转换的许多文档中都嵌入了图片,而在包中,图片是与文档部分有关系的部件。
其次,我想分析的主要文件是包含文本的文件。这是 “\word\document.xml”。这个文件描述了文档的内容,然后,你可以逐个元素地扫描文档的所有部分。
第三,从 OOXML 规范中获取信息
“WordprocessingML 文档的基础是其实际文本内容。这些文本内容可以存储在许多上下文中(表格、文本框等),但 WordprocessingML 中最基本的文本内容形式是段落,使用 p 元素(§2.3.1.22)指定。在段落内,所有段落级别的富格式都存储在 pPr 元素(§2.3.1.25;§2.3.1.26)中。[注意:段落属性的一些示例包括对齐、边框、断字覆盖、缩进、行距、底纹、文本方向和孤行控制。]在段落内,文本被分组到一个或多个 run 中,用 r 元素(§2.3.2.23)表示,它定义了一个具有共同属性集的文本区域。”
注意事项
技术选型
段落可以有自己的样式,但其中的 run 可以用更具体的样式覆盖该样式。因此,存在两个级别的样式。区分应用于样式(正常、标题、列表编号…)和文本修饰符(粗体、下划线、斜体…)很有用。
关于语言/本地化
该程序通过字面匹配 Word 文档中指定的样式名称与映射文件中指定的样式名称来工作。将 Word 文档打开并保存为不同语言,所有样式名称都会被翻译成新的 Word 语言。我附加的演示文件是用英文版 Word 保存的,样式名称仍然是英文(Normal, Heading1, Quote, Subtitle…),但如果我用意大利语版保存文档,样式名称将被翻译成意大利语(Normale, Titolo1, Citazione, sottotitolo…)。
亮点
我创建了一个结构,它将具有相同格式的所有文本保存在一个缓冲区中,并在格式更改时清空缓冲区。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()
Get
Return p_modifiers.Values
End Get
End Property
Public Shadows Function ToString() As String
Dim tmp As String
tmp = ""
For Each m As String In p_modifiers.Values
tmp &= m
Next
Return StyleName & "|" & tmp
End Function
End Class
清空缓冲区也意味着向文本添加一个 HTML 标签。我创建了一个表,其中包含 Word 样式的列表及其匹配的“HTML 开始标签”和“HTML 结束标签”。
|
|
|
正常 |
<p> |
</p> |
Heading1 |
<h1> |
</h1> |
代码 |
<text style=””> |
</text> |
… |
以及一个用于文本修饰符的表。
|
|
|
粗体 |
<b> |
</b> |
斜体 |
<i> |
</i> |
下划线 |
<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 'temp buffer
'searching in map table
Dim cs As ConvertionClass
cs = 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
局限性
此项目可以转换简单的文档。
- 它可以读取和理解文档中使用的所有样式,
- 它可以处理“目录”(将其转换为锚点名称),
- 它可以转换简单的项目符号列表,
- 它可以转换简单的表格,
- 它可以转换外部超链接。
转换样式 XML 文件必须作为第二个参数在命令行中指定。
转换结果是一个 HTML 文件,保存在相对文件夹 “.\CDATA\” 中。如果文档包含图片,它们将保存在相对文件夹 “.\CDATA\img\” 中,并通过 img 标签作为源进行引用。
我已经用 Microsoft Word 2010 和 2013 创建的文档进行了测试。
结论
这篇文章是使用此工具发布的。
我用 Microsoft Word 写了这篇文章,为 CodeProject 准备了一个转换表,最后我复制/粘贴了转换后的文档。它奏效了! :-P
本文仅展示了一种读取和转换 docx 文档的技术。与所有 OOXML 规范相比,它不会是详尽的。如果有人需要支持,请通过 Gekoproject.com 与我联系。