1. 简介
Aspose.words 可以在不使用 Microsoft.Word 的情况下生成、修改、转换、打印文档。不依赖office组件,这一点给我们提供了极大的便利性,可以简单的引入 DLL(Dynamic Link Library,动态链接库文件) ,就可以操作 word 文档。不过也有一点小小的麻烦就是需要许可证 Aspose.Words.lic 。
2. word 文档结构
既然要操作 word 文档,就要先简单了解一下 word 对于 aspose 来说是一个怎样的对象。word 在 aspose中是一个 DOM(Document Object Model ,文档对象模型) 。我们先来看一下 word文档在aspose 中的模型图。
这上面节点的具体内容可以先暂时不用理会,到了具体的使用场景,如果你要修改一个页眉,可以查一下,页眉是属于哪一个节点,有哪些属性。我们先建立一个DOM 的大概理解 : 根节点为 Document ,最底层节点为 Run 的一棵树。
3. 查看 DOM
aspose提供了查看DOM 的工具 DocumentExplorer,可以在 github 中下载。地址:https://github.com/aspose-words/Aspose.Words-for-.NET 。左侧的是与文档结构对应的树,右侧是节点中的一些编码内容。看这棵树是为了对 DOM 有一个直观的认识。
4. 为了有更加深刻的理解,我们实现一个小案例
假设有一份word文档, 给定一个 BookMark(书签)的名称,怎么知道这个书签在文档中的位置。我们先简单定位到页数,也就是说已知 Document (文档) 这个对象,BookMark(书签)的名称,要求解 这个BookMark 在文档中的 PageIndex (页数)。
书签在word 中的样子。
这个界面告诉我们书签 有 名称、位置 两个变量。 可以执行 添加、删除、定位、隐藏操作。
接下来看一下书签在 aspose 中的实现。
在 Aspose 中书签的Name 对应名称,可以取值赋值,Text 是书签的值,这个在我们打开word 书签界面的时候没有直接输入的地方,可能可以通过其他方式赋值。BookmarkStart 书签开始的地方,BookmarkEnd 书签结束的地方。 有开始的位置也有结束的位置,这说明书签是一段内容。(更准确的说,是word的文档协议先定义了书签有开始标记和结束标记,aspose根据这个协议,才有了 BookmarkStart 和 BookmarkEnd),我们可以把一整页设为书签,也可以把一张图片设为一个书签。如果书签是一个光标,那么开始和结束标记都是这个光标所在的位置。我们已经有个整个文档的对象 Aspose.words.Document , 书签对象 Aspose.words.BookMark , 书签开始对象 Aspose.words.BookmarkStart, 书签结束对象 Aspose.words.BookmarkEnd。
一份文档中有很多书签,如何找到其中的一个。首先找到所有的 Bookmark
Aspose.Words.BookmarkCollection ,然后根据 bookmarkName找到某一个书签
要实现我们的Case(还记得吗,根据书签定位到页数),是不是应该要有一个 类似 int i = GetPageIndex ( BookMark bookmark ) 的 API 。好像还真有一个实现了类似功能的API 。 Aspose.Words.Layout.LayoutCollector (不要问我怎么知道这个API的) 。LayoutCollector 字面意思:布局收集器。可以理解为文档中所有节点位置信息的集合。
不过还有一个问题:参数是 Node,这又是一个怎样的存在,一个抽象类。
那么 Bookmark 与 Node 是什么关系。
要回答这个问题就要看 BookmarkStart 这个对象,它既是 Node,也是 Bookmark 开始的位置。
到这个时候感觉有好几个对象,有点乱,隐约有点关联。试着建立他们之间的联系。
这样我们可以从 Document(文档)——> Bookmark(书签) ——> BookmarkStart (书签开始节点) ——> Node(节点) ——> PageIndex (页数)
Document doc = new Document(string fileName); Bookmark bookmark = doc.Range.Bookmarks["bookmarkName"]; int pageIndex = new LayoutCollector(doc).GetStartPageIndex(bookmark.bookmarkStart);
这个地方我们定位到了一个书签开始位置的页数。有了页数,就可以用aspose 中的打印功能直接跳转到这一页显示。
这时,发现刚刚提的那个问题,好像有点不太对。
严格来说应该是: 一个书签在文档中开始的页数 和 结束的页数分别是多少。 当然大部分情况下都是在同一页的。我们实际生活中接触到的是夹在两页之间的书签。word 文档中的电子书签在这个定位的功能上有了扩展。可以定位一个字、一段话、一张图片、还有很多其他意想不到的作用。
5.查找关键API
整个过程中有个地方比较微妙,就是如何查找 public int GetStartPageIndex(Node node) 这个关键API,如果你时间比较仓促,那就在官方提供的Demo中 用VS打开,检索 bookmark 、Page、Index 这几个关键字,一个单词一个单词分别查,可能会查到很多相关的,每一条快速浏览一下,这个API就在其中,有时比较难发现。这种查找方式,需要Demo作者对方法的命名比较规范,命名要有意义,与所做的事对应起来。我们自己在开发过程中命名的时候,如果感觉一个方法命名很纠结,不能用一两个单词表达所做的事情,或者无论如何写注释,都感觉没讲清楚的时候,那很有可能这个方法本身就是有问题的,它做的事情太多了,需要拆分。如果你有比较充足的时间,那就把官方Demo的例子都跑一下,你要找的API,在你调试某一个例子的过程中可能会发现,当然也可能没有现成的方法能实现你的需求,那就用其他方法继续查,StackOverflow 也可以。各种尝试之后不行,可以回到源头,想一下这个需求是不是本身就有问题,是不是可以换个方式实现。“否定问题是解决问题最好的方法”。