Skill: jirboy/word-vba (version 1.0.0)

--- name: word-vba description: "Word文档读写Skill - 基于VBA和ActiveX，支持doc/docx格式，提供完整读写、批量处理、文档合并、模板填充等功能" metadata: openclaw: emoji: 📄 --- # Word VBA 文档处理 Skill ## 🎯 一句话定义 通过Python调用Word VBA/ActiveX，实现Word文档的完整读写、批量处理、文档合并、模板填充等高级功能。 --- ## 📥 如何调用 (How to use me) **触发语句：** - "读取Word文档" - "写入Word文件" - "合并多个Word文档" - "填充Word模板" - "批量替换Word内容" **需要提供的信息：** 1. **必需：** 文件路径、操作类型 2. **可选：** 格式要求、输出路径 --- ## 🛠️ 核心模块 ### 1. word_vba_reader - 文档读取 **功能：** 读取Word文档的文本和完整格式信息 **使用示例：** ```python from word_vba_reader import WordReader reader = WordReader() result = reader.read_document("D:/docs/申请书.docx") # 查看文档统计 print(f"段落数: {result['paragraph_count']}") print(f"词数: {result['word_count']}") print(f"页数: {result['pages']}") # 遍历段落 for para in result['paragraphs']: print(f"文本: {para['text']}") print(f"字体: {para['format']['font_name']}") print(f"大小: {para['format']['font_size']}") print(f"粗体: {para['format']['bold']}") ``` --- ### 2. word_vba_writer - 文档写入 **功能：** 创建新Word文档并设置格式 **使用示例：** ```python from word_vba_writer import WordWriter writer = WordWriter() doc = writer.create_document() # 添加标题 writer.add_heading(doc, "一、立项依据", level=1) # 添加格式段落 writer.add_paragraph(doc, "这是正文内容。", { 'font_name': '宋体', 'font_size': 12, 'bold': False, 'alignment': 'justify', 'first_line_indent': 24 # 首行缩进24磅 }) # 添加表格 table_data = [ ["姓名", "年龄", "单位"], ["张三", "30", "清华大学"], ["李四", "28", "北京大学"] ] writer.add_table(doc, 3, 3, table_data) # 保存 writer.save_document(doc, "D:/output/文档.docx") writer.close() ``` --- ### 3. word_vba_utils - 高级功能 #### 3.1 文档合并 **功能：** 将多个Word文档合并为一个 **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 合并多个章节为完整申请书 file_list = [ "D:/chapters/摘要.docx", "D:/chapters/立项依据.docx", "D:/chapters/研究内容.docx", "D:/chapters/研究基础.docx" ] utils.merge_documents( file_list=file_list, output_path="D:/output/完整申请书.docx", add_page_breaks=True, # 章节间添加分页符 keep_source_format=True # 保留源格式 ) ``` --- #### 3.2 模板填充 **功能：** 根据模板和数据生成文档 **模板示例：** ``` 项目名称: {{project_name}} 项目负责人: {{leader}} 研究期限: {{start_date}} 至 {{end_date}} 申请经费: {{budget}}万元 ``` **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 填充数据 data = { 'project_name': '振动台子结构试验关键技术研究', 'leader': '纪金豹', 'start_date': '2026.01', 'end_date': '2029.12', 'budget': '50' } utils.fill_template( template_path="D:/templates/基金模板.docx", data=data, output_path="D:/output/填充后申请书.docx" ) ``` --- #### 3.3 批量替换 **功能：** 批量替换文档中的文本 **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 定义替换规则 replacements = { '2025年': '2026年', '面上项目': '青年基金项目', '50万元': '30万元' } utils.batch_replace( file_path="D:/docs/旧版本.docx", replacements=replacements, output_path="D:/output/新版本.docx" ) ``` --- #### 3.4 提取标题大纲 **功能：** 提取文档的标题结构 **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() headings = utils.extract_headings("D:/docs/论文.docx") for h in headings: indent = " " * (h['level'] - 1) print(f"{indent}{h['level']}. {h['text']}") # 输出: # 1. 第一章 引言 # 1.1 研究背景 # 1.2 研究意义 # 2. 第二章 文献综述 # ... ``` --- #### 3.5 生成目录 **功能：** 为文档自动生成目录 **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() utils.generate_toc( file_path="D:/docs/论文.docx", output_path="D:/output/论文_含目录.docx" ) ``` --- #### 3.6 文档比较 **功能：** 比较两个文档的差异 **使用示例：** ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() utils.compare_documents( doc1_path="D:/docs/初稿.docx", doc2_path="D:/docs/修改稿.docx", output_path="D:/output/差异比较.docx" ) ``` --- #### 3.7 批量处理 **功能：** 对多个文档执行相同操作 **使用示例：** ```python from word_vba_utils import WordVBAUtils from word_vba_writer import WordWriter utils = WordVBAUtils() # 定义处理函数：添加页眉 def add_header(doc): header = doc.Sections(1).Headers(1) header.Range.Text = "机密文件" header.Range.Font.Name = "宋体" header.Range.Font.Size = 10 # 批量处理 file_list = ["D:/docs/1.docx", "D:/docs/2.docx", "D:/docs/3.docx"] utils.batch_process( file_list=file_list, processor=add_header, output_dir="D:/output/processed/" ) ``` --- ## 💡 典型应用场景 ### 场景1: 基金申请书整合 ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 合并各章节 sections = [ "摘要.docx", "一、立项依据与研究内容.docx", "二、研究方案.docx", "三、创新点.docx", "四、研究基础.docx" ] utils.merge_documents( file_list=[f"D:/fund/{s}" for s in sections], output_path="D:/fund/NSFC申请书_完整版.docx", add_page_breaks=True ) ``` ### 场景2: 批量生成证书 ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 获奖名单 winners = [ {'name': '张三', 'award': '一等奖', 'date': '2026-03-04'}, {'name': '李四', 'award': '二等奖', 'date': '2026-03-04'}, {'name': '王五', 'award': '三等奖', 'date': '2026-03-04'} ] # 批量生成 for i, person in enumerate(winners, 1): utils.fill_template( template_path="D:/templates/证书模板.docx", data=person, output_path=f"D:/output/证书_{i}_{person['name']}.docx" ) ``` ### 场景3: 论文格式统一 ```python from word_vba_utils import WordVBAUtils utils = WordVBAUtils() # 统一替换期刊名称缩写 replacements = { 'Earthquake Engineering and Structural Dynamics': 'Earthq. Eng. Struct. Dyn.', 'Journal of Structural Engineering': 'J. Struct. Eng.', 'Mechanical Systems and Signal Processing': 'Mech. Syst. Signal Process.' } utils.batch_replace( file_path="D:/papers/参考文献.docx", replacements=replacements, output_path="D:/papers/参考文献_缩写版.docx" ) ``` --- ## ⚙️ 依赖要求 - **操作系统**: Windows（必需） - **Microsoft Word**: 2010或更高版本（必需） - **Python**: 3.8+ - **Python库**: `pip install pywin32` --- ## 🔧 与 docx-tools 的区别 | 特性 | word-vba | docx-tools | |------|----------|------------| | **依赖** | 需要Microsoft Word | 纯Python | | **平台** | 仅Windows | 跨平台 | | **功能** | 完整（VBA所有功能） | 基础功能 | | **速度** | 较慢（启动Word） | 快 | | **格式支持** | 100%（包括复杂样式） | 基本格式 | | **批量处理** | ✅ 支持 | ✅ 支持 | | **模板填充** | ✅ 支持 | ❌ 不支持 | | **文档合并** | ✅ 保留格式 | ⚠️ 有限支持 | | **目录生成** | ✅ 支持 | ❌ 不支持 | **选择建议：** - 需要精确格式控制、复杂操作 → **word-vba** - 快速批量处理、跨平台需求 → **docx-tools** --- ## 🐛 故障排除 ### 问题："无法创建Word应用对象" **原因**: Word未安装或COM接口未注册 **解决**: 1. 确保已安装Microsoft Word 2. 尝试修复Office安装 3. 以管理员身份运行 ### 问题：文档被占用无法打开 **解决**: 关闭Word中已打开的该文档，或重启Word进程 ### 问题：格式信息读取不完整 **解决**: 检查文档是否使用了特殊样式或模板 ### 问题："ImportError: No module named 'win32com'" **解决**: `pip install pywin32` --- ## 📝 最佳实践 1. **使用后及时关闭**: 调用 `writer.close()` 或 `utils._close_word()` 释放资源 2. **设置 visible=False**: 后台运行提高速度 3. **批量处理**: 使用 `batch_process` 而不是循环打开单个文件 4. **异常处理**: 使用 try-except 包裹代码，确保Word进程能正常关闭 --- _技能版本: v2.0_ _更新日期: 2026-03-04_ _更新说明: 添加高级功能模块（合并、模板、批量处理等）_

