把一批中文文档拖进去,选一个操作,逐个文件给结果和产出路径 —— 规范化标点引号单位、 格式互转、按标题拆分、多份合并。原生 macOS app,全部在你自己的机器上跑,文件不出本机。 本页下半部分是它的只读那一半:把 .docx 拖进浏览器,当场出结构树、统计和终稿体检报告 —— 同样不上传,解析在你这个标签页里完成。
做中文正式文档的人,手上很少只有一个文件。一份报告常常是:十几篇 Markdown 草稿、
几个别人发来的 .doc、一叠导出的 Excel、还有几份 PPT。要交出去之前,
这堆东西得先被摆平三件事:
直引号和弯引号并存,半角逗号句号混进中文句子, 「平方米」「立方米」「小时」有的写汉字有的写符号。一份份开着 Word 查找替换, 改到第八份就会漏,而且替换一不留神会把 URL 和代码里的半角符号一起改坏。
要把 PPT 的内容抽成 Markdown 归档,
要把写好的 Markdown 变成有正经标题样式的 Word,要把老掉牙的 .doc / .xls
升级成新格式,还要在 Excel / CSV / 纯文本之间来回倒。每一条都得找一个不同的工具。
一篇长 Markdown 要按章节拆成独立文件分头写, 写完又要按顺序合回一篇;一个 Excel 的十几个 sheet 要拆成十几个独立表分发出去。
DocTools 把这三件事收进同一个窗口:拖一批文件进来 → 左边选操作 → 回车跑。 每个文件单独一行结果,成功的给产出路径,可以直接在 Finder 里定位;失败的告诉你为什么跳过, 不会因为其中一个文件不对就整批停住。
下面 9 个操作是 app 启动时从后端读取并渲染成侧栏的全部内容 —— 不多不少。
| 操作 | 它做什么 | 吃什么格式 | 产出 |
|---|---|---|---|
| 规范化 | 中文文本修复三件套一起做:引号统一成中文弯引号、英文标点转中文、 中文单位名转标准符号 | docx · md · pptx | docx 产 _fixed 副本,原件不动 |
| 引号统一 | 只动引号,不碰标点和单位 —— 当你只想修引号、其他一个字都别改的时候用它 | docx · md | 同上 |
| 字体统一 | 把 PPT 全篇字体统一,含母版和版式(只改正文页的工具会漏掉这两处, 导致新建页又变回去) | pptx | 原地覆写留 .backup |
| 英文小写整理 | 把表格数据行 / 正文里的英文统一转小写。这是语义级的数据改写, 所以刻意独立成一个操作,不塞进“规范化”里连带执行 | xlsx · xlsm · docx | 产出副本 |
| 清页眉页脚 | 删掉 Word 的页眉页脚引用,正文一个字不改 | docx | _fixed 副本 |
| 格式转换 | 源格式自动认,你只选目标格式。完整矩阵见下一节 | docx · doc · pptx · ppt · md · csv · txt · xls · xlsx | 同目录同名新文件 |
| 拆分 | Markdown 按标题拆成一章一个文件;Excel 按 sheet 拆成一表一个文件 | md · xlsx · xlsm | <文件名>_split/ 目录 |
| 合并 | 多个 Markdown 合成一篇;多个 txt 按列合成一个 CSV | md · txt | 单个合并文件 |
| 预览 | Markdown 渲染成带样式的 HTML,用浏览器打开看 | md | 浏览器窗口 |
只有下表这些组合有对应引擎。选了没有引擎的组合,它会明确告诉你“这条转换没引擎,跳过”, 而不是产出一个坏文件。
| 从 | 到 | 怎么做的 |
|---|---|---|
| pptx / ppt | Markdown | 逐页抽文字与结构 |
| docx | Markdown | 调用 markitdown(临时环境,不用你预装) |
| Markdown | Word | 套模板生成,标题层级映射到 Word 样式,生成后自动再跑一遍文本修复 |
| docx | Word | 套模板重排(拿模板的样式重刷一遍版式) |
| csv / txt / xls | Excel | 表格引擎 |
| xlsx / csv | CSV / txt | 表格引擎 |
| 老 .doc | txt | 系统自带 textutil 直转 |
| 老 .doc | Word / Markdown | 先用 LibreOffice 升级成 docx(没装则 textutil 兜底),再走 docx 那条路 |
规范化不是一个黑盒开关。它把“修哪些内容”和“改哪些范围”拆成两组独立选项, 由后端声明、前端渲染 —— 也就是说加旋钮只改后端脚本,不用重新编译 app。
| 组 | 选项 | 说明 |
|---|---|---|
| 修哪些内容 | 引号统一为中文引号 | 直引号、日文角引号一律归成中文弯引号,并按出现顺序配对开合 |
| 英文标点转中文 | 含 , : ; ! ? ( ) 七种 | |
| 中文单位转标准符号 | 如 平方米 → m²,只在紧跟数字时才换 | |
| 引号设为宋体 | 会把引号拆成独立的文字片段单独设字体 | |
| 改哪些范围 | 正文 | Word 文档里文字藏在六个不同的地方。多数工具只处理正文, 于是改完打开一看,表格里、批注里、修订痕迹里的旧标点还在。这里六处可以逐个开关。 |
| 表格 | ||
| 审阅修订 | ||
| 批注 | ||
| 脚注 / 尾注 | ||
| 页眉页脚 |
下面这个工具不是示意图:把 .docx 拖进来,浏览器会当场把它当成 ZIP 拆开
—— 用原生的 DecompressionStream('deflate-raw') 解压,配一个手写的
ZIP 中央目录读取器定位条目,取出 word/document.xml 与 word/styles.xml,
再用 DOMParser 解析出段落、样式、标题层级和表格。没有引任何第三方库,
也没有任何网络请求。.md / .txt 走纯文本路径,同样跑完整体检。
[Content_Types].xml、
word/document.xml、word/styles.xml、一张真图片),
用 CompressionStream('deflate-raw') 压缩,再原路走一遍上面那个 ZIP 读取器。
所以它同时也是这条解析链的自测:能读出来,说明读取器是通的。
示例文里的问题(缺空格、连续空段、标题跳级、标点混用)都是故意埋的,人物是“张教练 / 李同学”,
全部为合成内容。
| 体检项 | 判据 | 为什么它值得单独查 |
|---|---|---|
| 中英文之间缺空格 | 汉字与拉丁字母直接相邻(数字不算,中文里“第3章”本来就常写在一起) | 混排不留空格会让整段的灰度看起来发糊;而它散在全文,肉眼一页页找必漏 |
| 连续空段 | 正文层出现 ≥2 个连续空段落 | 用回车顶版面的老习惯。转 PDF、换纸张、改字号之后,空段会把分页顶到奇怪的地方 |
| 标题层级跳级 | 标题序列里出现 h1 → h3 这种跨级(层级取 outlineLvl,缺了再看样式名) |
跳级的直接后果是自动生成的目录塌掉、导航面板结构错位 |
| 全角半角标点混用 | 半角 , . ; : ! ? ( ) 紧挨着汉字出现(网址、邮箱、行内代码已排除) |
一份稿子里两种标点并存是最典型的“多人合稿没统一”痕迹 |
| 中文里的直引号 | 含汉字的段落里出现 " 或 ' |
顺带查的第五项:直引号在正式稿里应该是弯引号 |
| 手打首行缩进 | 段落以空格 / 全角空格 / 制表符开头 | 顺带查的第六项:缩进该由段落样式给,手打的空格换个字号就散架 |
这个小演示把“规范化”里的纯文本规则(引号 / 标点 / 单位)用 JavaScript 照着后端那份实现重写了一遍,直接跑在你这个浏览器标签页里。改动会高亮出来。 它只是让你在下载前先看清改字的分寸 —— 真正批量改写 docx / pptx 的,是你机器上的那个 app。
uv(Python 运行器):brew install uv。DocTools.app 拖进“应用程序”。xattr -cr "/Applications/DocTools.app"
后端本身就是个可以单独调用的命令行程序,返回 JSON —— 这意味着你可以把它接进自己的脚本、 Makefile 或定时任务里,不必开 GUI。在仓库根目录:
# 看有哪些操作、每个操作支持哪些格式和选项
python3 backend/doc_gui_backend.py gui-ops
# 批量规范化,但这次不动单位、不动标点,只修引号
python3 backend/doc_gui_backend.py gui-run --op clean \
--opt rule.units=0 --opt rule.punct=0 --files 甲.docx 乙.docx
# 只对正文和表格生效,跳过批注与修订
python3 backend/doc_gui_backend.py gui-run --op clean \
--opt scope.comments=0 --opt scope.revision=0 --files 报告.docx
# Markdown 批量转 Word(套模板 + 自动文本修复)
python3 backend/doc_gui_backend.py gui-run --op convert --to word --files 第一章.md 第二章.md
还有一层更朴素的调度器接口,直接打动词,人看的彩色日志而非 JSON:
python3 backend/doc_dispatch.py convert --to md 汇报材料.pptx python3 backend/doc_dispatch.py split 长文.md python3 backend/doc_dispatch.py merge 01.md 02.md 03.md python3 backend/doc_dispatch.py clean 草稿.md python3 backend/doc_dispatch.py view 说明.md
--opt 命令行。
GUI 里跑则按全开的默认值执行。
一壳两层。SwiftUI 那一层只做界面:侧栏、拖放区、文件列表、结果卡、命令面板。 它不解析任何文档格式,也不写业务逻辑 —— 所有真正的活都交给本机的一个 Python 进程, 双方靠一份 JSON 信封契约通信:前端启动时问一次“有哪些操作”,界面完全由这份回答渲染出来; 执行时把操作名、目标格式、选项和文件列表递过去,拿回逐文件的成败与产出路径。 结果就是:加一个新操作、给某个操作加一组选项,改的是 Python 脚本, app 不用重新编译。
后端本身是纯路由。docx 的 XML 改写、pptx 的母版遍历、xlsx 的 sheet 拆分、 Markdown 的标题解析各自是独立引擎,调度器只负责按文件后缀把活分给谁 —— 所以一个引擎出问题, 炸的是那一个文件那一行,不会掀翻整批。同理,批量执行时每个文件的输出被单独捕获, 互不干扰地汇总成一张结果表。
几个不显眼但决定成败的细节:写文件前先给原件留副本或走 _fixed 旁路,
默认不覆盖你的输入;Word 文本修复直接操作文档 XML 的文字片段,
因此能覆盖正文之外的表格、批注、修订、脚注、页眉页脚;单位替换和半角转全角都带边界守卫
(见上面那个演示);GUI 程序看不到你 shell 里的 PATH,
所以它会去几个标准位置找 uv,找不到时直接说“找不到 uv”而不是静默失败。
| 步骤 | 用到什么 | 细节 |
|---|---|---|
| ① 定位中央目录 | 手写解析 | 从文件尾部往回扫 EOCD 签名(可能被注释顶开,所以要扫不是直接取末 22 字节), 读出条目数与中央目录偏移。碰上 ZIP64 会明确报错,不装作读懂了 |
| ② 逐条读目录项 | DataView | 每项 46 字节定长头 + 变长文件名,取压缩方式、压缩前后大小、本地头偏移。 大小以中央目录为准 —— 本地头在带 data descriptor 的包里可能是 0 |
| ③ 找到数据起点 | 手写解析 | 本地头是 30 字节定长 + 文件名 + 扩展域,三者相加才是真正的数据偏移, 而且扩展域长度经常与中央目录里的那份不一样,必须重读 |
| ④ 解压 | DecompressionStream('deflate-raw') |
浏览器原生。方式 0(stored)直接切片,方式 8(deflate)走流;其余方式如实报“不支持” |
| ⑤ 解析 XML | DOMParser |
按命名空间取 w:p / w:tbl / w:t;
标题层级先认 w:outlineLvl,没有再查 styles.xml 里样式名,
这样中英文版 Word 存的样式 ID 不同也能认出来 |
| ⑥ 体检与导出 | Blob + a[download] |
报告在内存里拼成 Markdown,通过 Blob 直接落到你的下载目录,全程无服务端 |
这是最常被问的一句,所以直说。上面那个体检器是只读的 —— 读进来、看一眼、给一份报告, 不改你的文件,所以放在浏览器里没有代价。批量改写是另一回事。
整批改写搬到网页上会退化。网页版能给的流程只有“上传 → 处理 → 下载”。 一次处理三五个文件还行;几十个文件时,你要先把它们传上去,等,再把产物一个个下回来, 然后自己在下载文件夹里跟原始目录做对照 —— 这比在本机上拖进去、就地产出、 点一下在 Finder 里看要难用得多,不是好一点点的差距。
而且那意味着把没公开的稿子交给一台服务器。需要批量规范化的文档, 通常正是还没定稿、还不能外传的那一批。为了省一次安装,把它们上传到别人的机器上, 这笔账不划算。本机运行则根本不存在这个问题:没有账号,没有上传,没有服务端日志。
GitHub · MIT 许可
github.com/zengtianli/doc-tools
| 下载成品 | 仓库的 Releases 页有打包好的 DocTools-<版本>-arm64.zip |
| 从源码构建 | git clone 之后 ./build.sh --install(需要 Xcode) |
| 只要后端 | backend/ 目录可以单独拿走当命令行工具用,不需要 app |
| 许可 | MIT —— 随便改,随便用 |
DocTools.app + 可单独调用的 Python 后端,
源码见 github.com/zengtianli/doc-tools。