Files
skills/file-reader/SKILL.md
T
hmo 04db423416 Initial commit: skills library
- 70 skills with code and documentation
- Add .gitignore (ignore __pycache__, output/, temp/, venv/)
- Clean up test intermediates and caches
2026-04-26 19:27:40 +08:00

438 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: file-reader
description: 文件读写编辑技能。读取、创建、编辑 docx/xlsx/pdf/pptx 等文件,特别处理中文编码问题。
---
# 文件读写编辑技能
## 概述
支持对 Word、Excel、PDF、PPT 四类办公文档进行**读取、创建、编辑**操作。
特别针对 WPS/Office 创建的中文文档编码问题提供解决方案。
## 适用场景
- 读取 `.docx` / `.xlsx` / `.pdf` / `.pptx` 文件内容
- 创建新的办公文档(报告、表格、演示文稿、PDF)
- 编辑已有文档(修改内容、格式、公式等)
- 中文编码乱码问题处理
- PDF 合并/拆分/OCR/加水印
- Excel 公式模型构建
---
## 一、Word 文档(docx
### 1.1 读取
```bash
# 使用内置脚本(自动处理中文编码)
python .opencode/skills/file-reader/scripts/file_reader.py <file.docx>
```
内容保存到 `temp/docx_output.txt`,确保中文在 Windows 终端也能正确查看。
**中文编码原理**WPS 创建的 docx 内部 XML 可能是 GBK 编码但声明为 UTF-8,直接用 python-docx 会乱码。脚本直接从 zip 包读取 XML 并正确处理编码。
### 1.2 创建新文档
使用 `docx-js`Node.js)生成,安装:`npm install -g docx`
```javascript
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
ImageRun, Header, Footer, AlignmentType, HeadingLevel, BorderStyle,
WidthType, ShadingType, PageNumber, PageBreak, LevelFormat,
TableOfContents, PageOrientation } = require('docx');
const fs = require('fs');
const doc = new Document({
styles: {
default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt
paragraphStyles: [
{ id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal",
quickFormat: true,
run: { size: 32, bold: true, font: "Arial" },
paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } },
]
},
sections: [{
properties: {
page: {
size: { width: 12240, height: 15840 }, // US Letter (DXA单位, 1440=1英寸)
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }
}
},
children: [/* 内容 */]
}]
});
Packer.toBuffer(doc).then(buf => fs.writeFileSync("output.docx", buf));
```
**关键规则**
- 页面尺寸必须显式设置(默认 A4,US Letter 需指定 12240x15840
- 横向:传纵向尺寸 + `orientation: PageOrientation.LANDSCAPE`(内部自动交换)
- 禁止用 `\n` 换行,用多个 `Paragraph`
- 禁止用 Unicode 项目符号,用 `LevelFormat.BULLET` + numbering config
- `PageBreak` 必须放在 `Paragraph`
- `ImageRun` 必须指定 `type`png/jpg 等)
- 表格必须同时设置 `columnWidths` 和每个 cell 的 `width`,且用 `WidthType.DXA`(百分比在 Google Docs 会坏)
- 表格着色用 `ShadingType.CLEAR`(不是 SOLID,否则全黑)
- 目录 TOC 要求标题用 `HeadingLevel`,且样式中包含 `outlineLevel`
### 1.3 编辑已有文档
采用 XML 解包→编辑→重打包 三步法:
```bash
# 步骤1:解包
python scripts/office/unpack.py document.docx unpacked/
# 步骤2:编辑 unpacked/word/ 下的 XML 文件
# 直接用 Edit 工具做字符串替换,不要写 Python 脚本
# 步骤3:重打包
python scripts/office/pack.py unpacked/ output.docx --original document.docx
```
**修订标记(Tracked Changes**
```xml
<!-- 插入 -->
<w:ins w:id="1" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:t>新文本</w:t></w:r>
</w:ins>
<!-- 删除(注意用 delText 不是 t) -->
<w:del w:id="2" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:delText>被删文本</w:delText></w:r>
</w:del>
```
**注意事项**
- 替换整个 `<w:r>` 元素,不要在 run 内部插入修订标签
- 保留原始 `<w:rPr>` 格式块
- 新内容中的引号用 XML 实体:`&#x201C;`"`&#x201D;`"`&#x2019;`'
- 删除整段时需在 `<w:pPr><w:rPr>` 中也加 `<w:del/>`,否则接受修订后留空段
---
## 二、PDF 文档(pdf
### 2.1 读取
```bash
# 使用 pypdf 读取并保存到文件(避免终端中文乱码)
python -c "
from pypdf import PdfReader
r = PdfReader('<file.pdf>')
with open('temp/pdf_output.txt', 'w', encoding='utf-8') as f:
for i, p in enumerate(r.pages):
f.write(f'--- Page {i+1} ---\n')
f.write(p.extract_text() + '\n\n')
"
```
**提取表格**pdfplumber 更强):
```python
import pdfplumber
with pdfplumber.open("doc.pdf") as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
for row in table:
print(row)
```
### 2.2 合并与拆分
```python
from pypdf import PdfReader, PdfWriter
# 合并多个 PDF
writer = PdfWriter()
for f in ["doc1.pdf", "doc2.pdf"]:
for page in PdfReader(f).pages:
writer.add_page(page)
with open("merged.pdf", "wb") as out:
writer.write(out)
# 拆分为单页
reader = PdfReader("input.pdf")
for i, page in enumerate(reader.pages):
w = PdfWriter()
w.add_page(page)
with open(f"page_{i+1}.pdf", "wb") as out:
w.write(out)
```
### 2.3 创建新 PDF
使用 `reportlab`
```python
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
doc = SimpleDocTemplate("report.pdf", pagesize=letter)
styles = getSampleStyleSheet()
story = [
Paragraph("报告标题", styles['Title']),
Spacer(1, 12),
Paragraph("正文内容..." * 20, styles['Normal']),
PageBreak(),
Paragraph("第二页", styles['Heading1']),
]
doc.build(story)
```
**陷阱**:禁止在 reportlab 中使用 Unicode 上下标字符(₀₁₂ ⁰¹²),内置字体不支持会渲染成黑块。用 `<sub>` `<super>` 标签代替。
### 2.4 OCR 扫描件
```python
# 依赖:pip install pytesseract pdf2image
import pytesseract
from pdf2image import convert_from_path
images = convert_from_path('scanned.pdf')
for i, img in enumerate(images):
text = pytesseract.image_to_string(img, lang='chi_sim') # 中文用 chi_sim
print(f"Page {i+1}:\n{text}\n")
```
### 2.5 其他操作
```python
from pypdf import PdfReader, PdfWriter
# 旋转页面
page = PdfReader("input.pdf").pages[0]
page.rotate(90)
# 添加水印
watermark = PdfReader("watermark.pdf").pages[0]
for page in PdfReader("doc.pdf").pages:
page.merge_page(watermark)
# 加密
writer.encrypt("user_password", "owner_password")
```
**命令行工具**
```bash
pdftotext -layout input.pdf output.txt # 提取文本(保留布局)
qpdf --empty --pages f1.pdf f2.pdf -- out.pdf # 合并
pdfimages -j input.pdf prefix # 提取图片
```
---
## 三、PPT 演示文稿(pptx
### 3.1 读取
```bash
# 文本提取
python -m markitdown presentation.pptx
# 缩略图预览
python scripts/thumbnail.py presentation.pptx
# 原始 XML
python scripts/office/unpack.py presentation.pptx unpacked/
```
### 3.2 创建新演示文稿
使用 `pptxgenjs`Node.js),安装:`npm install -g pptxgenjs`
无模板时从头创建,有模板时用解包→编辑→打包流程。
### 3.3 编辑已有文件
```bash
# 1. 分析模板
python scripts/thumbnail.py presentation.pptx
# 2. 解包→编辑→打包
python scripts/office/unpack.py presentation.pptx unpacked/
# 编辑 XML...
python scripts/office/pack.py unpacked/ output.pptx --original presentation.pptx
```
### 3.4 设计规范
**配色**:不要默认蓝色,根据主题选择配色方案。深色背景用于首尾页,浅色用于内容页。
**字体搭配**(标题/正文):Georgia+Calibri、Arial Black+Arial、Cambria+Calibri
**字号规范**
| 元素 | 字号 |
|------|------|
| 幻灯片标题 | 36-44pt 加粗 |
| 章节标题 | 20-24pt 加粗 |
| 正文 | 14-16pt |
| 注释 | 10-12pt |
**常见错误**
- 不要重复相同布局,要变换排版
- 正文左对齐,仅标题居中
- 每页必须有视觉元素(图、图标、图表),拒绝纯文字幻灯片
- 禁止标题下划线(AI 生成痕迹明显)
- 设置 text box `margin: 0` 或偏移以对齐装饰线与文字边缘
### 3.5 QA 检查(必须执行)
```bash
# 内容检查
python -m markitdown output.pptx
# 视觉检查:转图片后逐页审查
python scripts/office/soffice.py --headless --convert-to pdf output.pptx
pdftoppm -jpeg -r 150 output.pdf slide
# 检查残留占位符
python -m markitdown output.pptx | grep -iE "xxxx|lorem|ipsum"
```
**检查清单**:元素重叠、文字溢出、间距不均、低对比度、占位符残留。
**至少完成一轮 修复→验证 循环才能交付。**
---
## 四、Excel 电子表格(xlsx
### 4.1 读取
```bash
# 使用内置脚本(自动处理中文编码)
python .opencode/skills/file-reader/scripts/file_reader.py <file.xlsx> # 列出所有sheets
python .opencode/skills/file-reader/scripts/file_reader.py <file.xlsx> <sheet_idx> # 读指定sheet(默认前20行)
python .opencode/skills/file-reader/scripts/file_reader.py <file.xlsx> <sheet_idx> <max_rows> # 指定行数
```
**中文编码原理**:直接读取 xlsx 内部 XMLsharedStrings.xml),使用 UTF-8 解码,绕过 openpyxl 可能的编码问题。
**pandas 分析**
```python
import pandas as pd
df = pd.read_excel('file.xlsx') # 默认第一个sheet
all_sheets = pd.read_excel('file.xlsx', sheet_name=None) # 所有sheet
df.describe() # 统计摘要
```
### 4.2 创建与编辑
```python
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font, PatternFill, Alignment
# 创建新文件
wb = Workbook()
ws = wb.active
ws['A1'] = '标题'
ws['A1'].font = Font(bold=True, size=14)
ws.column_dimensions['A'].width = 20
# 编辑已有文件
wb = load_workbook('existing.xlsx')
ws = wb['Sheet1']
ws['B2'] = '=SUM(B3:B10)'
wb.save('output.xlsx')
```
### 4.3 公式规范(铁律)
> **必须用 Excel 公式,禁止在 Python 中算好再硬编码到单元格!**
```python
# ❌ 错误:Python 计算后写死值
total = df['Sales'].sum()
sheet['B10'] = total # 写死 5000
# ✅ 正确:写 Excel 公式
sheet['B10'] = '=SUM(B2:B9)'
sheet['C5'] = '=(C4-C2)/C2'
sheet['D20'] = '=AVERAGE(D2:D19)'
```
公式写入后必须重新计算:
```bash
python scripts/recalc.py output.xlsx
```
脚本返回 JSON,如果 `status``errors_found`,根据 `error_summary` 修复后重新计算。
### 4.4 财务模型规范
**颜色编码**
- 蓝色文字 (0,0,255):硬编码输入值
- 黑色文字 (0,0,0):公式和计算
- 绿色文字 (0,128,0):跨 sheet 引用
- 红色文字 (255,0,0):外部文件链接
- 黄色背景 (255,255,0):关键假设
**数字格式**
- 年份格式化为文本(避免 2024 变成 2,024
- 货币用 `$#,##0`,标题注明单位
- 零值显示为 `-`
- 负数用括号 `(123)` 不用减号
- 百分比默认 `0.0%`
### 4.5 注意事项
- openpyxl 单元格索引从 1 开始
- `data_only=True` 读取计算值,但保存后公式会永久丢失
- pandas 读取时指定 `dtype={'id': str}` 避免类型推断问题
- 假设值放单独单元格,公式用引用,不要内嵌常量
- 跨 sheet 引用格式:`Sheet1!A1`
- 检查公式错误:`#REF!``#DIV/0!``#VALUE!``#NAME?`
---
## 五、中文编码问题总结
| 文件类型 | 问题根源 | 解决方案 |
|---------|---------|---------|
| xlsx | WPS 内部 XML 可能是 GBK 但声明 UTF-8 | 直接读 zip 内 XML,正确解码 |
| docx | 同上 | 直接读 word/document.xml,尝试 UTF-8/GBK |
| pdf | 提取的中文在 Windows 终端乱码 | 保存到 UTF-8 文件再读取 |
| pptx | 类似 docx | 用 markitdown 或解包 XML |
---
## 六、依赖安装
```bash
# Python 核心(读取)
pip install python-docx openpyxl pypdf pdfplumber
# PDF 创建与 OCR
pip install reportlab pytesseract pdf2image
# PPT 文本提取
pip install "markitdown[pptx]" Pillow
# Node.js(文档创建)
npm install -g docx # Word 创建
npm install -g pptxgenjs # PPT 创建
# 系统工具(可选)
# LibreOffice - PDF转换、公式重算
# Poppler (pdftoppm/pdftotext/pdfimages) - PDF处理
# Tesseract - OCR
```
## 七、输出文件位置
| 文件类型 | 默认输出路径 |
|---------|------------|
| xlsx 读取 | `temp/xlsx_output.txt` |
| docx 读取 | `temp/docx_output.txt` |
| pdf 读取 | `temp/pdf_output.txt` |
保存到文件是为了确保中文在 Windows 终端乱码时仍可正确查看。