path: README.md
content: # Word VBA Skill

基于Microsoft Word VBA/ActiveX的Word文档处理工具包。

## 功能特性

- ✅ 完整的Word文档读写
- ✅ 批量文档处理
- ✅ 文档合并（保留格式）
- ✅ 模板填充（{{placeholder}}语法）
- ✅ 批量文本替换
- ✅ 标题大纲提取
- ✅ 自动生成目录
- ✅ 文档比较

## 快速开始

### 安装依赖

```bash
pip install pywin32
```

### 基本使用

```python
from word_vba import WordWriter, WordReader

# 创建文档
writer = WordWriter()
doc = writer.create_document()
writer.add_paragraph(doc, "Hello World", {'font_name': '宋体', 'font_size': 12})
writer.save_document(doc, "output.docx")
writer.close()

# 读取文档
reader = WordReader()
content = reader.read_document("output.docx")
print(content['paragraphs'][0]['text'])
```

### 高级功能

```python
from word_vba import WordVBAUtils

utils = WordVBAUtils()

# 合并文档
utils.merge_documents(
    ["doc1.docx", "doc2.docx"],
    "merged.docx"
)

# 填充模板
data = {'name': '张三', 'date': '2026-03-04'}
utils.fill_template("template.docx", data, "output.docx")
```

## 运行测试

```bash
python test_word_vba.py
```

## 目录结构

```
word-vba/
├── SKILL.md              # 详细文档
├── README.md             # 本文件
├── requirements.txt      # 依赖
├── __init__.py           # 包初始化
├── word_vba_reader.py    # 读取模块
├── word_vba_writer.py    # 写入模块
├── word_vba_utils.py     # 高级功能
└── test_word_vba.py      # 测试脚本
```

## 许可证

MIT License


path: requirements.txt
content: # Word VBA Skill 依赖
# 安装命令: pip install -r requirements.txt

# 核心依赖
pywin32>=227

# 可选依赖
# python-docx  # 如需与docx-tools协同使用


path: test_word_vba.py
content: #!/usr/bin/env python3
"""
Word VBA Skill 综合测试脚本
测试读取和写入功能的完整流程

作者: SuperMike
日期: 2026-03-04
"""

import sys
import tempfile
from pathlib import Path

# 添加当前目录到路径
sys.path.insert(0, str(Path(__file__).parent))

from word_vba_writer import WordWriter
from word_vba_reader import WordReader


