Repository navigation
Releases: ls-open/md-to-word
Release list
2.4.2
2.4.2
修复
- 题名以
?/!/./:结尾时多补了一个句点:APA 在标题后写
<group suffix=".">,citeproc-py 不会像 citeproc-js 那样把重复的标点合并掉,
What Aspects Are Automated?于是成了Automated?.。现在终止标点后面不再补句点;
合并同样看得穿标签与右引号——APA 的斜体书名(<i>What Now?</i>)与 Chicago/MLA
的引号题名(“What Aspects Are Automated?”)后面那个句点也一并吃掉。
说明
- 题名冒号后的首词大小写由数据决定:CSL 的
sentence转换只处理第一个词的首字母,
citeproc-js 也会保留.bib里的小写。要 APA 的「冒号后大写」,把.bib写成
vehicles: A dual-path即可,Zotero 与本工具都一致。
2.4.1
2.4.1
四份 demo 在本机 Word 里渲染成 PDF 逐页核对后修的三处问题。
修复
- 文献表排序:同姓作者撞成一个排序键,顺序掉到年份上——
Li, C. (2023)会排到
Li, M. (2021)后面。现在按样式声明的键序、升降序与名字形态排:APA 收首字母
(同首字母落到年份),Chicago/MLA 比全名;MLA 同作者按题名(它没有日期键),
Elsevier Harvard 的年份是降序。判据取自 citeproc-js 对同一组条目的输出。 - 相邻两张表被 Word 并成一张:
</w:tbl><w:tbl>在 Word 里就是一张表,第二张的表头、
题注与独立编号跟着错位,而且不报错。现在两张表之间垫一个 1 磅空段落。 - 无编号的独立公式按行内样式排(分式缩小、求和上下限跑到右侧):段落里只放公式后
Word 按显示样式排。有编号的公式与制表位同段,仍是行内样式——这是 Word 的规矩,已写进
references/design.md。 - 「图片宽度超过版心」的提醒不再自称「不报错、也没有警告」。
说明
- 数字制样式(GB/T 7714、Nature 等)不声明
<sort>,仍按引用顺序,未受影响。
2.4.0 —— PDF 图、引文消歧与静默错误的修复
2.4.0 —— PDF 图、引文消歧,以及一批「退出码 0 却已经错了」的修复
这一版来自三路独立审计(引文一致性、稿件链路、文档)加一轮 CSL 官方用例对标。
图片
- 可以直接引用 PDF:
{width=13cm},导出时按声明宽度的 300 dpi 栅格化;多页取第 1 页并提醒;加密或损坏的文件在预检拦下;Word 放不下的格式(.svg/.webp/.eps)同样在预检拦下并给出改法 - 超宽图按版心钳制(此前手册承诺钳制,实际会溢出页面被裁)
- 图片替代文字写进
docPr/@descr - 渲染在临时目录进行,用完即删;同一份 PDF 每次导出字节一致
引文(CSL)
- 补全 CSL 的三条消歧:同姓不同人补首字母
(D. Zhang et al., 2024)、同首作者不同合作者补名字(Bai, Sun, et al., 2024)、分不开时加年份后缀(Kim & Park, 2020a) collapse:year与year-suffix-ranged两种折叠((Kim & Park, 2019, 2020a)、(Green 2020a–c))- 定位符:
[@a, p. 33]、[@a, 33]、[@a, chap. 3]、[@a, 3, 7]、叙述式@a [p. 33]都成立,标签按 CSL 变量传下去 - 页码区间展开(
33-5 → 33–35)、引用级<sort>、上标数字制的连续编号合并 - 姓名与字段映射:团体作者
{{WHO}}、Jr.后缀、van前缀、translator / series / note / 页数、month、学位论文 genre、arXiv eprint - 手册新增「CSL 排版差异」七条,写清引文引擎已知的排版出入
稿件契约
2*3*4、a*b*c、价格 $5 到 $10不再被误判成强调/公式;带括号的链接不再截断- 脚注:补
w:footnoteRef(此前正文有号、注文无号)、脚注里的行内格式与链接 - 有序列表各自编号(此前两个独立列表在 Word 里连续编成 3./4.);嵌套层起始号生效
- 表格:单元格内的换行与转义、行尾竖线缺失的拦截、表头重复只在声明时生效
- 新门禁:缺定义的脚注、未独立成行的图片、重复的公式标签、
%%缺空格、分节参数写错 - YAML 未知键出提醒(
toc:、geometry:这类此前静默忽略) - 公式:
cases落成可伸缩定界符m:d,aligned/split落成m:eqArr,不再产出空 run - 多行公式不再被误报「折行段落」;标题尾部闭合井号按 CommonMark 去掉
其他
- 依赖新增
pypdfium2>=4.30(PDF 图栅格化,各平台轮子) - 文档全面清理:依赖数与
--json键名已对齐实现,删掉%20、fig-prefix、「缺 --citeproc」等过时说法
验证
- 871 个测试通过;sdist 与 wheel 均通过
twine check - 干净 venv 从 PyPI 安装后跑通:消歧、年份后缀、页码、PDF 图均正常
2.3.2 — 未配对的星号不再阻断导出
修复
- 显著性星号被误判为未闭合加粗:
**的闭合检查原来只数出现次数,.181***、0.052**这类显著性标记在正文和表格里都会被拦下,整份稿件导不出来。加粗的判据只有一条——前后各一对星号;凑不成对的星号是普通字符,成品里原样输出,不再阻断。$…$与$$…$$未闭合仍然拦截。
文档
- 入门文档注明包内
guide.md与技能入口SKILL.md同文,读其一即可;--contract不再引导重读。 - 手册与技能清单同步删去「跨行加粗」这一条不支持写法。
779 个测试通过。
2.3.1
补上一版在公式上的两个缺陷。
修复
\begin{aligned}不再崩溃。 这是 AI 生成多行公式最常见的写法,转换器会把&对齐符漏成<mi>&</mi>,产出非法 XML,用户拿到的是 lxml 的 traceback。现在按split处理,落成带对齐的两行矩阵。- 花括号不配对不再静默丢内容。
$$ \frac{a}{b $$这类写法转换器不报错,只把分子分母结构丢掉,渲染成ab。新增阻断门禁[公式括号不配对],按行提示补}。 - MathML 解析失败改用一句带公式片段的报错,不再抛 lxml 的 traceback。
文档
- 手册补多行公式用
cases/matrix/split;@eq-x的前缀由引擎自动添加(写「见 @eq-x」,不要写「见式 @eq-x」)。 - README 的公式条目写明:原生 OMML 对象,可在 Word 中继续编辑,不是图片、不是字面文本。
- 报错速查补「公式少了字符或结构不对」一行。
完整变更:git log v2.3.0..v2.3.1
2.3.0
这一版把「写法」和「门禁」两处长期含混的地方定清,并删掉了一批没有实际意义的兼容。
新增写法:段内换行
同一段落内需要换行(地址、诗句、长标题)时,在行尾写一个 \ 或两个空格,写出为 <w:br/>:
地址:北京市海淀区\
某路 1 号\
邮编 100000
不写标记时每一行仍是独立段落——短段落序列是正常写法,各自可以挂 %% 样式。
门禁
- 三类写法从「原样印出」改为阻断:
==高亮==、[文字]{.类名}(.underline除外)、\(…\)行内公式。 - 折行段落仍为提醒,但同类合并成一条并列出全部行号(最多 8 个),不再一稿刷几十行。
- 提醒现在进入
--json的messages,程序不必解析 stderr。 - 成功导出末尾的提醒收成一句:「导出完成。版面效果仍须在 Word 中人工核验。」
一处行为变更
不带 -r 时只认稿件同目录的 format.txt,不再猜「旁边那个唯一的 .txt」。旁边另有 .txt 时输出一行提醒点名;原「多个 .txt 无法判定」的退出码 1 取消。
清理
template/generator.py自带的 35 条样式表删掉,改从spec/constants.py引入;反查中文名不再依赖字典插入顺序。- 反推要求文件的逻辑原本写了两次(
main()与extract_spec_from_docx()),合并为一份并加测试钉住。 config.get_csl_dir()与word_pdf._render_script()中无调用方的分支删除。
文档
技能与手册只保留 agent 需要的内容:删除安装、运行时、源码检出、「requirements.txt 不是要求文件」等对人类读者的说明。references/design.md 新增 §4「为什么是 AI 原生」,把这一轮的判断写下来:文档先于参数、确定性优于宽容、可机读优先、不为不存在的人工操作做优化。
完整变更:git log v2.2.1..v2.3.0
2.2.1 — Python 下限改为 3.10;修 3.9 上 import 即崩
2.2.1
Python 下限改为 3.10。
2.x 从来没有在 3.9 上真正跑通过:inspector、extractor、generator、skill_utils 四个模块的函数注解写了 Path | str(PEP 604),而 3.9 里注解是定义时求值的,一 import 就抛 TypeError: unsupported operand type(s) for |。CI 的 3.9 任务一直在红,只是没人看。
这次不选择"补上 3.9 支持",理由:
- 硬依赖
latex2mathml自 3.79 起要求>=3.10。留 3.9 意味着 pip 会退到 3.78.1——那正是公式解析的上游,测的和装的不一致。 - Python 3.9 已于 2025-10 停止维护,3.10 也已于 2026-10 停止维护。
- 承诺一条从未交付、也不再有上游修复的路径,不如把话说对。
变更
requires-python→>=3.10;分类器去掉 3.9、补 3.13。- CI 矩阵改为 ubuntu 3.10 / 3.12 与 macOS 3.12——下限本身进入测试。
- 四个模块补
from __future__ import annotations(注解延迟求值,顺带省掉 import 时的求值开销)。 - 移除专为 3.9 写的兼容守卫测试,职责交给 CI 的下限任务。
顺带修掉的
--pdf在 pip 安装下不可用(渲染脚本原先只在仓库目录里找得到)——已随 2.2.0 修好,本次一并覆盖。-t传非.docx、-o传目录、输出目录只读这三类错误不再甩 traceback。md-to-word init --preset 写错不再抛KeyError。- 退出码
2(参数用法错误)写进契约;无参数运行时打印简明帮助。
验证:729 测试通过;scripts/acceptance.sh 在干净 venv 装 wheel 跑通全部入口。
2.2.0 — 通用定位、init/--guide/--json、修 --pdf 安装后不可用
2.2.0
定位:从「学术排版工具」改为通用的 Markdown → Word 编译器(学位论文、期刊投稿、技术报告、公文、说明书同一套写法)。包描述、关键词、受众分类、README 同步。
新增
md-to-word init:在当前目录写一份完整预设的format.txt和一份能直接导出的hello.md,第一次导出不必从空白开始。md-to-word --guide:打印入门文档(流程、门禁判据、边界)。它是仓库根SKILL.md的正文,只装 pip 包的人从这里读,两份由测试保证一字不差。--json:stdout 输出一行 JSON(ok、returncode、input、output、pdf、lang、messages、errors),键名固定;进度与报错仍走 stderr。给脚本与 Agent 用,不必解析会随语种变化的措辞。- 包内
guide.md、render_via_word.sh随 wheel 发布;[project.urls]补全主页、文档、仓库、issues。
修复
--pdf在 pip 安装下不可用:渲染脚本原来只在仓库里找得到,装好的包永远按「跳过」返回 3。脚本改为包内资源,仓库scripts/那份只做转发。-t传非.docx(如 PDF)抛BadZipFiletraceback → 改为一句说明。-o传目录抛IsADirectoryErrortraceback → 改为「输出路径是一个目录」。- 输出目录只读、磁盘已满等写入失败抛
PermissionErrortraceback → 改为「写出失败」并列出常见原因。 md-to-word init --preset 写错抛KeyError→ 改为「未找到预设」。init生成的样例把正文折成两行,第一次导出就报「折行段落」→ 改为一行一段,零告警。
契约
- 退出码补上
2(参数用法错误),0/1/2/3在SKILL.md、guide.md、两份手册里与代码一致,由测试核对。 - 无参数运行时打印简明帮助(描述 + 三条示例 + 去哪读全部),不再只是 argparse 的报错。
SKILL.md新增「版本契约」:命令与开关、退出码、JSON 键名、稿件契约、要求文件词法为冻结面;消息措辞、包内布局、版本号可变。
文档
- README 中英重写:一句定位 → 产物图 → 安装 → 30 秒 → 样例 → 能力 → 常用写法 → 与其他方案的区别 → 另两个入口 → 环境 → 模板反推 → 上游 → 许可。英文版示例用英文词表,首图换成英文样例页。
- 四份样例的
.docx/.pdf/page-*.png按当前源码重出;英文样例补齐产物。 - 手册:
format.txt命名统一(requirements.txt只指依赖);获取要求文件由三种改四种。
验证:729 测试通过;scripts/acceptance.sh 在干净 venv 里装 wheel 跑通全部入口(含 --pdf);四份样例与源码逐字节一致。
v2.1.0 — 英文完整可用
PyPI:pip install md-to-word-docx(命令仍是 md-to-word)。
这一版做了什么
英文用户补齐到可用。之前的版本只有界面文案是英文的:英文稿件被门禁拦下时,报错细节是中文,提示里还让他改写成「摘要/关键词/表题」;模板必填项诊断、对账报告、公式/文献异常同样是中文。现在全部双语,语言跟着稿件语种走(不是跟着界面设置):
- 348 条消息进同一张双语表(门禁 116、对账 91、模板 54、流水线 24、模板反推 23、CLI 17、写出器 15、公式 4、文献 3、标记 1)
- 英文提示里的样式名举例给英文正典名,
Table: …/Table Header: …这类示例由测试保证真的能被解析器解析 - 使用手册出英文版
manual_en.md,md-to-word --contract --lang en指向它;两份手册由测试盯着必须覆盖同一批样式名、短代码、YAML 键与 CSL 清单
默认预设跟着稿件语种走。此前不给 -r 时一律用 zh-academic:英文稿会被排成「首行缩进两字符 + 宋体 fallback」,文档能打开、样式都在,只是每段都被顶了两个字符——我们的英文样例一直是这种状态。现在中文稿 zh-academic,其他 en-nature,并在输出里说明用了哪一份。
修的真问题
md-to-word-template在 2.0.1 上是坏的:两处from generate_template import …是旧脚本名,每次运行都以ModuleNotFoundError崩掉。已修,并补了端到端测试- 残缺公式(如
\frac{1})会抛ValueError,穿过异常处理变成 traceback;现在收成「写不出来」的正常报错 - 英文稿写
%% References Title References时,文献列表生成成功却被警告「没写文献区标题」,而提示让他补的正是那一行——死循环,已修 - 输出目录不存在时不再抛
FileNotFoundError,而是一句话说明 - 传非
.docx给md-to-word-template不再甩BadZipFile
兼容性
同一份稿件与要求文件,2.0.1 与 2.1.0 的产物逐字节相同(三份样例的 sha1 一致)——这一版只改消息语言与几个错误路径,不改排版。
697 个测试通过。
v2.0.1 — 首个 PyPI 版本
PyPI:pip install md-to-word-docx(命令仍是 md-to-word;PyPI 上 md-to-word 因与既有项目过于相似被拒)。
这一版是什么
断代式重写:写出器全部自己实现,不再依赖 Pandoc / Quarto 或任何外部二进制。同一份稿件与要求文件,任何时候重跑都产出逐字节一致的 .docx(zip 时间戳固定)。
主要能力
- 声明式排版:
requirements.txt一行一种样式,与稿件一起进 Git - 原生 OMML 公式(居中、编号靠右、按章编号)、交叉引用
@eq-x - 三线表、跨页重复表头、题注与图同页
.bib+ CSL:内置 29 份样式,自带 CSL 自动规范化- 三道质量门禁:导前预检、导后防丢数、与要求文件逐条对账
- 包内
manual.md随版本走;md-to-word --contract给出契约速查与手册路径
早期版本说明
2.0.0 的包元数据里 README 仍写着旧包名 md-to-word,文件在 PyPI 上不可覆盖,故以 2.0.1 修正;两者代码相同。