更新README.md,完善表格操作功能。

This commit is contained in:
zero
2025-05-29 17:57:36 +08:00
parent 114dee00e0
commit ce3f23cb36
17 changed files with 3326 additions and 551 deletions

599
README.md
View File

@@ -18,6 +18,59 @@ WordZero 是一个使用 Golang 实现的 Word 文档操作库,提供基础的
-**高性能**: 零依赖的纯Go实现内存占用低 -**高性能**: 零依赖的纯Go实现内存占用低
- 🔧 **易于使用**: 简洁的API设计链式调用支持 - 🔧 **易于使用**: 简洁的API设计链式调用支持
## 安装
```bash
go get github.com/ZeroHawkeye/wordZero
```
## 项目结构
```
wordZero/
├── pkg/ # 公共包
│ ├── document/ # 文档核心操作
│ │ ├── document.go # 主要文档操作API
│ │ ├── table.go # 表格操作功能
│ │ ├── errors.go # 错误定义和处理
│ │ ├── logger.go # 日志系统
│ │ ├── doc.go # 包文档
│ │ ├── document_test.go # 文档操作单元测试
│ │ └── table_test.go # 表格功能单元测试
│ └── style/ # 样式管理系统
│ ├── style.go # 样式核心定义
│ ├── api.go # 快速API接口
│ ├── predefined.go # 预定义样式常量
│ ├── api_test.go # API测试
│ ├── style_test.go # 样式系统测试
│ └── README.md # 样式系统详细文档
├── examples/ # 使用示例
│ ├── basic/ # 基础功能示例
│ │ └── basic_example.go
│ ├── formatting/ # 格式化示例
│ │ └── text_formatting_example.go
│ ├── style_demo/ # 样式系统演示
│ │ └── style_demo.go
│ ├── table/ # 表格功能示例
│ │ └── table_example.go
│ ├── table_layout/ # 表格布局和尺寸示例
│ │ └── main.go
│ ├── table_style/ # 表格样式和外观示例
│ │ └── main.go
│ ├── cell_advanced/ # 单元格高级功能示例
│ │ └── main.go
│ ├── basic_usage.go # 基础使用示例
│ └── output/ # 示例输出文件目录
├── test/ # 集成测试文件
│ ├── document_test.go # 文档操作集成测试
│ ├── text_formatting_test.go # 文本格式化集成测试
│ └── table_style_test.go # 表格样式功能集成测试
├── .gitignore # Git忽略文件配置
├── go.mod # Go模块定义
├── LICENSE # MIT许可证
└── README.md # 项目说明文档
```
## 功能特性 ## 功能特性
### ✅ 已实现功能 ### ✅ 已实现功能
@@ -53,8 +106,6 @@ WordZero 是一个使用 Golang 实现的 Word 文档操作库,提供基础的
> **样式数量说明:** 系统内置18个预定义样式15个段落样式 + 3个字符样式。演示程序中显示的21个样式是因为动态创建了3个自定义样式进行功能展示。 > **样式数量说明:** 系统内置18个预定义样式15个段落样式 + 3个字符样式。演示程序中显示的21个样式是因为动态创建了3个自定义样式进行功能展示。
### 🚧 规划中功能
#### 表格功能 #### 表格功能
##### 表格基础操作 ##### 表格基础操作
@@ -85,52 +136,63 @@ WordZero 是一个使用 Golang 实现的 Word 文档操作库,提供基础的
- [x] 单元格文字方向和旋转 - [x] 单元格文字方向和旋转
- [x] 单元格内边距设置 - [x] 单元格内边距设置
##### 表格样式和外观
- [ ] 表格整体样式
- [ ] 预定义表格样式模板
- [ ] 自定义表格样式创建
- [ ] 表格主题色彩应用
- [ ] 表格样式继承和覆盖
- [ ] 表格边框设置
- [ ] 外边框样式(线型、颜色、粗细)
- [ ] 内边框样式(网格线设置)
- [ ] 单元格边框独立设置
- [ ] 边框部分应用(顶部、底部、左右)
- [ ] 无边框表格支持
- [ ] 表格背景和填充
- [ ] 表格背景色设置
- [ ] 单元格背景色设置
- [ ] 奇偶行颜色交替
- [ ] 渐变背景支持(基础渐变)
- [ ] 图案填充支持
##### 表格布局和尺寸 ##### 表格布局和尺寸
- [x] 表格尺寸控制 - [x] 表格尺寸控制
- [x] 表格总宽度设置(固定宽度、相对宽度、自动宽度) - [x] 表格总宽度设置(固定宽度、相对宽度、自动宽度)
- [x] 列宽设置(固定宽度、相对宽度、自动调整) - [x] 列宽设置(固定宽度、相对宽度、自动调整)
- [ ] 行高设置(固定高度、最小高度、自动调整) - [x] 行高设置(固定高度、最小高度、自动调整)
- [x] 单元格尺寸精确控制 - [x] 单元格尺寸精确控制
- [ ] 表格对齐和定位 - [x] 表格对齐和定位
- [x] 表格页面对齐(左对齐、居中、右对齐) - [x] 表格页面对齐(左对齐、居中、右对齐)
- [ ] 表格文字环绕设置 - [x] 表格文字环绕设置
- [ ] 表格相对定位 - [x] 表格相对定位
- [ ] 表格分页控制 - [x] 表格分页控制
- [ ] 表格跨页处理 - [x] 表格跨页处理
- [ ] 标题行重复显示 - [x] 标题行重复显示
- [ ] 表格分页符控制 - [x] 表格分页符控制
- [ ] 避免分页的行设置 - [x] 避免分页的行设置
##### 表格数据处理 ##### 表格数据处理
- [x] 数据导入导出 - [x] 数据导入导出
- [x] 二维数组数据绑定 - [x] 二维数组数据绑定
- [x] 表格数据提取为数组 - [x] 表格数据提取为数组
- [x] 批量数据填充 - [x] 批量数据填充
##### 表格访问和查询
- [x] 表格查找和定位
- [x] 按索引获取表格
- [x] 表格位置信息获取
- [x] 单元格访问接口
- [x] 按行列索引访问
##### 表格样式和外观
- [x] 表格整体样式
- [x] 预定义表格样式模板
- [x] 自定义表格样式创建
- [x] 表格主题色彩应用
- [x] 表格样式继承和覆盖
- [x] 表格边框设置
- [x] 外边框样式(线型、颜色、粗细)
- [x] 内边框样式(网格线设置)
- [x] 单元格边框独立设置
- [x] 边框部分应用(顶部、底部、左右)
- [x] 无边框表格支持
- [x] 表格背景和填充
- [x] 表格背景色设置
- [x] 单元格背景色设置
- [x] 奇偶行颜色交替
- [x] 渐变背景支持(基础渐变)
- [x] 图案填充支持
### 🚧 规划中功能
#### 表格功能完善
##### 高级表格功能
- [ ] 表格排序功能Word内置排序 - [ ] 表格排序功能Word内置排序
- [ ] 单列排序(升序、降序) - [ ] 单列排序(升序、降序)
- [ ] 多列排序 - [ ] 多列排序
- [ ] 保持标题行不参与排序 - [ ] 保持标题行不参与排序
##### 高级表格功能
- [ ] 表格标题和说明 - [ ] 表格标题和说明
- [ ] 表格标题设置(表格上方、下方) - [ ] 表格标题设置(表格上方、下方)
- [ ] 表格标题编号自动生成 - [ ] 表格标题编号自动生成
@@ -142,14 +204,8 @@ WordZero 是一个使用 Golang 实现的 Word 文档操作库,提供基础的
- [ ] 常用表格模板库 - [ ] 常用表格模板库
- [ ] 自定义模板保存 - [ ] 自定义模板保存
- [ ] 模板快速应用 - [ ] 模板快速应用
- [ ] 表格访问增强
##### 表格访问和查询
- [x] 表格查找和定位
- [x] 按索引获取表格
- [ ] 按标题查找表格 - [ ] 按标题查找表格
- [x] 表格位置信息获取
- [x] 单元格访问接口
- [x] 按行列索引访问
- [ ] 按范围批量访问 - [ ] 按范围批量访问
- [ ] 单元格遍历迭代器 - [ ] 单元格遍历迭代器
@@ -174,463 +230,52 @@ WordZero 是一个使用 Golang 实现的 Word 文档操作库,提供基础的
- [ ] 列表和编号 - [ ] 列表和编号
- [ ] 脚注和尾注 - [ ] 脚注和尾注
## 安装
```bash
go get github.com/ZeroHawkeye/wordZero
```
## 快速开始
### 基础文档创建
```go
package main
import (
"log"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
// 创建新文档
doc := document.New()
// 添加段落
doc.AddParagraph("Hello, World!")
doc.AddParagraph("这是使用 WordZero 创建的文档。")
// 保存文档
err := doc.Save("output.docx")
if err != nil {
log.Fatal(err)
}
}
```
### 使用标题样式(支持导航窗格)
```go
doc := document.New()
// 添加文档标题
doc.AddParagraph("WordZero 使用指南").SetAlignment(document.AlignCenter)
// 使用标题样式 - 这些标题将出现在Word导航窗格中
doc.AddHeadingParagraph("第一章:概述", 1) // Heading1
doc.AddHeadingParagraph("1.1 项目介绍", 2) // Heading2
doc.AddHeadingParagraph("1.1.1 核心特性", 3) // Heading3
// 添加正文内容
doc.AddParagraph("WordZero是一个功能强大的Word文档操作库...")
doc.AddHeadingParagraph("第二章:安装和配置", 1) // Heading1
doc.AddHeadingParagraph("2.1 环境要求", 2) // Heading2
doc.Save("guide.docx")
```
### 高级文本格式化
```go
doc := document.New()
// 创建格式化标题
titleFormat := &document.TextFormat{
Bold: true,
FontSize: 18,
FontColor: "FF0000", // 红色
FontName: "微软雅黑",
}
title := doc.AddFormattedParagraph("格式化标题", titleFormat)
title.SetAlignment(document.AlignCenter)
// 设置段落间距
spacingConfig := &document.SpacingConfig{
LineSpacing: 1.5, // 1.5倍行距
BeforePara: 12, // 段前12磅
AfterPara: 6, // 段后6磅
FirstLineIndent: 24, // 首行缩进24磅
}
para := doc.AddParagraph("这个段落有特定的间距设置")
para.SetSpacing(spacingConfig)
para.SetAlignment(document.AlignJustify) // 两端对齐
// 混合格式段落
mixed := doc.AddParagraph("这段文字包含")
mixed.AddFormattedText("粗体蓝色", &document.TextFormat{
Bold: true, FontColor: "0000FF"})
mixed.AddFormattedText(",普通文本,", nil)
mixed.AddFormattedText("斜体绿色", &document.TextFormat{
Italic: true, FontColor: "00FF00"})
doc.Save("formatted.docx")
```
### 样式系统使用
```go
import "github.com/ZeroHawkeye/wordZero/pkg/style"
doc := document.New()
styleManager := doc.GetStyleManager()
quickAPI := style.NewQuickStyleAPI(styleManager)
// 查看所有可用样式
allStyles := quickAPI.GetAllStylesInfo()
for _, styleInfo := range allStyles {
fmt.Printf("样式: %s (%s) - %s\n",
styleInfo.Name, styleInfo.ID, styleInfo.Description)
}
// 使用预定义样式创建段落
para := doc.AddParagraph("这是引用文本")
para.SetStyle("Quote") // 应用引用样式
// 创建自定义样式
config := style.QuickStyleConfig{
ID: "MyCustomStyle",
Name: "我的自定义样式",
Type: style.StyleTypeParagraph,
BasedOn: "Normal",
ParagraphConfig: &style.QuickParagraphConfig{
Alignment: "center",
LineSpacing: 2.0,
SpaceBefore: 15,
},
RunConfig: &style.QuickRunConfig{
FontName: "华文宋体",
FontSize: 14,
FontColor: "2F5496",
Bold: true,
},
}
customStyle, err := quickAPI.CreateQuickStyle(config)
if err == nil {
// 应用自定义样式
customPara := doc.AddParagraph("使用自定义样式的段落")
customPara.SetStyle("MyCustomStyle")
}
doc.Save("styled.docx")
```
### 高级表格功能
```go
package main
import (
"log"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
doc := document.New()
// 1. 创建基础表格
config := &document.TableConfig{
Rows: 4,
Cols: 4,
Width: 8000,
Data: [][]string{
{"学号", "姓名", "语文", "数学"},
{"001", "张三", "85", "92"},
{"002", "李四", "78", "88"},
{"003", "王五", "90", "85"},
},
}
table := doc.AddTable(config)
// 2. 设置表头格式
headerFormat := &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontSize: 14,
FontColor: "FFFFFF", // 白色文字
FontName: "微软雅黑",
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
}
// 为第一行设置表头格式
for col := 0; col < 4; col++ {
table.SetCellFormat(0, col, headerFormat)
}
// 3. 单元格富文本
table.SetCellFormattedText(1, 1, "张三", &document.TextFormat{
Bold: true,
FontColor: "FF0000",
})
// 在同一单元格添加不同格式的文本
table.AddCellFormattedText(1, 1, " (优秀)", &document.TextFormat{
Italic: true,
FontColor: "00FF00",
FontSize: 10,
})
// 4. 单元格合并
// 水平合并
table.MergeCellsHorizontal(0, 2, 3) // 合并表头的"语文"和"数学"列
table.SetCellText(0, 2, "成绩")
// 垂直合并
table.MergeCellsVertical(1, 3, 0) // 合并学号列
table.SetCellText(1, 0, "2024级")
// 区域合并2x2区域
mergeTable := doc.AddTable(&document.TableConfig{Rows: 4, Cols: 4, Width: 6000})
mergeTable.MergeCellsRange(1, 2, 1, 2) // 合并中间2x2区域
mergeTable.SetCellText(1, 1, "合并区域")
// 5. 检查和取消合并
isMerged, _ := table.IsCellMerged(0, 2)
if isMerged {
// 获取合并信息
mergeInfo, _ := table.GetMergedCellInfo(0, 2)
log.Printf("合并信息: %+v", mergeInfo)
// 可以选择取消合并
// table.UnmergeCells(0, 2)
}
// 6. 内容和格式操作
// 清空内容但保留格式
table.ClearCellContent(1, 2)
table.SetCellText(1, 2, "90")
// 清空格式但保留内容
table.ClearCellFormat(1, 3)
// 7. 设置单元格内边距
table.SetCellPadding(0, 0, 10) // 10磅内边距
// 8. 设置单元格文字方向
// 设置垂直文字(从上到下)
table.SetCellTextDirection(1, 0, document.TextDirectionTB)
// 通过CellFormat设置完整格式包括文字方向
verticalFormat := &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontSize: 14,
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
TextDirection: document.TextDirectionTB, // 从上到下
}
table.SetCellFormat(1, 1, verticalFormat)
table.SetCellText(1, 1, "竖排文字")
doc.Save("advanced_table.docx")
}
```
## 项目结构
```
wordZero/
├── pkg/ # 公共包
│ ├── document/ # 文档核心操作
│ │ ├── document.go # 主要文档操作API
│ │ ├── errors.go # 错误定义和处理
│ │ ├── logger.go # 日志系统
│ │ ├── doc.go # 包文档
│ │ └── document_test.go # 单元测试
│ └── style/ # 样式管理系统
│ ├── style.go # 样式核心定义
│ ├── api.go # 快速API接口
│ ├── predefined.go # 预定义样式常量
│ ├── api_test.go # API测试
│ ├── style_test.go # 样式系统测试
│ └── README.md # 样式系统详细文档
├── examples/ # 使用示例
│ ├── basic/ # 基础功能示例
│ │ └── basic_example.go
│ ├── formatting/ # 格式化示例
│ ├── style_demo/ # 样式系统演示
│ │ └── style_demo.go
│ └── output/ # 示例输出文件
├── test/ # 测试文件
├── go.mod # Go模块定义
├── LICENSE # MIT许可证
└── README.md # 项目说明文档
```
## 使用示例 ## 使用示例
### 基础功能演示 查看 `examples/` 目录下的示例代码:
运行基础示例: - `examples/basic/` - 基础功能演示
- `examples/style_demo/` - 样式系统演示
- `examples/table/` - 表格功能演示
- `examples/table_layout/` - 表格布局和尺寸演示
- `examples/formatting/` - 格式化演示
运行示例:
```bash ```bash
go run ./examples/basic/
```
这个示例展示了:
- 文档和标题创建
- 各种预定义样式的使用
- 文本格式化和混合格式
- 代码块和引用样式
- 列表段落的创建
### 完整样式系统演示
运行完整样式演示:
```bash
go run ./examples/style_demo/
```
这个示例展示了:
- 所有18种预定义样式
- 样式继承机制演示
- 自定义样式创建
- 样式查询和管理功能
- XML转换演示
### 读取现有文档
```go
doc, err := document.Open("existing.docx")
if err != nil {
log.Fatal(err)
}
// 读取段落内容
fmt.Printf("文档包含 %d 个段落\n", len(doc.Body.Paragraphs))
for i, para := range doc.Body.Paragraphs {
fmt.Printf("段落 %d: ", i+1)
for _, run := range para.Runs {
fmt.Print(run.Text.Content)
}
fmt.Println()
}
```
### 命令行使用
运行演示程序:
```bash
# 运行完整演示
go run main.go
# 运行基础功能演示 # 运行基础功能演示
go run ./examples/basic/ go run ./examples/basic/
# 运行样式演示 # 运行样式演示
go run ./examples/style_demo/ go run ./examples/style_demo/
# 运行表格演示
go run ./examples/table/
# 运行表格布局和尺寸演示
go run ./examples/table_layout/
# 运行格式化演示 # 运行格式化演示
go run ./examples/formatting/ go run ./examples/formatting/
``` ```
## 测试 ## 开发指南
### 运行测试 ### 项目开发规则
```bash 1. **项目结构**: 符合golang依赖库的项目结构完善的测试流程
# 运行所有测试 2. **模块化设计**: 规范的项目结构,避免文件循环依赖和过渡耦合
go test ./... 3. **测试规范**: 测试用例尽量放在test目录下
4. **文档维护**: 每次完成一个功能点需更新README文件的待办列表
# 运行特定包测试 ### 贡献指南
go test ./pkg/document/
go test ./pkg/style/
# 运行测试并显示覆盖率 欢迎提交 Issue 和 Pull Request在提交代码前请确保
go test -cover ./...
# 生成详细的测试报告 1. 代码符合 Go 代码规范
go test -v -coverprofile=coverage.out ./... 2. 添加必要的测试用例
go tool cover -html=coverage.out 3. 更新相关文档
``` 4. 确保所有测试通过
### 测试覆盖
- **文档操作**: 基础CRUD操作、文本格式化、段落属性
- **样式系统**: 预定义样式、自定义样式、样式继承
- **文件处理**: ZIP压缩/解压、XML序列化/反序列化
- **错误处理**: 各种异常情况和边界条件
## API 文档
详细的API文档请参考
- [文档操作API](pkg/document/) - 核心文档操作功能
- [样式系统API](pkg/style/) - 完整的样式管理系统
## 开发进度
### 当前版本: v0.3.0
#### v0.3.0 新增功能
- ✅ 完整的标题样式系统Heading1-9
- ✅ Word导航窗格支持
- ✅ 18种预定义样式系统内置样式
- ✅ 自定义样式创建和管理
- ✅ 样式继承机制
- ✅ 快速样式API
#### v0.2.0 功能
- ✅ 基础文档创建和读取
- ✅ 文本格式化支持
- ✅ 段落属性设置
- ✅ 混合格式文本
#### v0.1.0 功能
- ✅ 项目初始化
- ✅ OOXML基础架构
- ✅ ZIP文件处理
### 下一版本计划: v0.4.0
- 🚧 表格功能
- 🚧 图片插入
- 🚧 列表和编号
- 🚧 页面设置
## 贡献指南
欢迎贡献代码!请确保:
1. 所有新功能都有相应的单元测试
2. 代码符合Go语言规范
3. 提交前运行完整测试套件
4. 更新相关文档
## 许可证 ## 许可证
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件 本项目采用 MIT 许可证详见 [LICENSE](LICENSE) 文件
## 更新日志
### 2025-05-29 单元格文字方向功能实现
- ✅ 实现单元格文字方向设置功能支持6种方向
- `TextDirectionLR`:从左到右(默认)
- `TextDirectionTB`:从上到下
- `TextDirectionBT`:从下到上
- `TextDirectionRL`:从右到左
- `TextDirectionTBV`:从上到下,垂直显示
- `TextDirectionBTV`:从下到上,垂直显示
- ✅ 添加 `SetCellTextDirection()``GetCellTextDirection()` 方法
- ✅ 扩展 `CellFormat` 结构支持文字方向属性
- ✅ 添加完整的测试用例和演示程序
- ✅ 更新README文档和使用示例
### 2025-05-29 测试修复
- ✅ 修复 `TestComplexDocument` 测试调整期望段落数量从7改为6与实际创建的段落数量一致
- ✅ 修复 `TestErrorHandling` 测试:改进无效路径测试策略,确保在不同操作系统下都能正确测试错误处理
- ✅ 验证所有测试用例均通过,确保代码质量和功能稳定性
- ✅ 问题根因:测试用例期望值与实际实现不符,已修正测试逻辑
### 测试状态总结
- **总测试数量**: 20+ 个测试用例
- **覆盖模块**: document操作、style管理、格式化功能、错误处理
- **通过率**: 100%
- **测试结论**: 代码实现正确,测试用例已修复
## 致谢
- Office Open XML 规范
- Go语言社区的优秀库和工具

