OpenXml SDK学习笔记(1):Word的基本结构
能写多少篇我就不确定了,可能就这一篇就太监了,也有可能会写不少。
OpenXml SDK 相信很多人都不陌生,这个就是管Office一家的文档格式,Word, Excel, PowerPoint等都用到这个。并且,这个格式主要是给Word 2007以上使用的。如果是用到其中Excel部分,那建议直接使用NPOI这样的成品类库就行。
但是,NPOI FOR WORD真的是太难受了。当然,也不是说一定要用NPOI,现在成品的WORD操作库也不是没有,比如DocX。这个库基本算是 Xceed Words for .NET 的简化版。GitHub https://github.com/xceedsoftware/DocX 。而且,这个库非常牛逼的地方在于他是直接操作XML的,效率是上去了,可读性就下去了啊。要读懂这个,恐怕得对 ISO/IEC 29500 这个标准非常熟悉。那没救了,我是专精信息系统开发的,对这个标准的理解非常一般。而且说实话,这个标准里 95% 以上的内容我根本用不到,我又不用做一个Word,我只需要把我系统里的东西生成为一个Word显示出来就行了。
那于是,我痛定思痛,自己读文档吧:https://docs.microsoft.com/zh-cn/office/open-xml/open-xml-sdk (当然,英文的质量比中文可高多了,不过懒的看英文)。这文档写的可真是太专业了,想读懂它恐怕得要点技术水平。所以呢,我打算把这个文档给拆一下,做一个笔记。能写多少就随缘了,反正我把需求实现完了就不写了。恐怕两三篇就完事了。那第一篇讲的就是Word的基本结构。
一、WordprocessingML的理解
在看文档和使用的时候,就可以发现这样的一个命名空间:Wordprocessing。也可以看到这样的名词WordprocessingML。什么意思呢,Office家的这个产品叫Word,其作用是处理文字。所以,Wordprocessing翻译成 文字处理 就行了。对于OpenXml结构的docx文件那就是一个压缩包。你把后缀名从docx改成zip就可以用解压软件打开了。在其中,可以看到这样的结构:

这个结构里,第一个文件夹word就是我们要关注的内容,这个文件夹里是这样的:

有图片的话会更复杂一点,再多一个media文件夹,里面存着图片。不过这个无关紧要,本次我的需求只是简单的输出一个纯文档的证明文件。所以,不要管图片了。
在这里,重点需要注意注意的xml有两个,document.xml和styles.xml。他们分别对应着docx文件的样式部分和正文部分,大致就是这样的:

也就是说,如果我们需要通过代码编辑一个纯文本的Word,那就是修改这两个xml就可以了。甚至于,如果不需要搞样式的话,只要改docment.xml就行了。这两个Xml适用的标准就是 ISO/IEC 29500,并且这种Xml就称为:WordprocessingML。
但是,手写xml可太刑了。把整个 ISO/IEC 29500:2016 读完怕不是半条命就要去掉了。再等你把代码写完,恐怕你的工作就已经凉凉了。所以呢,微软自己出了个 OpenXml SDK 帮助开发者编辑这种Xml文件。不过呢,这玩意也是真的难用。而且说实话,里面一大片功能是根本用不着。说实话,日常使用的时候,也就是搞个样式,然后向里面添加文字,设置一下字体和段落样式,顶多插入点图片和表格。
对于我这种做普通的信息管理系统的人来说,图片和表格里都有大把的功能是完全用不着的。而且对于MIS的绝大部分用例而言,我都是只需要生成Word,然后由Word程序读取,而不需要由我来读一个Word模板,然后再向其中修改。当然,如果你会写了,读也不是什么太难的问题,只不过Word那个鬼程序里的Run真的是看不懂生成规律,经常会有乱七八糟的东西。所以,如果有碰到读模板再向里写的需求,我另外写一个笔记。
二、创建一个Word文件
打开VS,然后创建一个命令行程序,向里面添加一个名为“DocumentFormat.OpenXml”的Nuget包,这样项目的引用关系就做完了。然后添加以下代码:
 1 if (File.Exists("newDocx.docx"))
 2 {
 3     File.Delete("newDocx.docx");
 4 }
 5
 6 using (WordprocessingDocument doc = WordprocessingDocument.Create("newDocx.docx", DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
 7 {
 8     var main = doc.MainDocumentPart;
 9     if (doc.MainDocumentPart == null)
10     {
11         doc.AddMainDocumentPart();
12     }
13
14     if (doc.MainDocumentPart.Document == null)
15     {
16         doc.MainDocumentPart.Document = new Document();
17     }
18     var body = doc.MainDocumentPart.Document;
19
20     Paragraph para = new Paragraph();
21     Run r = new Run();
22     Text t = new Text();
23     t.Text = "Hello World";
24     r.Append(t);
25     para.Append(r);
26     body.Append(para);
27
28     doc.Save();
29 }
运行一下,就可以发现在运行目录下,出现了一个名为 newDocx.docx 的文件。这个文件打开,里面就一行 Hello World 文本。虽然,这个Hello World程序简单,但是要理解这个东西就 特!别!麻!烦!
首先,using语句块里,“WordprocessingDocument”对象 就是一个docx文档对象,也就是上文所述的那个压缩包。声明这个对象通常使用两种方法:Open和Create。非常好区别,一个是打开,一个是创建。
之后,“MainDocumentPart”这个属性就相当于压缩包里的“word”文件夹,非常的真实。
接着,“MainDocumentPart.Document”这个属性就相当于“word”文件夹下的“document.xml”文件,更真实了。
再下,“Paragraph”是一个段落。一份Word是由多个段落或者表格组成的。所以,在Word文档里,看到回车符,就可以认为是一个“Paragraph”对象的结束。

在“段落”里,有多个连续文本,也就是“Run”。一个Run就相当于Html里的span标签。比如,上文中,Hello World整个文本都是一样的格式。所以,就应当在一个Run里。写成Html大致是这样的感觉:
1 <p>
2 <span>Hello World</span>
3 </p>
但是,并不是所有情况都是这样的。在很多时候,一个段落的文本也是有不同的格式的。比如说,中文和英文的字体不一样,或者其它情况。比如,下文这样:

那这时,就需要对段落内的文本再进行拆分。上图的格式,写成Html大致是这样的感觉:
1 <p>
2 <span>某</span>
3 <span>个</span>
4 <span>大</span>
5 <span>学</span>
6 <span>大</span>
7 <span>学</span>
8 <span>生</span>
9 <span>创</span>
10 </p>
所以呢,这里每一个字都是一个Run。在Run里,则是正式的文字,相当于span的#innerText。但是,WordprocessingML要求你将这些文字放在名为Text的段落里。
至此,整个Word的基本结构就看懂了。Document里面有若干个Paragraph。每个Paragraph里,前后格式完全一样的文本放在一个Run里。前后格式不一样的文本放在不同的Run里。每一个Run里面再有若干个Text。那么,练习一下,下面的Word有几个Paragraph,几个Run?

这个就是我需求的一部分,我也没有截全。但是看的出来,是有两个段落。所以,两个Paragraph安排上。第一个Paragraph里,“兹证明”的格式是一样的,但是后面的下划线的文本是“空格符”,文本格式是“下划线”。他们的格式与前面的“兹证明”不一样。所以,哪怕再后续的“学院教师”与“兹证明”的格式相同,这里也得分成三个Run。再之后,又是一个下划线,再一个Run。再后,根据需求,这个括号也是三号宋体和文字的格式一样,所以“(教工号:”是一个Run。下划线再一个Run。“)指导项目如下:”一个Run。所以,第一段里就有7个Run。
第二段,就留给大家做练习了。在我截出来的部分,所有数字符号都是三号宋体,和文字一样。算一下多少个Run?具体过程我就略了,答案是5个。
OpenXml SDK学习笔记(1):Word的基本结构的更多相关文章
- OpenXml SDK学习笔记(4):设置文件级别的样式
		观察上一段日记最后的代码: 这里的样式基本可以理解为行内CSS.那么既然有行内的样式,就肯定有外部的样式.那这部分就对应笔记1里说的style.xml文件.这个文件对应的是Document.MainD ... 
- 2015.05.15,外语,学习笔记-《Word Power Made Easy》 01 “如何讨论人格特点”
		2015.03.17,外语,读书笔记-<Word Power Made Easy> 01 “如何讨论人格特点”学习笔记 SESSIONS 1 本来这些章节都是在一两年前学习的,现在趁给友人 ... 
- 【Intel AF 2.1 学习笔记一】AF程序结构
		Intel App Framework(原jqMobi)是用来开发hybrid app的开源免费框架,被intel收编之后发布了最新的2.1版本,最近正在学习.af的所谓程序结构,就是AF网页的架构, ... 
- OpenXml SDK 2.0 创建Word文档  添加页、段落、页眉和页脚
		using (WordprocessingDocument objWordDocument = WordprocessingDocument.Create(@"C:\********.doc ... 
- DirectX9.0c SDK学习笔记(一)
		Direct9.0c SDK中提供了一个叫DXviewer的*.x格式文件查看器的源码,代码给出了基于DXUT框架的模型显示接口使用方法, 对于我想编写一个动作捕捉的上位程序是大有助益的. 我的想法是 ... 
- RealThinClient SDK 学习笔记(1)
		从客户端调用远程函数的两种方法 1: RtcClientModule1.Prepare('select'); // call the "select" function on th ... 
- 2015.05.18,外语,学习笔记-《Word Power Made Easy》 03 “如何谈论不同从业者”
		Prefix Person,nous,etc. Practice,etc. Adjective psyche 精神 psychic ['saikik] adj.精神的n.灵媒 -logos 科研 ps ... 
- 2015.05.15,外语,学习笔记-《Word Power Made Easy》 02 “如何谈论医生”
		包括Sessions 4-6: Prefix Person,nous,etc. Practice,etc. Adjective internus内部 internist [ɪn'tɝnɪst] n.内 ... 
- Mysql学习笔记(二)对表结构的增删改查
		有将近一个星期都没有更新mysql了.相反linux的东西倒是学习不少.可能我个人情感上对linux更感兴趣一点.但mysql我也不烦,只是一旦将精力投入到了一样事情上去,就很难将精力分散去搞其他的东 ... 
随机推荐
- python风格对象
			对象表示形式 python提供了两种获取对象字符串表示形式的标准方式 repr() //便于开发者理解的方式返回对象的字符串表示形式(一般来说满足obj==eval(repr(obj) ... 
- python3.7+flask+alipay 支付宝付款功能
			文档参考github:https://github.com/fzlee/alipay/blob/master/docs/init.md 沙箱环境配置:https://opendocs.alipay.c ... 
- YbtOJ#643-机器决斗【贪心,李超树】
			正题 题目链接:https://www.ybtoj.com.cn/problem/643 题目大意 \(n\)个机器人,第\(i\)个攻击力为\(A_i\),防御为\(D_i\). 然后你每次可以对一 ... 
- 【C++ Primer Plus】编程练习答案——第3章
			1 void ch3_1() { 2 using namespace std; 3 unsigned int factor = 12; 4 unsigned int inch, feet; 5 cou ... 
- Java初步学习——2021.10.12每日总结,第六周周二
			(1)今天做了什么: (2)明天准备做什么? (3)遇到的问题,如何解决? 今天学习了菜鸟教程Java实例,数组 1.数组的排序和元素的查找--sort和binarySearch方法 import j ... 
- 如何利用Prometheus监控你的应用(此列子是对于golang sdk进行运用)
			Prometheus作为一套完整的开源监控接近方案,因为其诸多强大的特性以及生态的开放性,俨然已经成为了监控领域的事实标准并在全球范围内得到了广泛的部署应用.那么应该如何利用Prometheus对我们 ... 
- PTA习题6-8 统计一行文本的单词个数 (15分)
			参考<c和指针>里面运用strtok函数打印空白标记符(如\n,\t)的程序改写而成的代码 在之前我自己写了一个60行的链表版本的统计程序 相比之下这个strtok函数的程序要简洁明了的多 ... 
- Flink sql 之 TopN 与 StreamPhysicalRankRule (源码解析)
			基于flink1.14的源码做解析 公司内有很多业务方都在使用我们Flink sql平台做TopN的计算,今天同事突然问到我,Flink sql 是怎么实现topN的 ? 蒙圈了,这块源码没看过啊 , ... 
- (翻译)领域驱动设计实现-Implementing Domain Driven Design
			简介 Implementing Domain Driven Design 领域驱动设计实现 A practical guide for implementing the Domain Driven D ... 
- 【UE4】异步加载关卡 LoadingScreen ( 蓝图和C++ )
			一般先跳转到一个临时的关卡,然后异步加载目标关卡,同时展示Loading界面 对于含有流关卡的目标关卡,可以先载入子关卡 蓝图异步加载 无进度条 C++ 异步加载关卡 LoadPackageAsync ... 