def test_full_workflow():
    """测试完整工作流程：创建 -> 写入 -> 读取 -> 验证"""
    
    print("=" * 70)
    print("Word VBA Skill 综合测试")
    print("=" * 70)
    
    temp_dir = Path(tempfile.gettempdir())
    test_docx = temp_dir / "word_vba_full_test.docx"
    
    try:
        # ========== 阶段1: 创建并写入文档 ==========
        print("\n[阶段1] 创建并写入Word文档...")
        print("-" * 70)
        
        writer = WordWriter(visible=False)
        doc = writer.create_document()
        
        # 添加标题
        print("  → 添加标题...")
        writer.add_heading(doc, "国家自然科学基金申请书", level=1)
        writer.add_heading(doc, "项目名称：基于物理信息强化学习的振动台试验研究", level=2)
        
        # 添加空行
        writer.add_paragraph(doc, "", {})
        
        # 添加正文
        print("  → 添加正文段落...")
        writer.add_paragraph(doc, 
            "1. 项目背景与意义",
            {'font_name': '黑体', 'font_size': 14, 'bold': True}
        )
        
        writer.add_paragraph(doc,
            "结构抗震试验是保障工程结构地震安全性的关键手段。随着我国城镇化进程加速，"
            "对结构抗震性能的精确评估需求日益迫切。振动台试验作为最直观、最可靠的抗震试验方法，"
            "能够真实再现结构在地震作用下的动力响应。",
            {'font_name': '宋体', 'font_size': 12, 'alignment': 'justify'}
        )
        
        writer.add_paragraph(doc, "", {})
        
        writer.add_paragraph(doc,
            "2. 国内外研究现状",
            {'font_name': '黑体', 'font_size': 14, 'bold': True}
        )
        
        writer.add_paragraph(doc,
            "实时混合试验技术自20世纪90年代发展以来，已成为结构抗震试验研究的重要手段。"
            "Nakashima等[1]于1992年奠定了RTHS的基础理论框架，建立了基于显式积分算法的实时计算体系。",
            {'font_name': '宋体', 'font_size': 12, 'alignment': 'justify'}
        )
        
        # 添加表格
        print("  → 添加表格...")
        table_data = [
            ["研究内容", "预期目标", "考核指标"],
            ["R-PINN建模", "小样本高精度建模", "50组样本，R²>0.95"],
            ["双时间尺度架构", "实时模型更新", "周期≤5ms"],
            ["PIRL控制", "安全高效控制", "样本效率提升10倍"]
        ]
        writer.add_table(doc, 4, 3, table_data)
        
        # 添加格式测试段落
        writer.add_paragraph(doc, "", {})
        writer.add_paragraph(doc,
            "3. 格式测试",
            {'font_name': '黑体', 'font_size': 14, 'bold': True}
        )
        
        writer.add_paragraph(doc,
            "粗体文字测试",
            {'font_name': '宋体', 'font_size': 12, 'bold': True}
        )
        
        writer.add_paragraph(doc,
            "斜体文字测试",
            {'font_name': '宋体', 'font_size': 12, 'italic': True}
        )
        
        writer.add_paragraph(doc,
            "居中文字测试",
            {'font_name': '宋体', 'font_size': 12, 'alignment': 'center'}
        )
        
        writer.add_paragraph(doc,
            "右对齐文字测试",
            {'font_name': '宋体', 'font_size': 12, 'alignment': 'right'}
        )
        
        # 保存文档
        print("  → 保存文档...")
        writer.save_document(doc, str(test_docx))
        writer.close()
        
        print(f"✓ 文档已创建: {test_docx}")
        
        # ========== 阶段2: 读取文档 ==========
        print("\n[阶段2] 读取Word文档...")
        print("-" * 70)
        
        reader = WordReader()
        result = reader.read_document(str(test_docx))
        
        print(f"✓ 文档统计:")
        print(f"    - 段落数: {result['paragraph_count']}")
        print(f"    - 词数: {result['word_count']}")
        print(f"    - 页数: {result['pages']}")
        print(f"    - 表格数: {len(result['tables'])}")
        
        # ========== 阶段3: 验证内容 ==========
        print("\n[阶段3] 验证读取内容...")
        print("-" * 70)
        
        # 验证段落
        paragraphs = result['paragraphs']
        
        # 找非空段落
        non_empty = [p for p in paragraphs if not p.get('is_empty', True)]
        
        print(f"  → 非空段落数: {len(non_empty)}")
        
        # 验证标题格式
        title_paras = [p for p in non_empty if '国家自然科学基金' in p.get('text', '')]
        if title_paras:
            title = title_paras[0]
            print(f"  ✓ 找到标题: {title['text'][:30]}...")
            print(f"    - 字体: {title['format']['font_name_far_east'] or title['format']['font_name']}")
            print(f"    - 大小: {title['format']['font_size']}pt")
            print(f"    - 粗体: {title['format']['bold']}")
        
        # 验证正文格式
        body_paras = [p for p in non_empty if '结构抗震试验' in p.get('text', '')]
        if body_paras:
            body = body_paras[0]
            print(f"  ✓ 找到正文段落")
            print(f"    - 对齐: {body['paragraph_format']['alignment']}")
            print(f"    - 字体: {body['format']['font_name_far_east'] or body['format']['font_name']}")
        
        # 验证表格
        if result['tables']:
            table = result['tables'][0]
            print(f"  ✓ 找到表格: {table['rows']}行 x {table['columns']}列")
            print(f"    - 表头: {table['content'][0]}")
        
        # ========== 阶段4: 导出JSON ==========
        print("\n[阶段4] 导出为JSON...")
        print("-" * 70)
        
        json_path = test_docx.with_suffix('.json')
        reader2 = WordReader()
        reader2.export_to_json(str(test_docx), str(json_path))
        reader2._close_word()
        
        if json_path.exists():
            import json
            with open(json_path, 'r', encoding='utf-8') as f:
                json_data = json.load(f)
            print(f"✓ JSON导出成功")
            print(f"    - 文件大小: {json_path.stat().st_size} 字节")
            print(f"    - 包含段落: {len(json_data.get('paragraphs', []))}")
        
        # ========== 阶段5: 从JSON恢复 ==========
        print("\n[阶段5] 从JSON恢复文档...")
        print("-" * 70)
        
        restored_docx = temp_dir / "word_vba_restored.docx"
        writer2 = WordWriter()
        writer2.write_from_json(str(json_path), str(restored_docx))
        writer2.close()
        
        if restored_docx.exists():
            print(f"✓ 文档恢复成功: {restored_docx}")
            
            # 验证恢复的文档
            reader3 = WordReader()
            restored_result = reader3.read_document(str(restored_docx))
            reader3._close_word()
            
            if restored_result['paragraph_count'] == result['paragraph_count']:
                print(f"✓ 段落数一致: {restored_result['paragraph_count']}")
            else:
                print(f"! 段落数不一致: 原{result['paragraph_count']} vs 恢复{restored_result['paragraph_count']}")
        
        # ========== 测试完成 ==========
        print("\n" + "=" * 70)
        print("所有测试通过！")
        print("=" * 70)
        print("\n测试总结:")
        print(f"  ✓ Word文档创建和写入")
        print(f"  ✓ 段落格式设置（字体、大小、对齐）")
        print(f"  ✓ 表格创建和数据填充")
        print(f"  ✓ 文档读取和内容提取")
        print(f"  ✓ 格式信息读取")
        print(f"  ✓ JSON导出和恢复")
        
        # 清理临时文件
        for f in [test_docx, json_path, restored_docx]:
            if f.exists():
                f.unlink(missing_ok=True)
        
        return True
        
    except Exception as e:
        print(f"\n✗ 测试失败: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_edge_cases():
    """测试边界情况"""
    print("\n" + "=" * 70)
    print("边界情况测试")
    print("=" * 70)
    
    temp_dir = Path(tempfile.gettempdir())
    
    tests_passed = 0
    tests_total = 0
    
    # 测试1: 空文档
    tests_total += 1
    print("\n[测试1] 创建空文档...")
    try:
        writer = WordWriter()
        doc = writer.create_document()
        empty_doc = temp_dir / "empty.docx"
        writer.save_document(doc, str(empty_doc))
        writer.close()
        
        reader = WordReader()
        result = reader.read_document(str(empty_doc))
        reader._close_word()
        
        print(f"  ✓ 空文档创建成功，包含 {result['paragraph_count']} 个段落")
        tests_passed += 1
        empty_doc.unlink(missing_ok=True)
    except Exception as e:
        print(f"  ✗ 失败: {e}")
    
    # 测试2: 特殊字符
    tests_total += 1
    print("\n[测试2] 特殊字符处理...")
    try:
        writer = WordWriter()
        doc = writer.create_document()
        
        special_texts = [
            "包含数字123和符号!@#$%",
            "中文标点：，。！？；：",
            "英文标点,.!?;:"
        ]
        
        for text in special_texts:
            writer.add_paragraph(doc, text, {'font_name': '宋体', 'font_size': 12})
        
        special_doc = temp_dir / "special.docx"
        writer.save_document(doc, str(special_doc))
        writer.close()
        
        reader = WordReader()
        result = reader.read_document(str(special_doc))
        reader._close_word()
        
        print(f"  ✓ 特殊字符处理成功")
        tests_passed += 1
        special_doc.unlink(missing_ok=True)
    except Exception as e:
        print(f"  ✗ 失败: {e}")
    
    # 测试3: 大段落
    tests_total += 1
    print("\n[测试3] 大段落处理...")
    try:
        writer = WordWriter()
        doc = writer.create_document()
        
        large_text = "这是一个很长的段落。" * 100
        writer.add_paragraph(doc, large_text, {'font_name': '宋体', 'font_size': 12})
        
        large_doc = temp_dir / "large.docx"
        writer.save_document(doc, str(large_doc))
        writer.close()
        
        reader = WordReader()
        result = reader.read_document(str(large_doc))
        reader._close_word()
        
        print(f"  ✓ 大段落处理成功")
        tests_passed += 1
        large_doc.unlink(missing_ok=True)
    except Exception as e:
        print(f"  ✗ 失败: {e}")
    
    print(f"\n边界测试完成: {tests_passed}/{tests_total} 通过")
    return tests_passed == tests_total


def main():
    """主函数"""
    print("\n" + "=" * 70)
    print("Word VBA Skill 完整测试套件")
    print("=" * 70 + "\n")
    
    # 运行主测试
    main_passed = test_full_workflow()
    
    # 运行边界测试
    edge_passed = test_edge_cases()
    
    # 最终总结
    print("\n" + "=" * 70)
    if main_passed and edge_passed:
        print("所有测试通过！Skill可以正常使用。")
    else:
        print("部分测试失败，请检查错误信息。")
    print("=" * 70)


if __name__ == "__main__":
    main()


path: word_vba_reader.py
content: #!/usr/bin/env python3
"""
Word文档读取模块 - 基于VBA/ActiveX
支持读取doc和docx格式，包括文本和段落格式信息

作者: SuperMike
日期: 2026-03-04
"""

import win32com.client
import pythoncom
from pathlib import Path
from typing import Dict, List, Optional, Any
import json


class WordReader:
    """Word文档读取器 - 使用COM接口调用Word VBA"""
    
    def __init__(self, visible: bool = False):
        """
        初始化Word读取器
        
        Args:
            visible: 是否显示Word窗口，默认False（后台运行）
        """
        self.word_app = None
        self.visible = visible
        self.document = None
        
    def _init_word(self) -> bool:
        """初始化Word应用"""
        try:
            pythoncom.CoInitialize()
            self.word_app = win32com.client.Dispatch("Word.Application")
            self.word_app.Visible = self.visible
            self.word_app.DisplayAlerts = 0  # 不显示警告
            print("✓ Word应用启动成功")
            return True
        except Exception as e:
            print(f"✗ Word应用启动失败: {e}")
            return False
    
    def _close_word(self):
        """关闭Word应用"""
        try:
            if self.document:
                self.document.Close(SaveChanges=False)
                self.document = None
            if self.word_app:
                self.word_app.Quit()
                self.word_app = None
            pythoncom.CoUninitialize()
            print("✓ Word应用已关闭")
        except Exception as e:
            print(f"! 关闭Word时出错: {e}")
    
    def read_document(self, file_path: str) -> Dict[str, Any]:
        """
        读取Word文档内容和格式
        
        Args:
            file_path: Word文档路径（支持.doc和.docx）
            
        Returns:
            包含文档信息的字典
        """
        file_path = Path(file_path)
        
        if not file_path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        if not self._init_word():
            raise RuntimeError("无法初始化Word应用")
        
        try:
            print(f"正在打开文档: {file_path}")
            self.document = self.word_app.Documents.Open(str(file_path.absolute()))
            
            result = {
                'file_path': str(file_path),
                'file_name': file_path.name,
                'paragraph_count': self.document.Paragraphs.Count,
                'word_count': self.document.ComputeStatistics(0),  # wdStatisticWords
                'pages': self.document.ComputeStatistics(2),  # wdStatisticPages
                'paragraphs': [],
                'tables': []
            }
            
            # 读取所有段落
            print("正在读取段落内容...")
            for i, para in enumerate(self.document.Paragraphs, 1):
                para_info = self._extract_paragraph_info(para, i)
                result['paragraphs'].append(para_info)
                
                if i % 50 == 0:
                    print(f"  已读取 {i}/{result['paragraph_count']} 个段落")
            
            # 读取表格信息
            if self.document.Tables.Count > 0:
                print(f"发现 {self.document.Tables.Count} 个表格，正在读取...")
                for i, table in enumerate(self.document.Tables, 1):
                    table_info = self._extract_table_info(table, i)
                    result['tables'].append(table_info)
            
            print(f"✓ 文档读取完成: {result['paragraph_count']} 段落, "
                  f"{result['word_count']} 词, {result['pages']} 页")
            
            return result
            
        except Exception as e:
            print(f"✗ 读取文档时出错: {e}")
            raise
        finally:
            self._close_word()
    
    def _extract_paragraph_info(self, para, index: int) -> Dict[str, Any]:
        """提取段落信息"""
        try:
            # 获取Range对象以访问格式
            rng = para.Range
            font = rng.Font
            
            # 对齐方式映射
            alignment_map = {
                0: 'left',      # wdAlignParagraphLeft
                1: 'center',    # wdAlignParagraphCenter
                2: 'right',     # wdAlignParagraphRight
                3: 'justify'    # wdAlignParagraphJustify
            }
            
            # 行距规则映射
            line_spacing_map = {
                0: 'single',       # wdLineSpaceSingle
                1: '1.5',          # wdLineSpace1pt5
                2: 'double',       # wdLineSpaceDouble
                3: 'at_least',     # wdLineSpaceAtLeast
                4: 'exactly',      # wdLineSpaceExactly
                5: 'multiple'      # wdLineSpaceMultiple
            }
            
            # 大纲级别映射
            outline_level = para.OutlineLevel
            if outline_level <= 9:
                outline_name = f"标题{outline_level}"
            else:
                outline_name = "正文"
            
            return {
                'index': index,
                'text': para.Range.Text.strip(),
                'style': para.Style.NameLocal if para.Style else None,
                'outline_level': outline_level if outline_level <= 9 else None,
                'outline_name': outline_name,
                'format': {
                    'font_name': font.Name,
                    'font_name_far_east': font.NameFarEast,  # 中文字体
                    'font_size': font.Size,
                    'bold': font.Bold,
                    'italic': font.Italic,
                    'underline': font.Underline != 0,  # wdUnderlineNone = 0
                    'color': font.Color if hasattr(font, 'Color') else None,
                    'highlight': font.HighlightColorIndex if hasattr(font, 'HighlightColorIndex') else None
                },
                'paragraph_format': {
                    'alignment': alignment_map.get(para.Alignment, 'unknown'),
                    'line_spacing_rule': line_spacing_map.get(para.LineSpacingRule, 'unknown'),
                    'line_spacing': para.LineSpacing,
                    'space_before': para.SpaceBefore,
                    'space_after': para.SpaceAfter,
                    'first_line_indent': para.FirstLineIndent,
                    'left_indent': para.LeftIndent,
                    'right_indent': para.RightIndent
                },
                'is_empty': len(para.Range.Text.strip()) == 0,
                'is_in_table': para.Range.Information(12)  # wdWithInTable
            }
        except Exception as e:
            return {
                'index': index,
                'text': '[读取失败]',
                'error': str(e)
            }
    
    def _extract_table_info(self, table, index: int) -> Dict[str, Any]:
        """提取表格信息"""
        try:
            rows = table.Rows.Count
            cols = table.Columns.Count
            
            # 读取表格内容
            cells_content = []
            for row in range(1, rows + 1):
                row_data = []
                for col in range(1, cols + 1):
                    try:
                        cell_text = table.Cell(row, col).Range.Text.strip()
                        # 去掉单元格文本末尾的\r\x07
                        cell_text = cell_text.replace('\r', '').replace('\x07', '')
                        row_data.append(cell_text)
                    except:
                        row_data.append('')
                cells_content.append(row_data)
            
            return {
                'index': index,
                'rows': rows,
                'columns': cols,
                'content': cells_content
            }
        except Exception as e:
            return {
                'index': index,
                'error': str(e)
            }
    
    def read_text_only(self, file_path: str) -> str:
        """
        仅读取纯文本内容（不包含格式）
        
        Args:
            file_path: Word文档路径
            
        Returns:
            文档的纯文本内容
        """
        result = self.read_document(file_path)
        texts = []
        for para in result['paragraphs']:
            if para['text'] and not para['is_empty']:
                texts.append(para['text'])
        return '\n'.join(texts)
    
    def export_to_json(self, file_path: str, output_path: Optional[str] = None) -> str:
        """
        将Word文档导出为JSON格式
        
        Args:
            file_path: Word文档路径
            output_path: 输出JSON文件路径，默认为同名.json
            
        Returns:
            输出文件路径
        """
        result = self.read_document(file_path)
        
        if output_path is None:
            output_path = str(Path(file_path).with_suffix('.json'))
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(result, f, ensure_ascii=False, indent=2)
        
        print(f"✓ 已导出到: {output_path}")
        return output_path


def test_read():
    """测试读取功能"""
    import tempfile
    
    print("=" * 60)
    print("Word读取功能测试")
    print("=" * 60)
    
    # 先创建一个测试文档
    print("\n[测试1] 创建测试文档...")
    try:
        from word_vba_writer import WordWriter
        writer = WordWriter()
        doc = writer.create_document()
        
        # 添加不同格式的段落
        writer.add_paragraph(doc, "标题：测试文档", {
            'font_name': '黑体',
            'font_size': 16,
            'bold': True,
            'alignment': 'center'
        })
        
        writer.add_paragraph(doc, "", {})  # 空行
        
        writer.add_paragraph(doc, "这是一段正文内容，用于测试读取功能。", {
            'font_name': '宋体',
            'font_size': 12,
            'alignment': 'justify'
        })
        
        writer.add_paragraph(doc, "这是粗体斜体文字", {
            'font_name': '宋体',
            'font_size': 12,
            'bold': True,
            'italic': True
        })
        
        # 保存到临时文件
        temp_file = Path(tempfile.gettempdir()) / "word_vba_test.docx"
        writer.save_document(doc, str(temp_file))
        writer.close()
        
        print(f"✓ 测试文档已创建: {temp_file}")
        
        # 读取测试
        print("\n[测试2] 读取测试文档...")
        reader = WordReader()
        result = reader.read_document(str(temp_file))
        
        print(f"\n文档统计:")
        print(f"  - 段落数: {result['paragraph_count']}")
        print(f"  - 词数: {result['word_count']}")
        print(f"  - 页数: {result['pages']}")
        
        print(f"\n段落详情:")
        for para in result['paragraphs'][:5]:  # 只显示前5个
            print(f"\n  [段落{para['index']}]")
            print(f"    文本: {para['text'][:50]}...")
            print(f"    字体: {para['format']['font_name_far_east'] or para['format']['font_name']}")
            print(f"    大小: {para['format']['font_size']}pt")
            print(f"    粗体: {para['format']['bold']}")
            print(f"    对齐: {para['paragraph_format']['alignment']}")
        
        # 导出JSON测试
        print("\n[测试3] 导出JSON...")
        json_path = reader.export_to_json(str(temp_file))
        
        print("\n" + "=" * 60)
        print("✓ 所有测试通过！")
        print("=" * 60)
        
        # 清理临时文件
        temp_file.unlink(missing_ok=True)
        Path(json_path).unlink(missing_ok=True)
        
        return True
        
    except Exception as e:
        print(f"\n✗ 测试失败: {e}")
        import traceback
        traceback.print_exc()
        return False


if __name__ == "__main__":
    test_read()


path: word_vba_utils.py
content: #!/usr/bin/env python3
"""
Word VBA 高级功能模块
提供批量处理、文档合并、模板填充等高级功能

作者: SuperMike
日期: 2026-03-04
"""

import win32com.client
import pythoncom
from pathlib import Path
from typing import Dict, List, Optional, Any, Tuple, Callable
import json
import re


class WordVBAUtils:
    """Word VBA 高级工具类"""
    
    def __init__(self, visible: bool = False):
        self.word_app = None
        self.visible = visible
        self.documents = []
        
    def _init_word(self) -> bool:
        """初始化Word应用"""
        try:
            pythoncom.CoInitialize()
            self.word_app = win32com.client.Dispatch("Word.Application")
            self.word_app.Visible = self.visible
            self.word_app.DisplayAlerts = 0
            return True
        except Exception as e:
            print(f"[ERROR] Word启动失败: {e}")
            return False
    
    def _close_word(self):
        """关闭Word应用"""
        try:
            for doc in self.documents:
                try:
                    doc.Close(SaveChanges=False)
                except:
                    pass
            if self.word_app:
                self.word_app.Quit()
                self.word_app = None
            pythoncom.CoUninitialize()
        except:
            pass
    
    def merge_documents(self, 
                       file_list: List[str], 
                       output_path: str,
                       add_page_breaks: bool = True,
                       keep_source_format: bool = True) -> str:
        """
        合并多个Word文档
        
        Args:
            file_list: 要合并的文档路径列表
            output_path: 输出文件路径
            add_page_breaks: 是否在每个文档后添加分页符
            keep_source_format: 是否保留源文档格式
            
        Returns:
            输出文件路径
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            print(f"[INFO] 开始合并 {len(file_list)} 个文档...")
            
            # 创建新文档作为主文档
            main_doc = self.word_app.Documents.Add()
            self.documents.append(main_doc)
            
            for i, file_path in enumerate(file_list, 1):
                file_path = Path(file_path)
                if not file_path.exists():
                    print(f"[WARN] 文件不存在，跳过: {file_path}")
                    continue
                
                print(f"[{i}/{len(file_list)}] 正在插入: {file_path.name}")
                
                # 插入文件内容
                insert_range = main_doc.Content
                insert_range.Collapse(Direction=0)  # wdCollapseEnd
                
                # 使用InsertFile插入，可以保留格式
                insert_range.InsertFile(
                    FileName=str(file_path.absolute()),
                    ConfirmConversions=False
                )
                
                # 添加分页符
                if add_page_breaks and i < len(file_list):
                    insert_range = main_doc.Content
                    insert_range.Collapse(Direction=0)
                    insert_range.InsertBreak(Type=7)  # wdPageBreak
            
            # 保存合并后的文档
            output_path = Path(output_path)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            main_doc.SaveAs2(str(output_path.absolute()))
            
            print(f"[OK] 合并完成: {output_path}")
            return str(output_path)
            
        except Exception as e:
            print(f"[ERROR] 合并失败: {e}")
            raise
        finally:
            self._close_word()
    
    def fill_template(self,
                     template_path: str,
                     data: Dict[str, Any],
                     output_path: str,
                     placeholder_pattern: str = r'\{\{(.*?)\}\}') -> str:
        """
        填充模板文档
        
        Args:
            template_path: 模板文件路径（包含 {{key}} 占位符）
            data: 填充数据字典
            output_path: 输出文件路径
            placeholder_pattern: 占位符正则表达式，默认 {{key}}
            
        Returns:
            输出文件路径
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            print(f"[INFO] 填充模板: {template_path}")
            
            # 打开模板
            template_path = Path(template_path)
            doc = self.word_app.Documents.Open(str(template_path.absolute()))
            self.documents.append(doc)
            
            # 查找并替换所有占位符
            replaced_count = 0
            for para in doc.Paragraphs:
                text = para.Range.Text
                matches = re.findall(placeholder_pattern, text)
                
                for key in matches:
                    key = key.strip()
                    if key in data:
                        placeholder = f"{{{{{key}}}}}"
                        value = str(data[key])
                        
                        # 使用Find替换
                        find_obj = doc.Content.Find
                        find_obj.Text = placeholder
                        find_obj.Replacement.Text = value
                        find_obj.Execute(Replace=2)  # wdReplaceAll
                        replaced_count += 1
            
            # 保存为新文档
            output_path = Path(output_path)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            doc.SaveAs2(str(output_path.absolute()))
            
            print(f"[OK] 填充完成，替换了 {replaced_count} 个占位符")
            print(f"[OK] 输出文件: {output_path}")
            
            return str(output_path)
            
        except Exception as e:
            print(f"[ERROR] 模板填充失败: {e}")
            raise
        finally:
            self._close_word()
    
    def batch_replace(self,
                     file_path: str,
                     replacements: Dict[str, str],
                     output_path: Optional[str] = None) -> str:
        """
        批量替换文档中的文本
        
        Args:
            file_path: 源文档路径
            replacements: 替换字典 {旧文本: 新文本}
            output_path: 输出路径，默认覆盖原文件
            
        Returns:
            输出文件路径
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            print(f"[INFO] 批量替换: {file_path}")
            
            file_path = Path(file_path)
            doc = self.word_app.Documents.Open(str(file_path.absolute()))
            self.documents.append(doc)
            
            replace_count = 0
            for old_text, new_text in replacements.items():
                find_obj = doc.Content.Find
                find_obj.Text = old_text
                find_obj.Replacement.Text = new_text
                
                while find_obj.Execute(Replace=1):  # wdReplaceOne
                    replace_count += 1
            
            # 保存
            if output_path is None:
                output_path = file_path
            else:
                output_path = Path(output_path)
                output_path.parent.mkdir(parents=True, exist_ok=True)
            
            doc.SaveAs2(str(Path(output_path).absolute()))
            
            print(f"[OK] 替换了 {replace_count} 处文本")
            return str(output_path)
            
        except Exception as e:
            print(f"[ERROR] 替换失败: {e}")
            raise
        finally:
            self._close_word()
    
    def extract_headings(self, file_path: str) -> List[Dict[str, Any]]:
        """
        提取文档中的标题大纲
        
        Args:
            file_path: Word文档路径
            
        Returns:
            标题列表，包含级别和文本
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            file_path = Path(file_path)
            doc = self.word_app.Documents.Open(str(file_path.absolute()))
            self.documents.append(doc)
            
            headings = []
            for para in doc.Paragraphs:
                outline_level = para.OutlineLevel
                if outline_level <= 9:  # 是标题
                    headings.append({
                        'level': outline_level,
                        'text': para.Range.Text.strip(),
                        'style': para.Style.NameLocal if para.Style else None
                    })
            
            return headings
            
        except Exception as e:
            print(f"[ERROR] 提取标题失败: {e}")
            raise
        finally:
            self._close_word()
    
    def generate_toc(self, file_path: str, output_path: Optional[str] = None) -> str:
        """
        为文档生成目录
        
        Args:
            file_path: 源文档路径
            output_path: 输出路径，默认覆盖原文件
            
        Returns:
            输出文件路径
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            print(f"[INFO] 生成目录: {file_path}")
            
            file_path = Path(file_path)
            doc = self.word_app.Documents.Open(str(file_path.absolute()))
            self.documents.append(doc)
            
            # 在文档开头插入目录
            rng = doc.Content
            rng.Collapse(Direction=1)  # wdCollapseStart
            
            # 添加"目录"标题
            toc_para = doc.Content.Paragraphs.Add(rng)
            toc_para.Range.Text = "目录\r"
            toc_para.Style = doc.Styles("标题 1")
            
            # 插入目录域
            rng = doc.Content
            rng.Collapse(Direction=1)
            toc_field = doc.Fields.Add(rng, -1, "TOC \\o \"1-3\" \\h \\z \\u", True)
            
            # 更新目录
            toc_field.Update()
            
            # 保存
            if output_path is None:
                output_path = file_path
            else:
                output_path = Path(output_path)
                output_path.parent.mkdir(parents=True, exist_ok=True)
            
            doc.SaveAs2(str(Path(output_path).absolute()))
            
            print("[OK] 目录生成完成")
            return str(output_path)
            
        except Exception as e:
            print(f"[ERROR] 生成目录失败: {e}")
            raise
        finally:
            self._close_word()
    
    def compare_documents(self, 
                         doc1_path: str, 
                         doc2_path: str,
                         output_path: str) -> str:
        """
        比较两个文档的差异
        
        Args:
            doc1_path: 原始文档路径
            doc2_path: 修改后文档路径
            output_path: 比较结果输出路径
            
        Returns:
            输出文件路径
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        try:
            print(f"[INFO] 比较文档...")
            
            doc1 = self.word_app.Documents.Open(str(Path(doc1_path).absolute()))
            doc2 = self.word_app.Documents.Open(str(Path(doc2_path).absolute()))
            self.documents.extend([doc1, doc2])
            
            # 创建比较文档
            compare_doc = self.word_app.CompareDocuments(
                OriginalDocument=doc1,
                RevisedDocument=doc2
            )
            self.documents.append(compare_doc)
            
            # 保存比较结果
            output_path = Path(output_path)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            compare_doc.SaveAs2(str(output_path.absolute()))
            
            print(f"[OK] 比较完成: {output_path}")
            return str(output_path)
            
        except Exception as e:
            print(f"[ERROR] 比较失败: {e}")
            raise
        finally:
            self._close_word()
    
    def batch_process(self,
                     file_list: List[str],
                     processor: Callable[[Any], None],
                     output_dir: str):
        """
        批量处理多个文档
        
        Args:
            file_list: 文档路径列表
            processor: 处理函数，接收文档对象作为参数
            output_dir: 输出目录
        """
        if not self._init_word():
            raise RuntimeError("无法初始化Word")
        
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)
        
        try:
            for i, file_path in enumerate(file_list, 1):
                file_path = Path(file_path)
                if not file_path.exists():
                    continue
                
                print(f"[{i}/{len(file_list)}] 处理: {file_path.name}")
                
                doc = self.word_app.Documents.Open(str(file_path.absolute()))
                
                # 执行处理
                processor(doc)
                
                # 保存
                output_path = output_dir / file_path.name
                doc.SaveAs2(str(output_path.absolute()))
                doc.Close()
                
        except Exception as e:
            print(f"[ERROR] 批量处理失败: {e}")
            raise
        finally:
            self._close_word()