View File

@@ -111,7 +111,7 @@ doc.Save("example.docx")`
stylesInfo.SetStyle(style.StyleNormal) stylesInfo.SetStyle(style.StyleNormal)
// 确保输出目录存在 // 确保输出目录存在
outputFile := "../output/basic_example.docx" outputFile := "examples/output/basic_example.docx"
outputDir := filepath.Dir(outputFile) outputDir := filepath.Dir(outputFile)
fmt.Printf("📁 检查输出目录: %s\n", outputDir) fmt.Printf("📁 检查输出目录: %s\n", outputDir)

View File

@@ -26,7 +26,7 @@ func main() {
doc.AddParagraph("• 打开现有文档") doc.AddParagraph("• 打开现有文档")
// 保存文档 // 保存文档
outputFile := "output/example_document.docx" outputFile := "examples/output/example_document.docx"
err := doc.Save(outputFile) err := doc.Save(outputFile)
if err != nil { if err != nil {
log.Fatalf("保存文档失败: %v", err) log.Fatalf("保存文档失败: %v", err)
@@ -41,11 +41,11 @@ func main() {
log.Fatalf("打开文档失败: %v", err) log.Fatalf("打开文档失败: %v", err)
} }
fmt.Printf("文档包含 %d 个段落\n", len(openedDoc.Body.Paragraphs)) fmt.Printf("文档包含 %d 个段落\n", len(openedDoc.Body.GetParagraphs()))
// 打印所有段落内容 // 打印所有段落内容
fmt.Println("\n文档内容") fmt.Println("\n文档内容")
for i, para := range openedDoc.Body.Paragraphs { for i, para := range openedDoc.Body.GetParagraphs() {
if len(para.Runs) > 0 { if len(para.Runs) > 0 {
fmt.Printf("段落 %d: %s\n", i+1, para.Runs[0].Text.Content) fmt.Printf("段落 %d: %s\n", i+1, para.Runs[0].Text.Content)
} }

View File

@@ -66,7 +66,7 @@ func main() {
p5.SetAlignment(document.AlignRight) p5.SetAlignment(document.AlignRight)
// 保存文档 // 保存文档
err := doc.Save("../output/formatted_document.docx") err := doc.Save("examples/output/formatted_document.docx")
if err != nil { if err != nil {
log.Fatalf("保存文档失败: %v", err) log.Fatalf("保存文档失败: %v", err)
} }

View File

@@ -36,7 +36,7 @@ func main() {
demonstrateStyleManagement(quickAPI) demonstrateStyleManagement(quickAPI)
// 保存文档 // 保存文档
outputFile := "../output/styled_document_demo.docx" outputFile := "examples/output/styled_document_demo.docx"
err := doc.Save(outputFile) err := doc.Save(outputFile)
if err != nil { if err != nil {
log.Fatalf("保存文档失败: %v", err) log.Fatalf("保存文档失败: %v", err)

View File

@@ -206,7 +206,7 @@ func demonstrateTableCopyAndClear(doc *document.Document) {
copiedTable.SetCellText(1, 1, "复制4") copiedTable.SetCellText(1, 1, "复制4")
// 将复制的表格添加到文档 // 将复制的表格添加到文档
doc.Body.Tables = append(doc.Body.Tables, *copiedTable) doc.Body.AddElement(copiedTable)
fmt.Println(" 复制的表格已添加到文档") fmt.Println(" 复制的表格已添加到文档")
} }

View File

@@ -0,0 +1,283 @@
package main
import (
"fmt"
"log"
"path/filepath"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
fmt.Println("WordZero 表格布局和尺寸功能演示")
fmt.Println("==============================")
// 创建新文档
doc := document.New()
// 添加文档标题
title := doc.AddParagraph("表格布局和尺寸功能演示")
title.SetAlignment(document.AlignCenter)
titleFormat := &document.TextFormat{
Bold: true,
FontSize: 18,
FontColor: "2F5496",
FontName: "微软雅黑",
}
title.AddFormattedText("", titleFormat)
// 1. 行高设置演示
fmt.Println("1. 创建行高设置演示表格...")
doc.AddParagraph("1. 行高设置演示").SetStyle("Heading1")
heightTable := doc.AddTable(&document.TableConfig{
Rows: 4,
Cols: 3,
Width: 8000,
Data: [][]string{
{"行类型", "高度设置", "说明"},
{"自动行高", "默认", "根据内容自动调整高度"},
{"固定行高", "40磅", "精确的固定高度"},
{"最小行高", "30磅", "至少30磅内容多时可以更高"},
},
})
// 设置表头格式
headerFormat := &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontSize: 12,
FontColor: "FFFFFF",
FontName: "微软雅黑",
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
}
for col := 0; col < 3; col++ {
heightTable.SetCellFormat(0, col, headerFormat)
}
// 应用不同的行高设置
// 第2行固定高度40磅
heightTable.SetRowHeight(2, &document.RowHeightConfig{
Height: 40,
Rule: document.RowHeightExact,
})
// 第3行最小高度30磅
heightTable.SetRowHeight(3, &document.RowHeightConfig{
Height: 30,
Rule: document.RowHeightMinimum,
})
fmt.Println(" - 设置了不同的行高规则")
// 2. 表格对齐演示
fmt.Println("2. 创建表格对齐演示...")
doc.AddParagraph("2. 表格对齐演示").SetStyle("Heading1")
// 左对齐表格
doc.AddParagraph("2.1 左对齐表格").SetStyle("Heading2")
leftTable := doc.AddTable(&document.TableConfig{
Rows: 2,
Cols: 2,
Width: 4000,
Data: [][]string{
{"左对齐", "表格"},
{"Left", "Aligned"},
},
})
leftTable.SetTableAlignment(document.TableAlignLeft)
// 居中对齐表格
doc.AddParagraph("2.2 居中对齐表格").SetStyle("Heading2")
centerTable := doc.AddTable(&document.TableConfig{
Rows: 2,
Cols: 2,
Width: 4000,
Data: [][]string{
{"居中对齐", "表格"},
{"Center", "Aligned"},
},
})
centerTable.SetTableAlignment(document.TableAlignCenter)
// 右对齐表格
doc.AddParagraph("2.3 右对齐表格").SetStyle("Heading2")
rightTable := doc.AddTable(&document.TableConfig{
Rows: 2,
Cols: 2,
Width: 4000,
Data: [][]string{
{"右对齐", "表格"},
{"Right", "Aligned"},
},
})
rightTable.SetTableAlignment(document.TableAlignRight)
fmt.Println(" - 创建了左对齐、居中、右对齐三种表格")
// 3. 分页控制演示
fmt.Println("3. 创建分页控制演示表格...")
doc.AddParagraph("3. 分页控制演示").SetStyle("Heading1")
pageBreakTable := doc.AddTable(&document.TableConfig{
Rows: 6,
Cols: 4,
Width: 9000,
Data: [][]string{
{"序号", "姓名", "部门", "职位"},
{"001", "张三", "技术部", "工程师"},
{"002", "李四", "产品部", "产品经理"},
{"003", "王五", "设计部", "UI设计师"},
{"004", "赵六", "市场部", "市场专员"},
{"005", "钱七", "人事部", "HR专员"},
},
})
// 设置表头格式
for col := 0; col < 4; col++ {
pageBreakTable.SetCellFormat(0, col, &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontSize: 12,
FontColor: "FFFFFF",
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
})
}
// 设置第一行为重复的标题行
pageBreakTable.SetRowAsHeader(0, true)
fmt.Println(" - 设置第一行为重复标题行")
// 设置某些行禁止跨页分割
pageBreakTable.SetRowKeepTogether(1, true)
pageBreakTable.SetRowKeepTogether(2, true)
fmt.Println(" - 设置前两个数据行禁止跨页分割")
// 设置表格分页配置
pageBreakTable.SetTablePageBreak(&document.TablePageBreakConfig{
KeepWithNext: false,
KeepLines: true,
PageBreakBefore: false,
WidowControl: true,
})
// 4. 复杂布局演示
fmt.Println("4. 创建复杂布局演示表格...")
doc.AddParagraph("4. 复杂布局演示").SetStyle("Heading1")
complexTable := doc.AddTable(&document.TableConfig{
Rows: 5,
Cols: 4,
Width: 9000,
ColWidths: []int{1500, 3000, 2000, 2500},
Data: [][]string{
{"项目", "描述", "状态", "负责人"},
{"WordZero核心", "Word文档操作库核心功能", "进行中", "开发团队"},
{"表格功能", "完整的表格操作和格式化", "已完成", "张工程师"},
{"样式系统", "18种预定义样式和自定义样式", "已完成", "李设计师"},
{"测试套件", "完整的单元测试和集成测试", "进行中", "QA团队"},
},
})
// 设置表头为重复标题行
complexTable.SetHeaderRows(0, 0)
// 设置不同的行高
complexTable.SetRowHeight(0, &document.RowHeightConfig{
Height: 35,
Rule: document.RowHeightExact,
}) // 表头固定高度
// 批量设置数据行为最小高度
complexTable.SetRowHeightRange(1, 4, &document.RowHeightConfig{
Height: 25,
Rule: document.RowHeightMinimum,
})
// 设置表头格式
for col := 0; col < 4; col++ {
complexTable.SetCellFormat(0, col, &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontSize: 14,
FontColor: "FFFFFF",
FontName: "微软雅黑",
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
})
}
// 设置状态列的特殊格式
for row := 1; row <= 4; row++ {
status, _ := complexTable.GetCellText(row, 2)
var color string
switch status {
case "已完成":
color = "00AA00" // 绿色
case "进行中":
color = "FF8800" // 橙色
default:
color = "666666" // 灰色
}
complexTable.SetCellFormat(row, 2, &document.CellFormat{
TextFormat: &document.TextFormat{
Bold: true,
FontColor: color,
FontSize: 11,
},
HorizontalAlign: document.CellAlignCenter,
VerticalAlign: document.CellVAlignCenter,
})
}
// 设置表格居中对齐
complexTable.SetTableAlignment(document.TableAlignCenter)
fmt.Println(" - 设置了自定义列宽")
fmt.Println(" - 应用了不同的行高规则")
fmt.Println(" - 添加了状态标识颜色")
// 5. 输出统计信息
fmt.Println("5. 生成统计信息...")
doc.AddParagraph("5. 表格统计信息").SetStyle("Heading1")
// 获取分页信息
breakInfo := pageBreakTable.GetTableBreakInfo()
infoText := fmt.Sprintf("分页控制表格统计:总行数 %d标题行数 %d禁止分割行数 %d",
breakInfo["total_rows"], breakInfo["header_rows"], breakInfo["keep_together_rows"])
doc.AddParagraph(infoText)
// 获取表格布局信息
layout := complexTable.GetTableLayout()
layoutText := fmt.Sprintf("复杂表格布局:对齐方式 %s环绕类型 %s定位类型 %s",
layout.Alignment, layout.TextWrap, layout.Position)
doc.AddParagraph(layoutText)
// 验证行高设置
heightConfig, _ := complexTable.GetRowHeight(0)
heightText := fmt.Sprintf("表头行高设置:%d磅规则 %s", heightConfig.Height, heightConfig.Rule)
doc.AddParagraph(heightText)
// 6. 保存文档
outputPath := filepath.Join("examples", "output", "table_layout_demo.docx")
err := doc.Save(outputPath)
if err != nil {
log.Fatalf("保存文档失败: %v", err)
}
fmt.Printf("6. 文档已保存到: %s\n", outputPath)
fmt.Println("\n演示完成")
fmt.Println("\n新实现的功能包括")
fmt.Println("✅ 行高设置(固定高度、最小高度、自动调整)")
fmt.Println("✅ 表格对齐方式(左对齐、居中、右对齐)")
fmt.Println("✅ 分页控制(标题行重复、禁止跨页分割)")
fmt.Println("✅ 表格布局配置和查询")
fmt.Println("✅ 批量行高设置和统计信息获取")
}

View File

@@ -0,0 +1,677 @@
package main
import (
"fmt"
"log"
"os"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
// 创建输出目录
if err := os.MkdirAll("examples/output", 0755); err != nil {
log.Fatalf("创建输出目录失败: %v", err)
}
// 创建文档
doc := document.New()
if doc == nil {
log.Fatal("创建文档失败")
}
// 添加标题
doc.AddParagraph("WordZero - 表格样式和外观功能演示")
doc.AddParagraph("")
// 演示1基础表格边框
demonstrateBorders(doc)
// 演示2单元格边框
demonstrateCellBorders(doc)
// 演示3表格背景
demonstrateTableShading(doc)
// 演示4单元格背景
demonstrateCellShading(doc)
// 演示5奇偶行颜色交替
demonstrateAlternatingRows(doc)
// 演示6预定义样式模板
demonstrateStyleTemplates(doc)
// 演示7自定义表格样式
demonstrateCustomStyle(doc)
// 演示8复杂样式组合
demonstrateComplexStyle(doc)
// 演示9无边框表格
demonstrateNoBorders(doc)
// 演示10各种边框样式
demonstrateBorderStyles(doc)
// 保存文档
outputFile := "examples/output/table_style_demo.docx"
if err := doc.Save(outputFile); err != nil {
log.Fatalf("保存文档失败: %v", err)
}
fmt.Printf("表格样式演示文档已保存到: %s\n", outputFile)
}
// demonstrateBorders 演示表格边框设置
func demonstrateBorders(doc *document.Document) {
doc.AddParagraph("1. 表格边框设置演示")
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"姓名", "年龄", "职业"},
{"张三", "25", "工程师"},
{"李四", "30", "设计师"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 设置不同的边框样式
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "FF0000", // 红色粗上边框
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "0000FF", // 蓝色左边框
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 6,
Color: "00FF00", // 绿色双线下边框
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleDashed,
Width: 6,
Color: "FF00FF", // 紫色虚线右边框
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleDotted,
Width: 4,
Color: "808080", // 灰色点线内部水平边框
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "808080", // 灰色内部垂直边框
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
log.Fatalf("设置表格边框失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateCellBorders 演示单元格边框设置
func demonstrateCellBorders(doc *document.Document) {
doc.AddParagraph("2. 单元格边框设置演示")
config := &document.TableConfig{
Rows: 2,
Cols: 3,
Width: 4500,
Data: [][]string{
{"单元格A", "单元格B", "单元格C"},
{"数据1", "数据2", "数据3"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 为第一个单元格设置特殊边框
cellBorderConfig := &document.CellBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 8,
Color: "FF0000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "0000FF",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 6,
Color: "00FF00",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleDashed,
Width: 4,
Color: "FF00FF",
Space: 0,
},
DiagDown: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 2,
Color: "FFFF00", // 黄色对角线
Space: 0,
},
}
err := table.SetCellBorders(0, 0, cellBorderConfig)
if err != nil {
log.Fatalf("设置单元格边框失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateTableShading 演示表格背景设置
func demonstrateTableShading(doc *document.Document) {
doc.AddParagraph("3. 表格背景设置演示")
config := &document.TableConfig{
Rows: 3,
Cols: 2,
Width: 4000,
Data: [][]string{
{"产品", "价格"},
{"产品A", "¥100"},
{"产品B", "¥200"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 设置表格整体背景
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternPct25,
ForegroundColor: "000000",
BackgroundColor: "E0E0E0",
}
err := table.SetTableShading(shadingConfig)
if err != nil {
log.Fatalf("设置表格背景失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateCellShading 演示单元格背景设置
func demonstrateCellShading(doc *document.Document) {
doc.AddParagraph("4. 单元格背景设置演示")
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 4500,
Data: [][]string{
{"红色", "绿色", "蓝色"},
{"黄色", "紫色", "青色"},
{"橙色", "粉色", "灰色"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 为不同单元格设置不同颜色
colors := [][]string{
{"FF0000", "00FF00", "0000FF"}, // 红绿蓝
{"FFFF00", "FF00FF", "00FFFF"}, // 黄紫青
{"FFA500", "FFC0CB", "808080"}, // 橙粉灰
}
patterns := [][]document.ShadingPattern{
{document.ShadingPatternSolid, document.ShadingPatternPct50, document.ShadingPatternPct25},
{document.ShadingPatternSolid, document.ShadingPatternPct75, document.ShadingPatternPct10},
{document.ShadingPatternPct60, document.ShadingPatternPct40, document.ShadingPatternSolid},
}
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: patterns[i][j],
BackgroundColor: colors[i][j],
}
err := table.SetCellShading(i, j, shadingConfig)
if err != nil {
log.Fatalf("设置单元格(%d,%d)背景失败: %v", i, j, err)
}
}
}
doc.AddParagraph("")
}
// demonstrateAlternatingRows 演示奇偶行颜色交替
func demonstrateAlternatingRows(doc *document.Document) {
doc.AddParagraph("5. 奇偶行颜色交替演示")
config := &document.TableConfig{
Rows: 6,
Cols: 3,
Width: 4500,
Data: [][]string{
{"序号", "姓名", "分数"},
{"1", "张三", "85"},
{"2", "李四", "92"},
{"3", "王五", "78"},
{"4", "赵六", "95"},
{"5", "钱七", "88"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 设置奇偶行颜色交替
err := table.SetAlternatingRowColors("F0F0F0", "FFFFFF")
if err != nil {
log.Fatalf("设置奇偶行颜色交替失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateStyleTemplates 演示预定义样式模板
func demonstrateStyleTemplates(doc *document.Document) {
doc.AddParagraph("6. 预定义样式模板演示")
// 演示Grid样式
config1 := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 4500,
Data: [][]string{
{"项目", "预算", "实际"},
{"开发", "10000", "9500"},
{"测试", "5000", "5200"},
{"总计", "15000", "14700"},
},
}
table1 := doc.AddTable(config1)
if table1 == nil {
log.Fatal("创建表格失败")
}
styleConfig1 := &document.TableStyleConfig{
Template: document.TableStyleTemplateGrid,
FirstRowHeader: true,
LastRowTotal: true,
BandedRows: true,
BandedColumns: false,
}
err := table1.ApplyTableStyle(styleConfig1)
if err != nil {
log.Fatalf("应用Grid样式失败: %v", err)
}
doc.AddParagraph("")
// 演示Colorful样式
config2 := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 4500,
Data: [][]string{
{"部门", "人数", "预算"},
{"技术部", "20", "500万"},
{"销售部", "15", "300万"},
{"市场部", "10", "200万"},
},
}
table2 := doc.AddTable(config2)
if table2 == nil {
log.Fatal("创建表格失败")
}
styleConfig2 := &document.TableStyleConfig{
Template: document.TableStyleTemplateColorful1,
FirstRowHeader: true,
FirstColumnHeader: true,
BandedRows: true,
}
err = table2.ApplyTableStyle(styleConfig2)
if err != nil {
log.Fatalf("应用Colorful样式失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateCustomStyle 演示自定义表格样式
func demonstrateCustomStyle(doc *document.Document) {
doc.AddParagraph("7. 自定义表格样式演示")
config := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 4500,
Data: [][]string{
{"功能", "状态", "备注"},
{"登录", "完成", "已测试"},
{"注册", "开发中", "50%"},
{"支付", "计划中", "下个版本"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 创建自定义边框配置
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "2E75B6",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "2E75B6",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
}
// 创建自定义背景配置
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternPct10,
BackgroundColor: "E7F3FF",
}
// 应用自定义样式
err := table.CreateCustomTableStyle("CustomBlue", "蓝色主题", borderConfig, shadingConfig, true)
if err != nil {
log.Fatalf("创建自定义表格样式失败: %v", err)
}
doc.AddParagraph("")
}
// demonstrateComplexStyle 演示复杂样式组合
func demonstrateComplexStyle(doc *document.Document) {
doc.AddParagraph("8. 复杂样式组合演示")
config := &document.TableConfig{
Rows: 5,
Cols: 4,
Width: 6000,
Data: [][]string{
{"部门", "Q1", "Q2", "Q3"},
{"销售部", "120万", "135万", "150万"},
{"技术部", "80万", "90万", "95万"},
{"市场部", "60万", "70万", "85万"},
{"总计", "260万", "295万", "330万"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 应用基础样式
styleConfig := &document.TableStyleConfig{
Template: document.TableStyleTemplateColorful2,
FirstRowHeader: true,
LastRowTotal: true,
FirstColumnHeader: true,
BandedRows: false,
}
err := table.ApplyTableStyle(styleConfig)
if err != nil {
log.Fatalf("应用基础样式失败: %v", err)
}
// 设置特殊边框
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "B0B0B0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "B0B0B0",
Space: 0,
},
}
err = table.SetTableBorders(borderConfig)
if err != nil {
log.Fatalf("设置边框失败: %v", err)
}
// 为标题行设置特殊背景
for j := 0; j < 4; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "2E75B6",
ForegroundColor: "FFFFFF",
}
err = table.SetCellShading(0, j, shadingConfig)
if err != nil {
log.Fatalf("设置标题行背景失败: %v", err)
}
}
// 为总计行设置特殊背景
for j := 0; j < 4; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "FFFF99",
}
err = table.SetCellShading(4, j, shadingConfig)
if err != nil {
log.Fatalf("设置总计行背景失败: %v", err)
}
}
doc.AddParagraph("")
}
// demonstrateNoBorders 演示无边框表格
func demonstrateNoBorders(doc *document.Document) {
doc.AddParagraph("9. 无边框表格演示")
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 4500,
Data: [][]string{
{"项目A", "项目B", "项目C"},
{"说明1", "说明2", "说明3"},
{"结果1", "结果2", "结果3"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
// 移除所有边框
err := table.RemoveTableBorders()
if err != nil {
log.Fatalf("移除表格边框失败: %v", err)
}
// 设置轻微的背景色以区分单元格
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
var bgColor string
if (i+j)%2 == 0 {
bgColor = "F8F8F8"
} else {
bgColor = "FFFFFF"
}
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: bgColor,
}
err = table.SetCellShading(i, j, shadingConfig)
if err != nil {
log.Fatalf("设置单元格背景失败: %v", err)
}
}
}
doc.AddParagraph("")
}
// demonstrateBorderStyles 演示各种边框样式
func demonstrateBorderStyles(doc *document.Document) {
doc.AddParagraph("10. 各种边框样式演示")
borderStyles := []struct {
style document.BorderStyle
name string
}{
{document.BorderStyleSingle, "单线"},
{document.BorderStyleThick, "粗线"},
{document.BorderStyleDouble, "双线"},
{document.BorderStyleDotted, "点线"},
{document.BorderStyleDashed, "虚线"},
{document.BorderStyleDotDash, "点划线"},
{document.BorderStyleWave, "波浪线"},
}
for i, styleInfo := range borderStyles {
config := &document.TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
Data: [][]string{
{fmt.Sprintf("样式%d", i+1), styleInfo.name},
{"演示", "数据"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatalf("创建表格%d失败", i+1)
}
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: styleInfo.style,
Width: 6,
Color: "000000",
Space: 0,
},
Left: &document.BorderConfig{
Style: styleInfo.style,
Width: 6,
Color: "000000",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: styleInfo.style,
Width: 6,
Color: "000000",
Space: 0,
},
Right: &document.BorderConfig{
Style: styleInfo.style,
Width: 6,
Color: "000000",
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
log.Fatalf("设置表格%d边框失败: %v", i+1, err)
}
if i < len(borderStyles)-1 {
doc.AddParagraph("")
}
}
}

3
go.mod
View File

@@ -1,6 +1,3 @@
module github.com/ZeroHawkeye/wordZero module github.com/ZeroHawkeye/wordZero
go 1.21 go 1.21
require (
)

View File

@@ -31,9 +31,26 @@ type Document struct {
// Body 表示文档主体 // Body 表示文档主体
type Body struct { type Body struct {
XMLName xml.Name `xml:"w:body"` XMLName xml.Name `xml:"w:body"`
Paragraphs []Paragraph `xml:"w:p"` Elements []interface{} `xml:"-"` // 不序列化此字段,使用自定义方法
Tables []Table `xml:"w:tbl"` }
// MarshalXML 自定义XML序列化按照元素顺序输出
func (b *Body) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
// 开始元素
if err := e.EncodeToken(start); err != nil {
return err
}
// 序列化每个元素,保持顺序
for _, element := range b.Elements {
if err := e.Encode(element); err != nil {
return err
}
}
// 结束元素
return e.EncodeToken(start.End())
} }
// BodyElement 文档主体元素接口 // BodyElement 文档主体元素接口
@@ -218,7 +235,7 @@ func New() *Document {
doc := &Document{ doc := &Document{
Body: &Body{ Body: &Body{
Paragraphs: make([]Paragraph, 0), Elements: make([]interface{}, 0),
}, },
styleManager: style.NewStyleManager(), styleManager: style.NewStyleManager(),
parts: make(map[string][]byte), parts: make(map[string][]byte),
@@ -283,6 +300,9 @@ func Open(filename string) (*Document, error) {
Debugf("已读取文件部件: %s (%d 字节)", file.Name, len(data)) Debugf("已读取文件部件: %s (%d 字节)", file.Name, len(data))
} }
// 初始化样式管理器
doc.styleManager = style.NewStyleManager()
// 解析主文档 // 解析主文档
if err := doc.parseDocument(); err != nil { if err := doc.parseDocument(); err != nil {
Errorf("解析文档失败: %s", filename) Errorf("解析文档失败: %s", filename)
@@ -400,7 +420,7 @@ func (d *Document) Save(filename string) error {
// }) // })
func (d *Document) AddParagraph(text string) *Paragraph { func (d *Document) AddParagraph(text string) *Paragraph {
Debugf("添加段落: %s", text) Debugf("添加段落: %s", text)
p := Paragraph{ p := &Paragraph{
Runs: []Run{ Runs: []Run{
{ {
Text: Text{ Text: Text{
@@ -411,8 +431,8 @@ func (d *Document) AddParagraph(text string) *Paragraph {
}, },
} }
d.Body.Paragraphs = append(d.Body.Paragraphs, p) d.Body.Elements = append(d.Body.Elements, p)
return &d.Body.Paragraphs[len(d.Body.Paragraphs)-1] return p
} }
// AddFormattedParagraph 向文档添加一个格式化段落。 // AddFormattedParagraph 向文档添加一个格式化段落。
@@ -468,7 +488,7 @@ func (d *Document) AddFormattedParagraph(text string, format *TextFormat) *Parag
} }
} }
p := Paragraph{ p := &Paragraph{
Runs: []Run{ Runs: []Run{
{ {
Properties: runProps, Properties: runProps,
@@ -480,8 +500,8 @@ func (d *Document) AddFormattedParagraph(text string, format *TextFormat) *Parag
}, },
} }
d.Body.Paragraphs = append(d.Body.Paragraphs, p) d.Body.Elements = append(d.Body.Elements, p)
return &d.Body.Paragraphs[len(d.Body.Paragraphs)-1] return p
} }
// SetAlignment 设置段落的对齐方式。 // SetAlignment 设置段落的对齐方式。
@@ -725,7 +745,7 @@ func (d *Document) AddHeadingParagraph(text string, level int) *Paragraph {
} }
// 创建段落 // 创建段落
p := Paragraph{ p := &Paragraph{
Properties: paraProps, Properties: paraProps,
Runs: []Run{ Runs: []Run{
{ {
@@ -738,8 +758,8 @@ func (d *Document) AddHeadingParagraph(text string, level int) *Paragraph {
}, },
} }
d.Body.Paragraphs = append(d.Body.Paragraphs, p) d.Body.Elements = append(d.Body.Elements, p)
return &d.Body.Paragraphs[len(d.Body.Paragraphs)-1] return p
} }
// SetStyle 设置段落的样式。 // SetStyle 设置段落的样式。
@@ -824,7 +844,11 @@ func (d *Document) parseDocument() error {
Paragraphs []struct { Paragraphs []struct {
XMLName xml.Name `xml:"p"` XMLName xml.Name `xml:"p"`
Properties *struct { Properties *struct {
XMLName xml.Name `xml:"pPr"` XMLName xml.Name `xml:"pPr"`
ParagraphStyle *struct {
XMLName xml.Name `xml:"pStyle"`
Val string `xml:"val,attr"`
} `xml:"pStyle,omitempty"`
Spacing *struct { Spacing *struct {
XMLName xml.Name `xml:"spacing"` XMLName xml.Name `xml:"spacing"`
Before string `xml:"before,attr,omitempty"` Before string `xml:"before,attr,omitempty"`
@@ -882,18 +906,24 @@ func (d *Document) parseDocument() error {
// 转换为内部结构 // 转换为内部结构
d.Body = &Body{ d.Body = &Body{
Paragraphs: make([]Paragraph, len(doc.Body.Paragraphs)), Elements: make([]interface{}, len(doc.Body.Paragraphs)),
} }
for i, p := range doc.Body.Paragraphs { for i, p := range doc.Body.Paragraphs {
paragraph := Paragraph{ paragraph := &Paragraph{
Runs: make([]Run, len(p.Runs)), Runs: make([]Run, len(p.Runs)),
} }
// 复制段落属性 // 转换段落属性
if p.Properties != nil { if p.Properties != nil {
paragraph.Properties = &ParagraphProperties{} paragraph.Properties = &ParagraphProperties{}
if p.Properties.ParagraphStyle != nil {
paragraph.Properties.ParagraphStyle = &ParagraphStyle{
Val: p.Properties.ParagraphStyle.Val,
}
}
if p.Properties.Spacing != nil { if p.Properties.Spacing != nil {
paragraph.Properties.Spacing = &Spacing{ paragraph.Properties.Spacing = &Spacing{
Before: p.Properties.Spacing.Before, Before: p.Properties.Spacing.Before,
@@ -917,16 +947,13 @@ func (d *Document) parseDocument() error {
} }
} }
// 复制运行和运行属性
for j, r := range p.Runs { for j, r := range p.Runs {
run := Run{ run := Run{
Text: Text{ Text: Text{
Space: r.Text.Space,
Content: r.Text.Content, Content: r.Text.Content,
}, },
} }
// 复制运行属性
if r.Properties != nil { if r.Properties != nil {
run.Properties = &RunProperties{} run.Properties = &RunProperties{}
@@ -960,10 +987,10 @@ func (d *Document) parseDocument() error {
paragraph.Runs[j] = run paragraph.Runs[j] = run
} }
d.Body.Paragraphs[i] = paragraph d.Body.Elements[i] = paragraph
} }
Infof("解析完成,共 %d 个段落", len(d.Body.Paragraphs)) Infof("解析完成,共 %d 个元素", len(d.Body.Elements))
return nil return nil
} }
@@ -975,12 +1002,12 @@ func (d *Document) serializeDocument() error {
type documentXML struct { type documentXML struct {
XMLName xml.Name `xml:"w:document"` XMLName xml.Name `xml:"w:document"`
Xmlns string `xml:"xmlns:w,attr"` Xmlns string `xml:"xmlns:w,attr"`
Body Body `xml:"w:body"` Body *Body `xml:"w:body"`
} }
doc := documentXML{ doc := documentXML{
Xmlns: "http://schemas.openxmlformats.org/wordprocessingml/2006/main", Xmlns: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
Body: *d.Body, Body: d.Body,
} }
// 序列化为XML // 序列化为XML
@@ -1118,3 +1145,30 @@ func (d *Document) ToBytes() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// GetParagraphs 获取所有段落
func (b *Body) GetParagraphs() []*Paragraph {
paragraphs := make([]*Paragraph, 0)
for _, element := range b.Elements {
if p, ok := element.(*Paragraph); ok {
paragraphs = append(paragraphs, p)
}
}
return paragraphs
}
// GetTables 获取所有表格
func (b *Body) GetTables() []*Table {
tables := make([]*Table, 0)
for _, element := range b.Elements {
if t, ok := element.(*Table); ok {
tables = append(tables, t)
}
}
return tables
}
// AddElement 添加元素到文档主体
func (b *Body) AddElement(element interface{}) {
b.Elements = append(b.Elements, element)
}

View File

@@ -25,8 +25,8 @@ func TestNewDocument(t *testing.T) {
} }
// 验证初始状态 // 验证初始状态
if len(doc.Body.Paragraphs) != 0 { if len(doc.Body.GetParagraphs()) != 0 {
t.Errorf("Expected 0 paragraphs, got %d", len(doc.Body.Paragraphs)) t.Errorf("Expected 0 paragraphs, got %d", len(doc.Body.GetParagraphs()))
} }
// 验证样式管理器初始化 // 验证样式管理器初始化
@@ -44,8 +44,8 @@ func TestAddParagraph(t *testing.T) {
para := doc.AddParagraph(text) para := doc.AddParagraph(text)
// 验证段落添加 // 验证段落添加
if len(doc.Body.Paragraphs) != 1 { if len(doc.Body.GetParagraphs()) != 1 {
t.Errorf("Expected 1 paragraph, got %d", len(doc.Body.Paragraphs)) t.Errorf("Expected 1 paragraph, got %d", len(doc.Body.GetParagraphs()))
} }
// 验证段落内容 // 验证段落内容
@@ -58,7 +58,8 @@ func TestAddParagraph(t *testing.T) {
} }
// 验证返回的指针是否正确 // 验证返回的指针是否正确
if &doc.Body.Paragraphs[0] != para { paragraphs := doc.Body.GetParagraphs()
if paragraphs[0] != para {
t.Error("Returned paragraph pointer is incorrect") t.Error("Returned paragraph pointer is incorrect")
} }
} }
@@ -135,7 +136,7 @@ func TestAddFormattedParagraph(t *testing.T) {
para := doc.AddFormattedParagraph(text, format) para := doc.AddFormattedParagraph(text, format)
// 验证段落添加 // 验证段落添加
if len(doc.Body.Paragraphs) != 1 { if len(doc.Body.GetParagraphs()) != 1 {
t.Error("Failed to add formatted paragraph") t.Error("Failed to add formatted paragraph")
} }
@@ -379,8 +380,8 @@ func TestComplexDocument(t *testing.T) {
mixed.AddFormattedText("文本。", nil) mixed.AddFormattedText("文本。", nil)
// 验证文档结构 // 验证文档结构
if len(doc.Body.Paragraphs) != 6 { if len(doc.Body.GetParagraphs()) != 6 {
t.Errorf("Expected 6 paragraphs, got %d", len(doc.Body.Paragraphs)) t.Errorf("Expected 6 paragraphs, got %d", len(doc.Body.GetParagraphs()))
} }
// 保存并验证 // 保存并验证
@@ -416,13 +417,13 @@ func TestDocumentOpen(t *testing.T) {
} }
// 验证文档内容 // 验证文档内容
if len(loadedDoc.Body.Paragraphs) != 3 { if len(loadedDoc.Body.GetParagraphs()) != 3 {
t.Errorf("Expected 3 paragraphs, got %d", len(loadedDoc.Body.Paragraphs)) t.Errorf("Expected 3 paragraphs, got %d", len(loadedDoc.Body.GetParagraphs()))
} }
// 验证第一段内容 // 验证第一段内容
if len(loadedDoc.Body.Paragraphs[0].Runs) > 0 { if len(loadedDoc.Body.GetParagraphs()[0].Runs) > 0 {
content := loadedDoc.Body.Paragraphs[0].Runs[0].Text.Content content := loadedDoc.Body.GetParagraphs()[0].Runs[0].Text.Content
if content != "第一段" { if content != "第一段" {
t.Errorf("Expected '第一段', got '%s'", content) t.Errorf("Expected '第一段', got '%s'", content)
} }
@@ -565,8 +566,8 @@ func TestMemoryUsage(t *testing.T) {
doc.AddParagraph("内存测试段落") doc.AddParagraph("内存测试段落")
} }
if len(doc.Body.Paragraphs) != numParagraphs { if len(doc.Body.GetParagraphs()) != numParagraphs {
t.Errorf("Expected %d paragraphs, got %d", numParagraphs, len(doc.Body.Paragraphs)) t.Errorf("Expected %d paragraphs, got %d", numParagraphs, len(doc.Body.GetParagraphs()))
} }
// 测试保存大文档 // 测试保存大文档

File diff suppressed because it is too large Load Diff

View File

@@ -100,15 +100,15 @@ func TestAddTable(t *testing.T) {
Width: 6000, Width: 6000,
} }
initialTableCount := len(doc.Body.Tables) initialTableCount := len(doc.Body.GetTables())
table := doc.AddTable(config) table := doc.AddTable(config)
if table == nil { if table == nil {
t.Fatal("添加表格失败") t.Fatal("添加表格失败")
} }
if len(doc.Body.Tables) != initialTableCount+1 { if len(doc.Body.GetTables()) != initialTableCount+1 {
t.Errorf("期望表格数量%d实际%d", initialTableCount+1, len(doc.Body.Tables)) t.Errorf("期望表格数量%d实际%d", initialTableCount+1, len(doc.Body.GetTables()))
} }
} }
@@ -1221,3 +1221,292 @@ func TestTextDirectionConstants(t *testing.T) {
} }
} }
} }
// TestRowHeight 测试行高设置功能
func TestRowHeight(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 3,
Cols: 2,
Width: 4000,
}
table := doc.CreateTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置固定行高
heightConfig := &RowHeightConfig{
Height: 30,
Rule: RowHeightExact,
}
err := table.SetRowHeight(0, heightConfig)
if err != nil {
t.Errorf("设置行高失败: %v", err)
}
// 测试获取行高
retrievedConfig, err := table.GetRowHeight(0)
if err != nil {
t.Errorf("获取行高失败: %v", err)
}
if retrievedConfig.Height != 30 {
t.Errorf("期望行高30实际%d", retrievedConfig.Height)
}
if retrievedConfig.Rule != RowHeightExact {
t.Errorf("期望行高规则%s实际%s", RowHeightExact, retrievedConfig.Rule)
}
// 测试批量设置行高
batchConfig := &RowHeightConfig{
Height: 25,
Rule: RowHeightMinimum,
}
err = table.SetRowHeightRange(1, 2, batchConfig)
if err != nil {
t.Errorf("批量设置行高失败: %v", err)
}
// 验证批量设置结果
for i := 1; i <= 2; i++ {
config, err := table.GetRowHeight(i)
if err != nil {
t.Errorf("获取第%d行高度失败: %v", i, err)
}
if config.Height != 25 {
t.Errorf("第%d行期望高度25实际%d", i, config.Height)
}
if config.Rule != RowHeightMinimum {
t.Errorf("第%d行期望规则%s实际%s", i, RowHeightMinimum, config.Rule)
}
}
// 测试无效索引
err = table.SetRowHeight(10, heightConfig)
if err == nil {
t.Error("期望设置无效行索引失败,但成功了")
}
_, err = table.GetRowHeight(10)
if err == nil {
t.Error("期望获取无效行索引失败,但成功了")
}
}
// TestTableLayout 测试表格布局和定位功能
func TestTableLayout(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 2,
Cols: 2,
Width: 4000,
}
table := doc.CreateTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置表格布局
layoutConfig := &TableLayoutConfig{
Alignment: TableAlignCenter,
TextWrap: TextWrapNone,
Position: PositionInline,
}
err := table.SetTableLayout(layoutConfig)
if err != nil {
t.Errorf("设置表格布局失败: %v", err)
}
// 测试获取表格布局
retrievedLayout := table.GetTableLayout()
if retrievedLayout.Alignment != TableAlignCenter {
t.Errorf("期望对齐方式%s实际%s", TableAlignCenter, retrievedLayout.Alignment)
}
// 测试快捷方法设置对齐
err = table.SetTableAlignment(TableAlignRight)
if err != nil {
t.Errorf("设置表格对齐失败: %v", err)
}
retrievedLayout = table.GetTableLayout()
if retrievedLayout.Alignment != TableAlignRight {
t.Errorf("期望对齐方式%s实际%s", TableAlignRight, retrievedLayout.Alignment)
}
}
// TestTablePageBreak 测试表格分页控制功能
func TestTablePageBreak(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 4,
Cols: 2,
Width: 4000,
}
table := doc.CreateTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置行禁止跨页分割
err := table.SetRowKeepTogether(0, true)
if err != nil {
t.Errorf("设置行禁止跨页分割失败: %v", err)
}
// 测试检查行是否禁止跨页分割
keepTogether, err := table.IsRowKeepTogether(0)
if err != nil {
t.Errorf("检查行跨页分割设置失败: %v", err)
}
if !keepTogether {
t.Error("期望行禁止跨页分割为true实际为false")
}
// 测试设置标题行
err = table.SetRowAsHeader(0, true)
if err != nil {
t.Errorf("设置标题行失败: %v", err)
}
// 测试检查是否为标题行
isHeader, err := table.IsRowHeader(0)
if err != nil {
t.Errorf("检查标题行设置失败: %v", err)
}
if !isHeader {
t.Error("期望第0行为标题行实际不是")
}
// 测试设置标题行范围
err = table.SetHeaderRows(0, 1)
if err != nil {
t.Errorf("设置标题行范围失败: %v", err)
}
// 验证标题行范围设置
for i := 0; i <= 1; i++ {
isHeader, err := table.IsRowHeader(i)
if err != nil {
t.Errorf("检查第%d行标题行设置失败: %v", i, err)
}
if !isHeader {
t.Errorf("期望第%d行为标题行实际不是", i)
}
}
// 测试表格分页信息
breakInfo := table.GetTableBreakInfo()
if breakInfo["total_rows"] != 4 {
t.Errorf("期望总行数4实际%v", breakInfo["total_rows"])
}
if breakInfo["header_rows"] != 2 {
t.Errorf("期望标题行数2实际%v", breakInfo["header_rows"])
}
// 测试表格分页配置
pageBreakConfig := &TablePageBreakConfig{
KeepWithNext: true,
KeepLines: true,
PageBreakBefore: false,
WidowControl: true,
}
err = table.SetTablePageBreak(pageBreakConfig)
if err != nil {
t.Errorf("设置表格分页配置失败: %v", err)
}
// 测试行与下一行保持在同一页
err = table.SetRowKeepWithNext(1, true)
if err != nil {
t.Errorf("设置行与下一行保持在同一页失败: %v", err)
}
// 测试无效索引
err = table.SetRowKeepTogether(10, true)
if err == nil {
t.Error("期望设置无效行索引失败,但成功了")
}
err = table.SetRowAsHeader(10, true)
if err == nil {
t.Error("期望设置无效行索引失败,但成功了")
}
_, err = table.IsRowHeader(10)
if err == nil {
t.Error("期望检查无效行索引失败,但成功了")
}
_, err = table.IsRowKeepTogether(10)
if err == nil {
t.Error("期望检查无效行索引失败,但成功了")
}
}
// TestRowHeightConstants 测试行高规则常量
func TestRowHeightConstants(t *testing.T) {
// 验证行高规则常量定义正确
if RowHeightAuto != "auto" {
t.Errorf("期望RowHeightAuto为'auto',实际'%s'", RowHeightAuto)
}
if RowHeightMinimum != "atLeast" {
t.Errorf("期望RowHeightMinimum为'atLeast',实际'%s'", RowHeightMinimum)
}
if RowHeightExact != "exact" {
t.Errorf("期望RowHeightExact为'exact',实际'%s'", RowHeightExact)
}
}
// TestTableAlignmentConstants 测试表格对齐常量
func TestTableAlignmentConstants(t *testing.T) {
// 验证表格对齐常量定义正确
if TableAlignLeft != "left" {
t.Errorf("期望TableAlignLeft为'left',实际'%s'", TableAlignLeft)
}
if TableAlignCenter != "center" {
t.Errorf("期望TableAlignCenter为'center',实际'%s'", TableAlignCenter)
}
if TableAlignRight != "right" {
t.Errorf("期望TableAlignRight为'right',实际'%s'", TableAlignRight)
}
}
// TestTableRowPropertiesExtensions 测试TableRowProperties扩展方法
func TestTableRowPropertiesExtensions(t *testing.T) {
trp := &TableRowProperties{}
// 测试SetCantSplit方法
trp.SetCantSplit(true)
if trp.CantSplit == nil || trp.CantSplit.Val != "1" {
t.Error("设置CantSplit失败")
}
trp.SetCantSplit(false)
if trp.CantSplit != nil {
t.Error("清除CantSplit失败")
}
// 测试SetTblHeader方法
trp.SetTblHeader(true)
if trp.TblHeader == nil || trp.TblHeader.Val != "1" {
t.Error("设置TblHeader失败")
}
trp.SetTblHeader(false)
if trp.TblHeader != nil {
t.Error("清除TblHeader失败")
}
}

View File

@@ -50,11 +50,12 @@ func TestOpenDocument(t *testing.T) {
} }
// 验证内容 // 验证内容
if len(openedDoc.Body.Paragraphs) != 1 { paragraphs := openedDoc.Body.GetParagraphs()
t.Fatalf("Expected 1 paragraph, got %d", len(openedDoc.Body.Paragraphs)) if len(paragraphs) != 1 {
t.Fatalf("Expected 1 paragraph, got %d", len(paragraphs))
} }
if openedDoc.Body.Paragraphs[0].Runs[0].Text.Content != "Test paragraph" { if paragraphs[0].Runs[0].Text.Content != "Test paragraph" {
t.Fatalf("Paragraph content mismatch") t.Fatalf("Paragraph content mismatch")
} }

779
test/table_style_test.go Normal file
View File

@@ -0,0 +1,779 @@
package test
import (
"fmt"
"testing"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
// TestTableBorders 测试表格边框功能
func TestTableBorders(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"姓名", "年龄", "职业"},
{"张三", "25", "工程师"},
{"李四", "30", "设计师"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置表格边框
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "FF0000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "0000FF",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 6,
Color: "00FF00",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleDashed,
Width: 6,
Color: "FF00FF",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleDotted,
Width: 4,
Color: "808080",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "808080",
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
t.Fatalf("设置表格边框失败: %v", err)
}
t.Log("表格边框设置成功")
}
// TestCellBorders 测试单元格边框功能
func TestCellBorders(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
Data: [][]string{
{"A1", "B1"},
{"A2", "B2"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置单元格边框
cellBorderConfig := &document.CellBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 8,
Color: "FF0000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "0000FF",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 6,
Color: "00FF00",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleDashed,
Width: 4,
Color: "FF00FF",
Space: 0,
},
DiagDown: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 2,
Color: "FFFF00",
Space: 0,
},
}
err := table.SetCellBorders(0, 0, cellBorderConfig)
if err != nil {
t.Fatalf("设置单元格边框失败: %v", err)
}
t.Log("单元格边框设置成功")
}
// TestTableShading 测试表格背景功能
func TestTableShading(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 3,
Cols: 2,
Width: 4000,
Data: [][]string{
{"产品", "价格"},
{"产品A", "100"},
{"产品B", "200"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置表格背景
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternPct25,
ForegroundColor: "000000",
BackgroundColor: "E0E0E0",
}
err := table.SetTableShading(shadingConfig)
if err != nil {
t.Fatalf("设置表格背景失败: %v", err)
}
t.Log("表格背景设置成功")
}
// TestCellShading 测试单元格背景功能
func TestCellShading(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 4500,
Data: [][]string{
{"A", "B", "C"},
{"1", "2", "3"},
{"X", "Y", "Z"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置单元格背景
testCases := []struct {
row int
col int
backgroundColor string
pattern document.ShadingPattern
}{
{0, 0, "FF0000", document.ShadingPatternSolid}, // 红色实色
{0, 1, "00FF00", document.ShadingPatternPct50}, // 绿色50%
{0, 2, "0000FF", document.ShadingPatternPct25}, // 蓝色25%
{1, 0, "FFFF00", document.ShadingPatternSolid}, // 黄色实色
{1, 1, "FF00FF", document.ShadingPatternPct75}, // 紫色75%
{1, 2, "00FFFF", document.ShadingPatternPct10}, // 青色10%
}
for _, tc := range testCases {
shadingConfig := &document.ShadingConfig{
Pattern: tc.pattern,
BackgroundColor: tc.backgroundColor,
}
err := table.SetCellShading(tc.row, tc.col, shadingConfig)
if err != nil {
t.Fatalf("设置单元格(%d,%d)背景失败: %v", tc.row, tc.col, err)
}
}
t.Log("单元格背景设置成功")
}
// TestAlternatingRowColors 测试奇偶行颜色交替
func TestAlternatingRowColors(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 5,
Cols: 3,
Width: 4500,
Data: [][]string{
{"序号", "姓名", "分数"},
{"1", "张三", "85"},
{"2", "李四", "92"},
{"3", "王五", "78"},
{"4", "赵六", "95"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试设置奇偶行颜色交替
err := table.SetAlternatingRowColors("F0F0F0", "FFFFFF")
if err != nil {
t.Fatalf("设置奇偶行颜色交替失败: %v", err)
}
t.Log("奇偶行颜色交替设置成功")
}
// TestTableStyleTemplates 测试表格样式模板
func TestTableStyleTemplates(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 5000,
Data: [][]string{
{"项目", "预算", "实际"},
{"开发", "10000", "9500"},
{"测试", "5000", "5200"},
{"总计", "15000", "14700"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试应用表格样式模板
styleConfig := &document.TableStyleConfig{
Template: document.TableStyleTemplateGrid,
FirstRowHeader: true,
LastRowTotal: true,
BandedRows: true,
BandedColumns: false,
}
err := table.ApplyTableStyle(styleConfig)
if err != nil {
t.Fatalf("应用表格样式模板失败: %v", err)
}
t.Log("表格样式模板应用成功")
}
// TestCustomTableStyle 测试自定义表格样式
func TestCustomTableStyle(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 4500,
Data: [][]string{
{"功能", "状态", "备注"},
{"登录", "完成", "已测试"},
{"注册", "开发中", "50%"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 创建自定义边框配置
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "2E75B6",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "2E75B6",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
}
// 创建自定义背景配置
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternPct10,
BackgroundColor: "E7F3FF",
}
// 测试创建自定义表格样式
err := table.CreateCustomTableStyle("CustomStyle1", "自定义样式1", borderConfig, shadingConfig, true)
if err != nil {
t.Fatalf("创建自定义表格样式失败: %v", err)
}
t.Log("自定义表格样式创建成功")
}
// TestRemoveBorders 测试移除边框功能
func TestRemoveBorders(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
Data: [][]string{
{"A", "B"},
{"C", "D"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 先设置边框
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
t.Fatalf("设置表格边框失败: %v", err)
}
// 测试移除表格边框
err = table.RemoveTableBorders()
if err != nil {
t.Fatalf("移除表格边框失败: %v", err)
}
// 测试移除单元格边框
err = table.RemoveCellBorders(0, 0)
if err != nil {
t.Fatalf("移除单元格边框失败: %v", err)
}
t.Log("移除边框功能测试成功")
}
// TestComplexTableStyle 测试复杂表格样式组合
func TestComplexTableStyle(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 5,
Cols: 4,
Width: 6000,
Data: [][]string{
{"部门", "Q1", "Q2", "Q3"},
{"销售部", "120", "135", "150"},
{"技术部", "80", "90", "95"},
{"市场部", "60", "70", "85"},
{"总计", "260", "295", "330"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 应用样式模板
styleConfig := &document.TableStyleConfig{
Template: document.TableStyleTemplateColorful1,
FirstRowHeader: true,
LastRowTotal: true,
FirstColumnHeader: true,
BandedRows: true,
}
err := table.ApplyTableStyle(styleConfig)
if err != nil {
t.Fatalf("应用表格样式失败: %v", err)
}
// 设置自定义边框
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "B0B0B0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "B0B0B0",
Space: 0,
},
}
err = table.SetTableBorders(borderConfig)
if err != nil {
t.Fatalf("设置表格边框失败: %v", err)
}
// 为标题行设置特殊背景
for j := 0; j < 4; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "2E75B6",
ForegroundColor: "FFFFFF",
}
err = table.SetCellShading(0, j, shadingConfig)
if err != nil {
t.Fatalf("设置标题行背景失败: %v", err)
}
}
// 为总计行设置特殊背景
for j := 0; j < 4; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "FFFF99",
}
err = table.SetCellShading(4, j, shadingConfig)
if err != nil {
t.Fatalf("设置总计行背景失败: %v", err)
}
}
t.Log("复杂表格样式组合测试成功")
}
// BenchmarkTableStyleOperations 表格样式操作性能测试
func BenchmarkTableStyleOperations(b *testing.B) {
// 创建文档
doc := document.New()
if doc == nil {
b.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 10,
Cols: 5,
Width: 7500,
}
table := doc.AddTable(config)
if table == nil {
b.Fatal("创建表格失败")
}
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "000000",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "C0C0C0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "C0C0C0",
Space: 0,
},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := table.SetTableBorders(borderConfig)
if err != nil {
b.Fatalf("设置表格边框失败: %v", err)
}
}
}
// TestBorderStyles 测试各种边框样式
func TestBorderStyles(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 测试边框样式
borderStyles := []document.BorderStyle{
document.BorderStyleNone,
document.BorderStyleSingle,
document.BorderStyleThick,
document.BorderStyleDouble,
document.BorderStyleDotted,
document.BorderStyleDashed,
document.BorderStyleDotDash,
document.BorderStyleWave,
}
for i, style := range borderStyles {
// 为每种样式创建一个表格
config := &document.TableConfig{
Rows: 2,
Cols: 2,
Width: 2000,
Data: [][]string{
{fmt.Sprintf("样式%d", i+1), string(style)},
{"测试", "数据"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatalf("创建表格%d失败", i+1)
}
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: style,
Width: 6,
Color: "000000",
Space: 0,
},
Left: &document.BorderConfig{
Style: style,
Width: 6,
Color: "000000",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: style,
Width: 6,
Color: "000000",
Space: 0,
},
Right: &document.BorderConfig{
Style: style,
Width: 6,
Color: "000000",
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
t.Fatalf("设置表格%d边框失败: %v", i+1, err)
}
}
t.Log("边框样式测试完成")
}
// TestShadingPatterns 测试各种底纹图案
func TestShadingPatterns(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 测试底纹图案
patterns := []document.ShadingPattern{
document.ShadingPatternClear,
document.ShadingPatternSolid,
document.ShadingPatternPct25,
document.ShadingPatternPct50,
document.ShadingPatternPct75,
document.ShadingPatternHorzStripe,
document.ShadingPatternVertStripe,
document.ShadingPatternDiagStripe,
}
// 创建表格
config := &document.TableConfig{
Rows: len(patterns),
Cols: 2,
Width: 3000,
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
for i, pattern := range patterns {
// 设置单元格内容
err := table.SetCellText(i, 0, fmt.Sprintf("图案%d", i+1))
if err != nil {
t.Fatalf("设置单元格文本失败: %v", err)
}
err = table.SetCellText(i, 1, string(pattern))
if err != nil {
t.Fatalf("设置单元格文本失败: %v", err)
}
// 设置单元格背景
shadingConfig := &document.ShadingConfig{
Pattern: pattern,
BackgroundColor: "C0C0C0",
ForegroundColor: "000000",
}
err = table.SetCellShading(i, 1, shadingConfig)
if err != nil {
t.Fatalf("设置单元格背景失败: %v", err)
}
}
t.Log("底纹图案测试完成")
}

View File

@@ -30,15 +30,16 @@ func TestTextFormatting(t *testing.T) {
} }
// 检查段落是否被正确添加 // 检查段落是否被正确添加
if len(doc.Body.Paragraphs) != 1 { paragraphs := doc.Body.GetParagraphs()
t.Errorf("预期1个段落但得到了 %d 个", len(doc.Body.Paragraphs)) if len(paragraphs) != 1 {
t.Errorf("预期1个段落但得到了 %d 个", len(paragraphs))
} }
// 检查运行属性 // 检查运行属性
if len(doc.Body.Paragraphs[0].Runs) == 0 { if len(paragraphs[0].Runs) == 0 {
t.Error("段落中没有运行") t.Error("段落中没有运行")
} else { } else {
run := doc.Body.Paragraphs[0].Runs[0] run := paragraphs[0].Runs[0]
if run.Properties == nil { if run.Properties == nil {
t.Error("运行属性为空") t.Error("运行属性为空")
} else { } else {
@@ -199,12 +200,13 @@ func TestDocumentSaveAndOpen(t *testing.T) {
} }
// 验证内容 // 验证内容
if len(openedDoc.Body.Paragraphs) != 1 { paragraphs := openedDoc.Body.GetParagraphs()
t.Errorf("预期1个段落但得到了 %d 个", len(openedDoc.Body.Paragraphs)) if len(paragraphs) != 1 {
t.Errorf("预期1个段落但得到了 %d 个", len(paragraphs))
} }
if len(openedDoc.Body.Paragraphs) > 0 { if len(paragraphs) > 0 {
para := openedDoc.Body.Paragraphs[0] para := paragraphs[0]
// 检查对齐方式 // 检查对齐方式
if para.Properties == nil || para.Properties.Justification == nil { if para.Properties == nil || para.Properties.Justification == nil {

48
test_runner.go Normal file
View File

@@ -0,0 +1,48 @@
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
fmt.Println("Running tests for WordZero project...")
// 运行pkg包的测试
fmt.Println("\n=== Testing pkg packages ===")
cmd := exec.Command("go", "test", "./pkg/...", "-v")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Printf("pkg tests failed: %v\n", err)
} else {
fmt.Println("pkg tests passed")
}
// 运行test包的测试
fmt.Println("\n=== Testing test package ===")
cmd = exec.Command("go", "test", "./test", "-v")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
fmt.Printf("test package failed: %v\n", err)
} else {
fmt.Println("test package passed")
}
// 运行所有测试
fmt.Println("\n=== Running all tests ===")
cmd = exec.Command("go", "test", "./...", "-cover")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
fmt.Printf("Overall tests failed: %v\n", err)
os.Exit(1)
} else {
fmt.Println("All tests passed!")
}
}