301 lines
9.8 KiB
Markdown
301 lines
9.8 KiB
Markdown
# OfficeXml
|
||
|
||
[迁移指南](./%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md)
|
||
|
||
## 概述
|
||
|
||
将 docx、pptx、xlsx 等文件中的 xml 转为 tsl 对象
|
||
|
||
```xml
|
||
<w:p w14:paraId="6E3ED3BE" w14:textId="77777777" w:rsidR="00C57A1E"
|
||
w:rsidRDefault="00C57A1E" w:rsidP="00C27AE9">
|
||
<w:pPr>
|
||
<w:jc w:val="left" />
|
||
</w:pPr>
|
||
<w:r>
|
||
<w:rPr>
|
||
<w:rFonts w:hint="eastAsia" />
|
||
</w:rPr>
|
||
<w:t>最小申购、赎回单位</w:t>
|
||
</w:r>
|
||
<w:r>
|
||
<w:rPr>
|
||
<w:rFonts w:hint="eastAsia" />
|
||
</w:rPr>
|
||
<w:t>(份)</w:t>
|
||
</w:r>
|
||
</w:p>
|
||
```
|
||
|
||
上述是一个 docx 中的段落的 xml,序列化为 tsl 过程如下
|
||
|
||
```go
|
||
uses DocxML; // 上述xml属于DocxML
|
||
p := new P(); // 创建一个P对象(段落w:p)
|
||
p.Init(node); // 假设node节点是上面的xml指向的Node对象
|
||
p.Deserialize(); // 将node对象的xml序列化到tsl对象
|
||
|
||
// 序列化完毕后,可直接对应取值
|
||
echo p.PPr.Jc.Val; // 输出:left
|
||
|
||
// 如果需要修改内容,通过Serialize()回写到node
|
||
p.PPr.Jc.Val := "center";
|
||
// 回写方法1,适合修改单个对象
|
||
p.PPr.Jc.Serialize();
|
||
// 回写方法2,大量修改,不同的对象
|
||
p.Serialize(); // 或p.PPr.Serialize()
|
||
|
||
// 在获取存在多个节点的对象时,比如上述的w:r对象是复数的,则需要通过Rs()获取
|
||
// 直接调用Rs()会获取所有的R对象,加上索引会获取第N+1个
|
||
echo p.Rs(1).T.Text; // 输出:(份)
|
||
```
|
||
|
||
## 基类
|
||
|
||
`OpenXmlAttribute`是节点的属性类
|
||
|
||
`OpenXmlElement`是节点基类
|
||
|
||
`OpenXmlSimpleType`是简单类型的元素节点类,继承于 `OpenXmlElement`,如 `<b />`
|
||
|
||
`OpenXmlTextElement`是包含文本内容的元素节点类,继承于 `OpenXmlElement`,如 `<t>文本</t>`
|
||
|
||
`OpenXmlCompositeElement`是复杂节点的元素节点类,继承于 `OpenXmlElement`,会包含一系列的属性或其他元素节点类
|
||
|
||
## 基本功能
|
||
|
||
### 获取属性/子节点
|
||
|
||
获取同名但命名空间不同的属性/子节点内容时,遵守以下规范
|
||
|
||
1. 默认情况下,获取的命名空间前缀与父节点一致
|
||
2. 当父节点有前缀,但是属性/子节点没有前缀时候,通过 `("")`空字符串参数获取
|
||
3. 存在同名属性/子节点情况下,默认获取的一定是和父节点前缀相同的内容
|
||
|
||
```xml
|
||
<m:r w:val="testw" val="testN" m:val="testm">
|
||
<m:rPr>
|
||
<m:sty m:val="p" />
|
||
</m:rPr>
|
||
<a:rPr lang="en-US"
|
||
altLang="zh-CN" sz="1100"
|
||
i="0" kern="1200">
|
||
<a:latin
|
||
typeface="Cambria Math"
|
||
panose="02040503050406030204"
|
||
pitchFamily="18"
|
||
charset="0" />
|
||
</a:rPr>
|
||
<m:t>cos</m:t>
|
||
</m:r>
|
||
```
|
||
|
||
```go
|
||
r.Val; // 默认获取前缀为m,即m:val的属性
|
||
r.Val("m"); // 指定前缀m
|
||
r.Val(""); // 无前缀的属性,即val
|
||
r.Val("w"); // 获取前缀为w,即w:val的属性
|
||
|
||
r.RPr; // 默认获取前缀为m,即m:rPr的子节点
|
||
r.RPr("m"); // 指定前缀为m
|
||
r.RPr("a"); // 获取前缀为a,即a:rPr的子节点
|
||
```
|
||
|
||
### 删除属性/节点
|
||
|
||
```go
|
||
移除方式一:通过RemoveAttribute或RemoveChild
|
||
// 移除r的属性w:val
|
||
// 因为r.Val("w")是直接获取了属性值,所以不能作为参数传递给RemoveAttribute
|
||
r.RemoveAttribute(r.Attribute("w:val"));
|
||
|
||
// 移除r的子节点rPr
|
||
// 因为r.RPr就是一个对象,所以可直接使用
|
||
r.RemoveChild(r.RPr);
|
||
|
||
移除方式二:通过赋值nil
|
||
// 这种方式不需要区分属性和子节点,也不需要知道属性的全称
|
||
r.Val("w") := nil;
|
||
r.RPr := nil;
|
||
|
||
最后需要回写才会生效
|
||
r.Serialize();
|
||
```
|
||
|
||
### 回落功能(Fallback)
|
||
|
||
项目的 `fallback`功能是指获取某个属性或节点不存在时候,会检查是否设置了 `fallback`,如果设置了会通过 `fallback`获取对应的属性或节点
|
||
|
||
```xml
|
||
// pPr1
|
||
<w:pPr>
|
||
<w:jc w:val="left" />
|
||
</w:pPr>
|
||
|
||
// pPr2
|
||
<w:pPr>
|
||
<w:jc w:val="left" />
|
||
<w:wordWrap w:val="1" />
|
||
</w:pPr>
|
||
```
|
||
|
||
```go
|
||
// 假设已经获取到了对象ppr1和ppr2
|
||
ppr1.SetFallback(ppr2); // 将ppr1的fallback设置为ppr2
|
||
ppr1.Jc.Val; // 得到"left"
|
||
ppr1.WordWrap.Val; // ppr1不存在wordWrap,但是ppr2存在wordWrap,所以回落到ppr2的wordWrap获取到"1"
|
||
```
|
||
|
||
## Unit 单元
|
||
|
||
- `DocxML`包含 `docx`文件独有的 xml 节点对象,一般 xml 的命名空间是 `w`,如 `w:p`
|
||
- `PptxML`包含 `pptx`文件独有的 xml 节点对象,一般 xml 的命名空间是 `p`,如 `p:spPr`
|
||
- `XlsxML`包含 `xlsx`文件独有的 xml 节点对象
|
||
- `DrawingML`包含 `docx,pptx,xlsx`文件图形的 xml 节点对象,一般 xml 的命名空间是 `a`,如 `a:xfrm`
|
||
- `SharedML`包含 `docx,pptx,xlsx`文件共有的 xml 节点对象
|
||
- `VML`
|
||
|
||
[参考链接 1](http://webapp.docx4java.org/OnlineDemo/ecma376/)
|
||
|
||
[参考链接 2](http://officeopenxml.com/index.php)
|
||
|
||
## 部件
|
||
|
||
### Components
|
||
|
||
一共有三个部件,分别是:
|
||
|
||
1. `DocxComponents.tsf`:docx 文件的各部分 xml 内容
|
||
2. `XlsxComponents`:xlsx 文件的各部分 xml 内容
|
||
3. `PptxComponents`:pptx 文件的各部分 xml 内容
|
||
|
||
以 `DocxComponents.tsf`为例,使用这个类,可以获取到对应的 docx 文件的 xml 对象
|
||
|
||
```go
|
||
component := new DocxComponents(); // 创建对象
|
||
component.Open("", "xxx.docx"); // 打开文件
|
||
document := component.Document; // 获取document.xml,生成Document对象
|
||
document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中
|
||
document.Body.Elements(); // 可以获取document的body下的所有对象列表
|
||
// 反序列化后,可进行读写
|
||
|
||
styles := component.Styles; // 获取styles.xml,生成Styles对象
|
||
```
|
||
|
||
document.xml 内容如下
|
||
|
||
```xml
|
||
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
|
||
xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex"
|
||
xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex"
|
||
mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16du wp14">
|
||
<w:body>
|
||
<w:p w14:paraId="7B19BB62" w14:textId="33E8D5E1" w:rsidR="00B118EF"
|
||
w:rsidRDefault="00B118EF" w:rsidP="00B118EF">
|
||
<w:pPr>
|
||
<w:tabs>
|
||
<w:tab w:val="left" w:pos="5670" />
|
||
</w:tabs>
|
||
</w:pPr>
|
||
<w:r>
|
||
<w:rPr>
|
||
<w:rFonts w:hint="eastAsia" />
|
||
</w:rPr>
|
||
<w:t>分栏前</w:t>
|
||
</w:r>
|
||
</w:p>
|
||
<w:sectPr w:rsidR="00B118EF" w:rsidSect="002E4343">
|
||
<w:type w:val="continuous" />
|
||
<w:pgSz w:w="11906" w:h="16838" w:code="9" />
|
||
<w:pgMar w:top="1440" w:right="1797" w:bottom="1440" w:left="1797" w:header="851"
|
||
w:footer="992" w:gutter="0" />
|
||
<w:cols w:space="720" />
|
||
<w:docGrid w:type="linesAndChars" w:linePitch="312" />
|
||
</w:sectPr>
|
||
</w:body>
|
||
</w:document>
|
||
```
|
||
|
||
### 单位装饰器 UnitDecorator
|
||
|
||
每个对象都有一个单位装饰器,能统一转成磅(point)单位(如果有配置属性转换),还能保留原来的接口
|
||
|
||
每个 `ML`都有装饰器 `tsf`统一命名是 `Unit的名称+UnitDecorator`,如 `docx`(`docx`大部分 `xml`隶属于 `DocxML`)的 `SectPr`对象的装饰器是 `SectPrUnitDecorator`
|
||
|
||
如:有下面一段 xml,其中的 `pgSz.w = "11906", pgSz.h = "16838"`都需要转换成 point
|
||
|
||
```xml
|
||
<w:sectPr w:rsidR="00B118EF" w:rsidSect="002E4343">
|
||
<w:type w:val="continuous" />
|
||
<w:pgSz w:w="11906" w:h="16838" w:code="9" />
|
||
<w:pgMar w:top="1440" w:right="1797" w:bottom="1440" w:left="1797" w:header="851"
|
||
w:footer="992" w:gutter="0" />
|
||
<w:cols w:space="720" />
|
||
<w:docGrid w:type="linesAndChars" w:linePitch="312" />
|
||
</w:sectPr>
|
||
```
|
||
|
||
```go
|
||
uses DocxMLUnitDecorator;
|
||
component := new DocxComponents(); // 创建对象
|
||
component.Open("", "xxx.docx"); // 打开文件
|
||
document := component.Document; // 获取document.xml,生成Document对象
|
||
document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中
|
||
|
||
sect_pr := document.Body.SectPr; // 获取SectPr对象
|
||
sect_pr_unit_decorator := new SectPrUnitDecorator(sect_pr); // 装饰器构造需要原本的对象
|
||
echo "w = ", sect_pr.PgSz.W; // 输出的是字符串,原本的单位是twips
|
||
echo "\n";
|
||
echo "w = ", sect_pr_unit_decorator.PgSz.W; // 此时输出的是数字类型,单位是point
|
||
```
|
||
|
||
### 适配器 Adapter
|
||
|
||
适配器是通过 key 获取对应的对象,比如样式可以通过样式 ID 获取对应的样式对象
|
||
|
||
只有部分对象才有适配器(具体可见 `autounit/xxxMLAdapter`),比如 `DocxML.Styles`的适配器是 `StylesAdapter`
|
||
|
||
styles.xml 部分如下
|
||
|
||
```xml
|
||
<w:styles xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
|
||
<w:style w:type="character" w:customStyle="1" w:styleId="a4">
|
||
<w:name w:val="页眉 字符" />
|
||
<w:basedOn w:val="a0" />
|
||
<w:link w:val="a3" />
|
||
<w:uiPriority w:val="99" />
|
||
<w:rsid w:val="00B118EF" />
|
||
<w:rPr>
|
||
<w:sz w:val="18" />
|
||
<w:szCs w:val="18" />
|
||
</w:rPr>
|
||
</w:style>
|
||
<w:style w:type="character" w:customStyle="1" w:styleId="a6">
|
||
<w:name w:val="页脚 字符" />
|
||
<w:basedOn w:val="a0" />
|
||
<w:link w:val="a5" />
|
||
<w:uiPriority w:val="99" />
|
||
<w:rsid w:val="00B118EF" />
|
||
<w:rPr>
|
||
<w:sz w:val="18" />
|
||
<w:szCs w:val="18" />
|
||
</w:rPr>
|
||
</w:style>
|
||
</w:styles>
|
||
```
|
||
|
||
```go
|
||
uses DocxMLAdapter;
|
||
component := new DocxComponents(); // 创建对象
|
||
component.Open("", "xxx.docx"); // 打开文件
|
||
document := component.Document; // 获取document.xml,生成Document对象
|
||
document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中
|
||
|
||
styles := document.Styles;
|
||
// 现在需要通过styleId获取Style对象
|
||
styles_adapter := new StylesAdapter(styles);
|
||
// 通过StyleId获取Style对象
|
||
style := styles_adapter.GetStyleByStyleId("a6");
|
||
echo style.Name; // 输出的是"页脚 字符"
|
||
```
|