def test_utils():
    """测试高级功能"""
    import tempfile
    from word_vba_writer import WordWriter
    
    print("=" * 60)
    print("Word VBA 高级功能测试")
    print("=" * 60)
    
    utils = WordVBAUtils()
    temp_dir = Path(tempfile.gettempdir())
    
    try:
        # 测试1: 创建测试文档
        print("\n[测试1] 创建测试文档...")
        writer = WordWriter()
        
        # 文档1
        doc1 = writer.create_document()
        writer.add_heading(doc1, "第一章 引言", level=1)
        writer.add_paragraph(doc1, "这是第一章的内容。", {'font_name': '宋体'})
        doc1_path = temp_dir / "test_doc1.docx"
        writer.save_document(doc1, str(doc1_path))
        
        # 文档2
        doc2 = writer.create_document()
        writer.add_heading(doc2, "第二章 方法", level=1)
        writer.add_paragraph(doc2, "这是第二章的内容。", {'font_name': '宋体'})
        doc2_path = temp_dir / "test_doc2.docx"
        writer.save_document(doc2, str(doc2_path))
        writer.close()
        
        print(f"[OK] 创建测试文档完成")
        
        # 测试2: 合并文档
        print("\n[测试2] 测试文档合并...")
        merged_path = utils.merge_documents(
            [str(doc1_path), str(doc2_path)],
            str(temp_dir / "merged.docx")
        )
        print(f"[OK] 合并完成: {merged_path}")
        
        # 测试3: 提取标题
        print("\n[测试3] 提取文档标题...")
        headings = utils.extract_headings(str(doc1_path))
        print(f"[OK] 找到 {len(headings)} 个标题:")
        for h in headings:
            print(f"  - 级别{h['level']}: {h['text'][:30]}")
        
        # 测试4: 模板填充
        print("\n[测试4] 测试模板填充...")
        
        # 创建模板
        template_writer = WordWriter()
        template_doc = template_writer.create_document()
        template_writer.add_paragraph(template_doc, "项目名称: {{project_name}}", {})
        template_writer.add_paragraph(template_doc, "负责人: {{leader}}", {})
        template_writer.add_paragraph(template_doc, "预算: {{budget}}万元", {})
        template_path = temp_dir / "template.docx"
        template_writer.save_document(template_doc, str(template_path))
        template_writer.close()
        
        # 填充数据
        data = {
            'project_name': '国家自然科学基金项目',
            'leader': '纪金豹',
            'budget': '50'
        }
        filled_path = utils.fill_template(
            str(template_path),
            data,
            str(temp_dir / "filled.docx")
        )
        print(f"[OK] 模板填充完成: {filled_path}")
        
        # 测试5: 批量替换
        print("\n[测试5] 测试批量替换...")
        replacements = {
            '第一章': 'Chapter 1',
            '第二章': 'Chapter 2'
        }
        replaced_path = utils.batch_replace(
            str(merged_path),
            replacements,
            str(temp_dir / "replaced.docx")
        )
        print(f"[OK] 替换完成: {replaced_path}")
        
        print("\n" + "=" * 60)
        print("所有高级功能测试通过！")
        print("=" * 60)
        
        # 清理
        for f in temp_dir.glob("test_doc*.docx"):
            f.unlink(missing_ok=True)
        for f in temp_dir.glob("merged.docx"):
            f.unlink(missing_ok=True)
        for f in temp_dir.glob("template.docx"):
            f.unlink(missing_ok=True)
        for f in temp_dir.glob("filled.docx"):
            f.unlink(missing_ok=True)
        for f in temp_dir.glob("replaced.docx"):
            f.unlink(missing_ok=True)
        
        return True
        
    except Exception as e:
        print(f"\n[ERROR] 测试失败: {e}")
        import traceback
        traceback.print_exc()
        return False


