--- 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 ``` 内容保存到 `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 新文本 被删文本 ``` **注意事项**: - 替换整个 `` 元素,不要在 run 内部插入修订标签 - 保留原始 `` 格式块 - 新内容中的引号用 XML 实体:`“`(")`”`(")`’`(') - 删除整段时需在 `` 中也加 ``,否则接受修订后留空段 --- ## 二、PDF 文档(pdf) ### 2.1 读取 ```bash # 使用 pypdf 读取并保存到文件(避免终端中文乱码) python -c " from pypdf import PdfReader r = PdfReader('') 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 上下标字符(₀₁₂ ⁰¹²),内置字体不支持会渲染成黑块。用 `` `` 标签代替。 ### 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 # 列出所有sheets python .opencode/skills/file-reader/scripts/file_reader.py # 读指定sheet(默认前20行) python .opencode/skills/file-reader/scripts/file_reader.py # 指定行数 ``` **中文编码原理**:直接读取 xlsx 内部 XML(sharedStrings.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 终端乱码时仍可正确查看。