#!/usr/bin/env python3 """ fix-richtext-rpr-order.py — 把 openpyxl CellRichText 写出的 子元素顺序 改成 OOXML 合规顺序 (rFont -> sz -> color)。 ⚠️⚠️ 重要警告(2026-06-18 世茂实证): 本脚本只修 rPr 顺序,但实测 **修了顺序 Microsoft Excel 仍报"需要修复"**。 openpyxl 生成的富文本表在 Excel 严格校验下还有其他不合规处(去掉富文本只留 纯文本都仍报错)。所以本脚本 **不保证解决 Excel 报错**,仅作"试过什么"的记录。 正解:不要用 CellRichText 做局部标红/加粗,改用纯文本前缀(【需客户核实】…)。 详见 ../references/openpyxl-excel-richtext-pitfall.md 用法: python fix-richtext-rpr-order.py <文件.xlsx> # 原地修复 python fix-richtext-rpr-order.py <文件.xlsx> --check # 只检查,不改 python fix-richtext-rpr-order.py <文件.xlsx> --sheet sheet2.xml # 指定 sheet """ import sys, re, zipfile, os def analyze(xlsx, sheet_name="xl/worksheets/sheet1.xml"): z = zipfile.ZipFile(xlsx) if sheet_name not in z.namelist(): sheets = [n for n in z.namelist() if re.match(r"xl/worksheets/sheet\d+\.xml$", n)] print(f"指定 sheet 不存在;可用:{sheets}") return None, None xml = z.read(sheet_name).decode("utf-8") rprs = re.findall(r"(.*?)", xml, flags=re.S) bad = 0 for inner in rprs: p_sz = inner.find(" p_color: bad += 1 # color 在 sz 之前 = 错误顺序 return xml, (len(rprs), bad) def fix(xlsx, sheet_name="xl/worksheets/sheet1.xml"): xml, stats = analyze(xlsx, sheet_name) if xml is None: return total, bad = stats print(f"rPr 总数 {total},错误顺序 {bad}") if bad == 0: print("无需修复(顺序已合规)。注意:顺序合规 ≠ Excel 不报错,见脚本顶部警告。") return def _fix(m): inner = m.group(1) rf = re.search(r"", inner) cs = re.search(r"", inner) fam = re.search(r"", inner) b = re.search(r"", inner) i = re.search(r"", inner) sz = re.search(r"", inner) co = re.search(r"", inner) # OOXML 顺序: rFont, charset, family, b, i, ..., sz, color parts = [x.group(0) for x in (rf, cs, fam, b, i, sz, co) if x] return "" + "".join(parts) + "" fixed = re.sub(r"(.*?)", _fix, xml, flags=re.S) tmp = xlsx + ".tmp" with zipfile.ZipFile(xlsx, "r") as zin, zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as zo: for it in zin.namelist(): zo.writestr(it, fixed.encode("utf-8") if it == sheet_name else zin.read(it)) os.replace(tmp, xlsx) print(f"已修复并写回 {xlsx}") print("⚠️ 仍需用 Microsoft Excel 实际打开验证——本脚本不保证消除 Excel 报错。") if __name__ == "__main__": if len(sys.argv) < 2: print(__doc__) sys.exit(1) path = sys.argv[1] sheet = "xl/worksheets/sheet1.xml" if "--sheet" in sys.argv: sheet = "xl/worksheets/" + sys.argv[sys.argv.index("--sheet") + 1] if "--check" in sys.argv: _, stats = analyze(path, sheet) if stats: print(f"rPr 总数 {stats[0]},错误顺序 {stats[1]}") else: fix(path, sheet)