if __name__ == "__main__":
    test_utils()


path: word_vba_writer.py
content: #!/usr/bin/env python3
"""
Word文档写入模块 - 基于VBA/ActiveX (编码修复版)
支持创建doc和docx文件，可设置段落格式

作者: SuperMike
日期: 2026-03-04
"""

import win32com.client
import pythoncom
from pathlib import Path
from typing import Dict, List, Optional, Any, Union
import json


class WordWriter:
    """Word文档写入器 - 使用COM接口调用Word VBA"""
    
    def __init__(self, visible: bool = False):
        """
        初始化Word写入器
        
        Args:
            visible: 是否显示Word窗口，默认False（后台运行）
        """
        self.word_app = None
        self.visible = visible
        self.document = None
        
    def _init_word(self) -> bool:
        """初始化Word应用"""
        try:
            pythoncom.CoInitialize()
            self.word_app = win32com.client.Dispatch("Word.Application")
            self.word_app.Visible = self.visible
            self.word_app.DisplayAlerts = 0
            print("[OK] Word应用启动成功")
            return True
        except Exception as e:
            print(f"[ERROR] Word应用启动失败: {e}")
            print("[提示] 请确保已安装Microsoft Word")
            return False
    
    def close(self):
        """关闭Word应用"""
        try:
            if self.document:
                self.document.Close(SaveChanges=False)
                self.document = None
            if self.word_app:
                self.word_app.Quit()
                self.word_app = None
            pythoncom.CoUninitialize()
            print("[OK] Word应用已关闭")
        except Exception as e:
            print(f"[WARN] 关闭Word时出错: {e}")
    
    def create_document(self) -> Any:
        """
        创建新的Word文档
        
        Returns:
            Word文档对象
        """
        if not self.word_app and not self._init_word():
            raise RuntimeError("无法初始化Word应用")
        
        try:
            self.document = self.word_app.Documents.Add()
            print("[OK] 新文档创建成功")
            return self.document
        except Exception as e:
            print(f"[ERROR] 创建文档失败: {e}")
            raise
    
    def open_document(self, file_path: str) -> Any:
        """
        打开现有Word文档
        
        Args:
            file_path: Word文档路径
            
        Returns:
            Word文档对象
        """
        file_path = Path(file_path)
        
        if not file_path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        if not self.word_app and not self._init_word():
            raise RuntimeError("无法初始化Word应用")
        
        try:
            self.document = self.word_app.Documents.Open(str(file_path.absolute()))
            print(f"[OK] 文档已打开: {file_path}")
            return self.document
        except Exception as e:
            print(f"[ERROR] 打开文档失败: {e}")
            raise
    
    def add_paragraph(self, 
                      document: Any, 
                      text: str, 
                      format_dict: Optional[Dict[str, Any]] = None,
                      style: Optional[str] = None) -> Any:
        """
        添加段落并设置格式
        
        Args:
            document: Word文档对象
            text: 段落文本
            format_dict: 格式设置字典
            style: 段落样式名称（如"标题 1"、"正文"）
            
        Returns:
            段落对象
        """
        try:
            # 添加段落
            para = document.Content.Paragraphs.Add()
            para.Range.Text = text + '\r'  # Word需要\r作为段落结束
            
            # 应用样式
            if style:
                try:
                    para.Style = document.Styles(style)
                except:
                    print(f"[WARN] 样式'{style}'不存在，使用默认样式")
            
            # 应用格式
            if format_dict:
                self._apply_format(para, format_dict)
            
            return para
            
        except Exception as e:
            print(f"[ERROR] 添加段落失败: {e}")
            raise
    
    def _apply_format(self, para: Any, format_dict: Dict[str, Any]):
        """应用格式设置到段落"""
        try:
            rng = para.Range
            font = rng.Font
            pf = para.ParagraphFormat
            
            # 字体设置
            if 'font_name' in format_dict:
                font.Name = format_dict['font_name']
            
            if 'font_name_far_east' in format_dict:
                font.NameFarEast = format_dict['font_name_far_east']
            
            if 'font_size' in format_dict:
                font.Size = format_dict['font_size']
            
            # 字形设置
            if 'bold' in format_dict:
                font.Bold = format_dict['bold']
            
            if 'italic' in format_dict:
                font.Italic = format_dict['italic']
            
            if 'underline' in format_dict:
                if format_dict['underline']:
                    font.Underline = 1  # wdUnderlineSingle
                else:
                    font.Underline = 0  # wdUnderlineNone
            
            # 颜色设置
            if 'color' in format_dict and format_dict['color']:
                font.Color = format_dict['color']
            
            # 对齐方式
            if 'alignment' in format_dict:
                alignment_map = {
                    'left': 0,      # wdAlignParagraphLeft
                    'center': 1,    # wdAlignParagraphCenter
                    'right': 2,     # wdAlignParagraphRight
                    'justify': 3    # wdAlignParagraphJustify
                }
                pf.Alignment = alignment_map.get(format_dict['alignment'], 0)
            
            # 行距设置
            if 'line_spacing' in format_dict:
                pf.LineSpacing = format_dict['line_spacing']
            
            if 'line_spacing_rule' in format_dict:
                line_spacing_map = {
                    'single': 0,
                    '1.5': 1,
                    'double': 2,
                    'at_least': 3,
                    'exactly': 4,
                    'multiple': 5
                }
                pf.LineSpacingRule = line_spacing_map.get(format_dict['line_spacing_rule'], 0)
            
            # 段前段后间距
            if 'space_before' in format_dict:
                pf.SpaceBefore = format_dict['space_before']
            
            if 'space_after' in format_dict:
                pf.SpaceAfter = format_dict['space_after']
            
            # 缩进设置
            if 'first_line_indent' in format_dict:
                pf.FirstLineIndent = format_dict['first_line_indent']
            
            if 'left_indent' in format_dict:
                pf.LeftIndent = format_dict['left_indent']
            
            if 'right_indent' in format_dict:
                pf.RightIndent = format_dict['right_indent']
            
            # 大纲级别
            if 'outline_level' in format_dict:
                para.OutlineLevel = format_dict['outline_level']
                
        except Exception as e:
            print(f"[WARN] 应用格式时出错: {e}")
    
    def add_heading(self, 
                    document: Any, 
                    text: str, 
                    level: int = 1,
                    format_dict: Optional[Dict[str, Any]] = None) -> Any:
        """
        添加标题
        
        Args:
            document: Word文档对象
            text: 标题文本
            level: 标题级别（1-9）
            format_dict: 额外格式设置
            
        Returns:
            段落对象
        """
        default_format = {
            'font_name': '黑体',
            'font_size': {1: 16, 2: 14, 3: 12}.get(level, 12),
            'bold': True,
            'outline_level': level
        }
        
        if format_dict:
            default_format.update(format_dict)
        
        return self.add_paragraph(document, text, default_format)
    
    def add_table(self, 
                  document: Any, 
                  rows: int, 
                  cols: int,
                  data: Optional[List[List[str]]] = None) -> Any:
        """
        添加表格
        
        Args:
            document: Word文档对象
            rows: 行数
            cols: 列数
            data: 表格数据（二维列表）
            
        Returns:
            表格对象
        """
        try:
            # 在文档末尾添加表格
            rng = document.Content
            rng.Collapse(Direction=0)  # wdCollapseEnd
            
            table = document.Tables.Add(rng, rows, cols)
            
            # 填充数据
            if data:
                for i, row_data in enumerate(data[:rows], 1):
                    for j, cell_text in enumerate(row_data[:cols], 1):
                        try:
                            table.Cell(i, j).Range.Text = str(cell_text)
                        except:
                            pass
            
            print(f"[OK] 表格添加成功: {rows}行 x {cols}列")
            return table
            
        except Exception as e:
            print(f"[ERROR] 添加表格失败: {e}")
            raise
    
    def insert_page_break(self, document: Any):
        """插入分页符"""
        try:
            rng = document.Content
            rng.Collapse(Direction=0)
            rng.InsertBreak(Type=7)  # wdPageBreak
        except Exception as e:
            print(f"[WARN] 插入分页符失败: {e}")
    
    def save_document(self, 
                      document: Any, 
                      file_path: str,
                      file_format: Optional[str] = None):
        """
        保存文档
        
        Args:
            document: Word文档对象
            file_path: 保存路径
            file_format: 文件格式（'docx'或'doc'），默认根据扩展名判断
        """
        try:
            file_path = Path(file_path)
            file_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 确定文件格式
            if file_format is None:
                ext = file_path.suffix.lower()
                if ext == '.doc':
                    file_format = 'doc'
                else:
                    file_format = 'docx'
            
            # 格式常量
            format_constants = {
                'docx': 16,   # wdFormatDocumentDefault
                'doc': 0,     # wdFormatDocument
                'pdf': 17,    # wdFormatPDF
                'rtf': 6      # wdFormatRTF
            }
            
            save_format = format_constants.get(file_format, 16)
            
            document.SaveAs2(
                FileName=str(file_path.absolute()),
                FileFormat=save_format
            )
            
            print(f"[OK] 文档已保存: {file_path}")
            
        except Exception as e:
            print(f"[ERROR] 保存文档失败: {e}")
            raise
    
    def write_from_json(self, json_path: str, output_docx: str):
        """
        从JSON文件恢复Word文档
        
        Args:
            json_path: JSON文件路径（由WordReader导出）
            output_docx: 输出Word文档路径
        """
        with open(json_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        doc = self.create_document()
        
        # 写入段落
        for para_info in data.get('paragraphs', []):
            text = para_info.get('text', '')
            fmt = para_info.get('format', {})
            pf = para_info.get('paragraph_format', {})
            
            # 合并格式
            format_dict = {**fmt, **pf}
            
            self.add_paragraph(doc, text, format_dict)
        
        # 写入表格
        for table_info in data.get('tables', []):
            rows = table_info.get('rows', 0)
            cols = table_info.get('columns', 0)
            content = table_info.get('content', [])
            
            if rows > 0 and cols > 0:
                self.add_table(doc, rows, cols, content)
        
        self.save_document(doc, output_docx)
        print(f"[OK] 已从JSON恢复文档: {output_docx}")


def test_write():
    """测试写入功能"""
    import tempfile
    
    print("=" * 60)
    print("Word写入功能测试")
    print("=" * 60)
    
    try:
        writer = WordWriter(visible=False)
        doc = writer.create_document()
        
        # 测试1: 添加标题
        print("\n[测试1] 添加标题...")
        writer.add_heading(doc, "一、测试文档标题", level=1)
        writer.add_heading(doc, "1.1 子标题", level=2)
        
        # 测试2: 添加正文
        print("[测试2] 添加正文段落...")
        writer.add_paragraph(doc, "这是第一段正文，使用宋体12号字。", {
            'font_name': '宋体',
            'font_size': 12,
            'alignment': 'justify'
        })
        
        writer.add_paragraph(doc, "这是第二段正文，使用楷体，左对齐。", {
            'font_name': '楷体',
            'font_size': 12,
            'alignment': 'left'
        })
        
        # 测试3: 添加格式文本
        print("[测试3] 添加格式文本...")
        writer.add_paragraph(doc, "这是粗体文字", {
            'font_name': '宋体',
            'font_size': 12,
            'bold': True
        })
        
        writer.add_paragraph(doc, "这是斜体文字", {
            'font_name': '宋体',
            'font_size': 12,
            'italic': True
        })
        
        writer.add_paragraph(doc, "这是居中文字", {
            'font_name': '宋体',
            'font_size': 12,
            'alignment': 'center'
        })
        
        # 测试4: 添加表格
        print("[测试4] 添加表格...")
        table_data = [
            ["姓名", "年龄", "职业"],
            ["张三", "28", "工程师"],
            ["李四", "32", "教师"],
            ["王五", "25", "医生"]
        ]
        writer.add_table(doc, 4, 3, table_data)
        
        # 测试5: 添加更多段落
        print("[测试5] 添加更多内容...")
        writer.add_paragraph(doc, "", {})
        writer.add_paragraph(doc, "以上表格展示了测试数据。", {
            'font_name': '宋体',
            'font_size': 12
        })
        
        # 保存文档
        temp_file = Path(tempfile.gettempdir()) / "word_vba_write_test.docx"
        writer.save_document(doc, str(temp_file))
        writer.close()
        
        print(f"\n[OK] 测试文档已保存: {temp_file}")
        
        # 验证文档是否存在
        if temp_file.exists():
            size = temp_file.stat().st_size
            print(f"[OK] 文件大小: {size} 字节")
            print("\n" + "=" * 60)
            print("所有写入测试通过！")
            print("=" * 60)
            
            # 清理
            temp_file.unlink(missing_ok=True)
            return True
        else:
            print("[ERROR] 文件未创建")
            return False
            
    except Exception as e:
        print(f"\n[ERROR] 测试失败: {e}")
        import traceback
        traceback.print_exc()
        return False


if __name__ == "__main__":
    test_write()


path: __init__.py
content: """
Word VBA Skill - Word文档处理工具包

基于Microsoft Word VBA/ActiveX的Python接口，提供完整的Word文档读写功能。

主要模块:
    - word_vba_reader: 文档读取，提取文本和格式
    - word_vba_writer: 文档写入，创建和格式化
    - word_vba_utils: 高级功能（合并、模板、批量处理）

使用示例:
    >>> from word_vba import WordReader, WordWriter
    >>> 
    >>> # 读取文档
    >>> reader = WordReader()
    >>> content = reader.read_document("document.docx")
    >>> 
    >>> # 创建文档
    >>> writer = WordWriter()
    >>> doc = writer.create_document()
    >>> writer.add_paragraph(doc, "Hello World", {'font_name': '宋体'})
    >>> writer.save_document(doc, "output.docx")

要求:
    - Windows操作系统
    - Microsoft Word 2010或更高版本
    - pywin32库

作者: SuperMike
版本: 2.0
日期: 2026-03-04
"""

__version__ = "2.0"
__author__ = "SuperMike"
__date__ = "2026-03-04"

# 导入主要类
from .word_vba_reader import WordReader
from .word_vba_writer import WordWriter
from .word_vba_utils import WordVBAUtils

# 定义公开接口
__all__ = [
    'WordReader',
    'WordWriter',
    'WordVBAUtils',
]


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)