OfficeXml/README.md

301 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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; // 输出的是"页脚 字符"
```