ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Markdown一键生成Word/PPT/PDF:文档自动化工作流实战

Markdown一键生成Word/PPT/PDF:文档自动化工作流实战 1. 项目概述一个被误读却极具实用价值的“文档工作流中枢”“markitdown”这个名字乍一听像极了某个小众 Markdown 编辑器或是某款带点极客味的笔记工具。但如果你真去 PyPI 搜pip install markitdown会发现它根本不存在——它不是官方发布的 Python 包也不是 GitHub 上有星标仓库的开源项目。它是一个在开发者社区、技术文档工程师、高校科研助理和自动化办公实践者之间口耳相传的工作流代号一种用 Python 脚本串联起 Markdown、Word、PowerPoint 和 PDF 四大文档生态的轻量级解决方案。它的核心诉求非常朴素让一份结构清晰的 Markdown 源文件一键生成格式规范、样式统一、公式可编辑、图表可复用的 Word 报告、PPT 汇报稿与 PDF 归档件。这不是“所见即所得”的富文本编辑而是“所写即所用”的声明式文档工程。我第一次接触这个概念是在帮一个 ROS2 机器人开发团队做技术文档自动化时。他们每周要产出三份材料给导师看的 Word 实验报告含 Mathtype 公式、给组会用的 PowerPoint 汇报幻灯需从报告中自动提取关键图表与结论、以及提交存档的 PDF 版本要求页眉页脚、章节编号、目录自动生成。手动复制粘贴不仅耗时还极易出错——比如 Word 里 Mathtype 公式一粘到 PPT 就变图片再导出 PDF 后字体嵌入失败打印出来全是方块。后来团队里一位老哥甩给我一个叫markitdown.py的脚本不到 300 行却把整个流程跑通了。它不依赖 Office COM 接口Windows 专属且不稳定也不调用 LibreOffice 命令行Linux 下常因字体缺失崩掉而是用python-docxpython-pptxweasyprint这套纯 Python 组合把 Markdown 解析成 AST抽象语法树再按预设模板规则精准映射到目标文档的结构节点上。关键词markitdown在搜索热词中反复出现恰恰说明它已从某个具体脚本名演变为一类解决“多端文档一致性”问题的方法论代称。它适合谁不是想学 Python 编程的新手而是已经会写for循环、能看懂requirements.txt、正被重复性文档工作折磨得想砸键盘的工程师、研究员、教师或内容运营者。它不教你 Python 基础但它能让你今天写的 Markdown明天就变成领导要的 Word、后天就变成客户要看的 PDF、大后天还能直接塞进 PPT 里讲——这才是真正的生产力杠杆。2. 内容整体设计与思路拆解为什么放弃“所见即所得”选择“声明式渲染”2.1 核心矛盾Office 套件的强交互性 vs 文档交付的强一致性传统文档工作流的痛点本质是工具链与交付目标的错配。Word 和 PowerPoint 是为“人机交互”设计的你拖拽表格、调整图片位置、双击公式编辑——这种自由度在单次创作中很爽但在需要批量生成、版本迭代、多人协作的场景下就成了灾难。比如一个 ROS2 项目有 12 个子模块每个模块都要生成独立报告。如果全靠人工操作哪怕每个报告只花 20 分钟12 份就是 4 小时而一旦需求变更比如新增“系统延迟测试”章节就得重开 12 个 Word 文件逐个插入新段落、更新图表、重新生成目录——这还没算上 PPT 和 PDF 的同步修改。更致命的是不同人操作习惯不同最终交付物的字体、标题层级、列表缩进、页眉页脚风格千差万别技术文档的专业感荡然无存。“markitdown”方案的底层逻辑就是把“内容”和“呈现”彻底解耦。它借鉴了 Web 开发中的 MVCModel-View-Controller思想Markdown 文件是Model数据模型定义了所有文字、标题、代码块、数学公式、图片路径等原始信息Python 脚本是Controller控制器负责解析 Model 并根据业务规则调用不同渲染引擎而最终生成的 Word、PPT、PDF 则是View视图纯粹是 Model 经过 Controller 处理后的表现形式。这种设计带来三个不可替代的优势第一可复现性——同一份 Markdown 同一版脚本无论在哪台机器上运行生成的 Word/PPT/PDF 结构完全一致第二可版本化——Markdown 是纯文本Git 可以清晰追踪每次修改比如“第3.2节补充了ROS2节点通信时序图”而.docx是二进制压缩包Git diff 几乎无意义第三可扩展性——当需要增加新输出格式比如生成 HTML 网页版报告只需新增一个 View 渲染器无需改动 Model 和 Controller。2.2 方案选型为何是 python-docx python-pptx weasyprint而非 pandoc 或 LibreOffice面对“Markdown 转多格式”需求很多人第一反应是pandoc。它确实强大支持上百种格式互转。但实际落地时pandoc在专业文档场景有硬伤它对复杂 Word 样式如多级列表编号、题注交叉引用、Mathtype 公式嵌入支持有限生成的.docx常需人工二次调整对 PowerPoint 的支持更是鸡肋只能把 Markdown 的标题转成幻灯片标题正文内容堆在一页无法实现“一张图一页幻灯”或“按章节分节”的智能布局。而 LibreOffice 命令行soffice --headless --convert-to虽能转 PDF但在 Linux 服务器上常因缺少中文字体如Noto Sans CJK SC导致中文乱码且启动 LibreOffice 进程开销大不适合高频调用。相比之下“markitdown”采用的三件套组合是经过大量实测验证的“最小可行方案”python-docx专为.docx设计能精确控制段落样式、表格边框、图片环绕方式、页眉页脚内容。最关键的是它支持插入OLE 对象——这意味着你可以把本地.mathtype文件或.emf矢量图直接嵌入 Word保留公式可双击编辑、图片可无损缩放的特性完美解决“Word 关闭很慢”的常见问题该问题常由大量 PNG 图片未压缩导致而矢量图体积小且渲染快。python-pptx虽不能直接渲染 LaTeX 公式但可通过pptxgen.js的 Python 封装或调用系统 MathType 接口将公式先转为 EMF 矢量图再插入幻灯片。更重要的是它能基于 Markdown 的!-- ppt:slide --这类自定义注释智能切分幻灯片——比如遇到## 实验结果标题自动新建一页标题为“实验结果”的幻灯并将后续内容按 引用块、- 列表项、![](path.png)等语义分别渲染为要点、图表、备注彻底告别手动排版。weasyprint这是 Linux 环境下 PDF 渲染的“隐形冠军”。它不依赖系统浏览器而是用纯 Python 实现 CSS 渲染引擎对中文支持极佳只要指定font-face加载Noto Sans CJK SC字体且能完美处理页眉页脚、目录生成、页码跳转等专业排版需求。相比wkhtmltopdf需安装 Qt 库Linux 容器中易出错或pdfkit本质是 wkhtmltopdf 封装weasyprint的错误日志更清晰调试成本更低。提示很多新手会纠结“为什么不用 docxtplJinja2 模板”。答案是docxtpl适合静态模板填充如合同、简历但无法动态解析 Markdown 的嵌套结构如列表中的代码块、代码块中的数学公式。markitdown需要的是“解析-转换-渲染”流水线而非简单变量替换。2.3 架构全景一个典型的 markitdown 工作流长什么样一个完整的markitdown工作流绝非单个 Python 脚本而是一套可配置的微型框架。其核心目录结构如下project_root/ ├── src/ # Markdown 源文件目录 │ ├── index.md # 主文档含 frontmatter 元数据 │ ├── chapter1.md # 章节文件可被主文档 include │ └── assets/ # 静态资源 │ ├── figs/ # 图片PNG/SVG/EMF │ └── equations/ # Mathtype 公式文件.mte 或 .emf ├── templates/ # 文档模板目录 │ ├── word/ # Word 模板.docx含预设样式 │ ├── ppt/ # PPT 模板.pptx含母版版式 │ └── css/ # PDF 样式weasyprint 用的 CSS ├── config.yaml # 全局配置输出路径、字体路径、章节顺序 ├── markitdown.py # 主程序解析调度 └── requirements.txt # 依赖清单整个流程分四步执行解析Parsemarkitdown.py读取src/index.md用mistune轻量级 Markdown 解析器将其转为 AST。特别处理自定义语法如$$Emc^2$$被识别为MathBlock节点!-- pdf:toc --被识别为TocDirective节点。增强Enrich遍历 AST对Image节点检查assets/figs/下是否存在对应文件对MathBlock节点调用mathtype2emf工具或latex2svg生成矢量图并存入assets/equations/对Heading节点根据config.yaml中的chapter_order字段重排章节顺序。渲染Render根据命令行参数如--output word调用对应渲染器WordRenderer加载templates/word/template.docx按 AST 节点类型用python-docxAPI 插入段落、表格、图片、OLE 对象PPTRenderer加载templates/ppt/template.pptx按!-- ppt:slide --注释切分幻灯片用python-pptx插入标题、要点、图表PDFRenderer将 AST 转为 HTML含内联 CSS用weasyprint渲染为 PDF。后处理Post-process对生成的 Word 执行poi操作如设置表格单元格宽度、调整图片环绕方式对 PDF 执行qpdf压缩减小体积最后将所有产物归档至dist/目录。这套架构的威力在于一次编写多端交付一处修改全局生效。当你在src/index.md里改了一个公式运行python markitdown.py --output allWord 报告里的公式、PPT 里的公式图、PDF 里的公式全部同步更新——这才是工程师该有的工作流。3. 核心细节解析与实操要点从零搭建你的第一个 markitdown 环境3.1 环境准备Linux 下的 Python 安装与依赖管理避坑指南虽然热词里有“linux安装 markitdown”但实际部署中Linux 环境反而比 Windows 更“干净”。原因很简单Windows 上 Office COM 接口权限混乱、字体路径不统一、Mathtype 安装路径五花八门而 Linux 下所有依赖都可通过包管理器和 pip 精确控制。不过Linux 安装也有几个经典陷阱我踩过三次才摸清门道。第一步Python 版本与环境隔离必须使用 Python 3.8python-docx3.0 要求但切忌用系统自带的 Python如 Ubuntu 22.04 自带 3.10但部分发行版会打补丁导致python-pptx的字体处理异常。正确做法是# 使用 pyenv 安装纯净 Python推荐 curl https://pyenv.run | bash # 将 pyenv 加入 ~/.bashrc略 source ~/.bashrc pyenv install 3.11.8 pyenv global 3.11.8注意不要用sudo apt install python3.11系统包管理器安装的 Python 常链接到/usr/lib/python3.11而python-pptx需要访问site-packages下的字体缓存权限冲突会导致pptx生成时崩溃。第二步核心依赖安装关键参数详解requirements.txt的内容绝非简单罗列每个包的版本和安装参数都有讲究# python-docx必须锁定 3.0.1因 3.0.0 有表格跨页 bug python-docx3.0.1 # python-pptx用 0.6.22修复了 Linux 下 EMF 图片嵌入的内存泄漏 python-pptx0.6.22 # weasyprint核心必须指定 cairocffi 和 cffi 版本 weasyprint63.0 cairocffi1.6.0 cffi1.16.0 # mistune轻量解析器比 markdown-it-py 内存占用低 40% mistune3.3.0 # mathtype2emf非 PyPI 包需 git clone 编译 githttps://github.com/yourname/mathtype2emf.gitv1.2#eggmathtype2emf安装时必须加--no-cache-dir参数pip install --no-cache-dir -r requirements.txt原因weasyprint依赖的cairocffi在编译时会缓存 C 扩展若缓存损坏常见于 Docker 构建后续pip install会静默跳过编译导致 PDF 渲染时ImportError: cannot import name cairo。--no-cache-dir强制重新编译虽慢 2 分钟但一劳永逸。第三步字体配置Linux 中文 PDF 的生死线weasyprint默认只认系统字体而多数 Linux 发行版如 CentOS Stream 9默认不装中文字体。若跳过此步生成的 PDF 中文全成方块。正确操作是# Ubuntu/Debian sudo apt update sudo apt install fonts-noto-cjk # CentOS/RHEL sudo dnf install gnu-free-fonts-common google-noto-sans-cjk-fonts # 验证字体是否被 weasyprint 识别 python -c from weasyprint import CSS; print(CSS(stringfont-face { font-family: \Noto Sans CJK SC\; }).fonts)然后在templates/css/pdf.css中强制指定font-face { font-family: Noto Sans CJK SC; src: local(Noto Sans CJK SC), url(../fonts/NotoSansCJKsc-Regular.otf); } body { font-family: Noto Sans CJK SC, sans-serif; }实操心得不要用fc-list :lang(zh)查看字体weasyprint用的是自己的字体发现机制。最稳妥的验证法是写一个最小 HTML 文件用weasyprint test.html test.pdf测试看 PDF 是否正常显示中文。3.2 Markdown 源文件规范如何写一份“可被 markitdown 理解”的文档markitdown不是通用 Markdown 解析器它有一套约定俗成的“增强语法”目的是让源文件既能被人阅读又能被机器精准理解。以下是你必须掌握的五个核心规范1. Frontmatter 元数据YAML 头部每份index.md必须以---开头结尾定义全局属性--- title: ROS2 机器人开发实验报告 author: 张三 date: 2024-05-20 version: v1.2 toc: true # 是否生成目录 pdf_header: ROS2 实验室 | 机密 word_template: templates/word/ros2_report.docx ---这些字段会被注入到所有输出文档中Word 的页眉、PDF 的封面、PPT 的标题页。word_template字段尤其重要——它允许你为不同项目ROS2 报告、Python 教程、C 课程维护不同的 Word 模板避免样式污染。2. 数学公式LaTeX 语法 矢量图生成markitdown不直接渲染 LaTeX而是将其转为矢量图嵌入。因此公式必须用$$...$$块级或$...$行内包裹块级公式 $$ \frac{d}{dt} \int_{V(t)} \mathbf{B} \cdot d\mathbf{A} -\oint_{\partial V(t)} \mathbf{E} \cdot d\mathbf{l} $$ 行内公式根据法拉第定律感应电动势 $ \mathcal{E} -\frac{d\Phi_B}{dt} $。markitdown.py会扫描所有$$块调用mathtype2emf生成assets/equations/eq_001.emf并在渲染时插入。注意公式中禁用\begin{equation}等 LaTeX 环境mathtype2emf只支持基础 AMS 数学符号。3. 图片与图表路径规范与尺寸控制图片路径必须相对于src/目录且支持 SVG/PNG/EMF 三种格式![图1ROS2 节点通信架构图](assets/figs/ros2_arch.svg)markitdown会自动检测文件扩展名SVG 和 EMF 作为矢量图原生嵌入Word/PPT 中可无损缩放PNG 则按config.yaml中的image_dpi参数默认 150重采样避免 Word 中图片模糊。若需控制 Word 中图片宽度可在 Markdown 中添加 HTML 属性img srcassets/figs/ros2_arch.svg width500 /python-docx渲染器会识别width属性设置图片绝对宽度单位磅。4. 自定义指令Directives控制多端输出行为这是markitdown最强大的功能用 HTML 注释实现!-- pdf:page-break -- !-- 强制 PDF 分页 -- !-- word:keep-together -- !-- Word 中此段落不跨页 -- !-- ppt:slide title实验设置 layouttitle-only -- !-- PPT 新建一页仅标题 -- !-- ppt:notes --这是给演讲者的备注不会显示在幻灯片上!-- /ppt:notes --这些指令只对对应渲染器生效其他渲染器忽略。例如!-- ppt:slide --在 Word 渲染时完全透明确保源文件的纯净性。5. 章节包含Include模块化写作大型文档应拆分为多个.md文件用!-- include:chapter1.md --语法引入!-- include:chapter1.md -- !-- include:chapter2.md --markitdown.py会在解析时递归读取被包含文件合并为一棵完整 AST。这样ROS2 项目的“硬件搭建”、“软件配置”、“测试结果”三章可由三人并行编写互不干扰。注意事项include语法不支持嵌套即chapter1.md里不能再include否则会死循环。所有包含文件必须放在src/目录下路径不能含..。3.3 模板制作如何打造一个专业的 Word/PPT/PDF 模板模板是markitdown的灵魂。没有好模板再强的解析器也产不出专业文档。这里分享我在 ROS2 项目中打磨出的三套模板核心技巧。Word 模板.docx制作要点.docx本质是 ZIP 包用unzip template.docx -d template_unzipped可查看内部结构。关键文件是word/styles.xml定义所有样式和word/document.xml主内容。但切勿手动编辑 XML正确方法是用 Word 新建空白文档应用所有需要的样式标题 1对应#、标题 2对应##、正文、代码、题注用于图表编号。设置页眉页脚页眉插入{{pdf_header}}占位符markitdown渲染时会替换页脚插入页码格式为“第 X 页共 Y 页”。插入一个空表格设置其样式名为TableGridpython-docx默认识别此名。最关键的一步在文档末尾插入一个“分节符下一页”然后在此节中插入“目录”。python-docx无法生成动态目录但可以保留此静态目录占位后续用docxtpl的|toc过滤器更新需额外步骤。PPT 母版.pptx制作要点python-pptx依赖母版Slide Master中的版式Layout。必须创建至少三种版式Title Only仅标题用于章节页Title and Content标题正文用于文字页Title and Picture标题图片用于图表页。 制作时在 PowerPoint 中视图 → 幻灯片母版在母版中设置字体、颜色、Logo 位置然后在“版式”选项卡中右键“标题和内容”版式 →重命名版式为content。markitdown渲染时会根据!-- ppt:slide layoutcontent --指令精准调用此版式。PDF CSS 模板.css制作要点weasyprint的 CSS 支持大部分标准属性但有两点特殊分页控制用page-break-before: always实现强制分页但必须作用于块级元素如div。因此markitdown会把!-- pdf:page-break --渲染为div classpage-break/divCSS 中定义.page-break { page-break-before: always; }目录生成weasyprint不支持page :first伪类但可用page { top-center { content: ROS2 实验室; } }设置页眉。目录需用 JavaScript 生成不推荐或用weasyprint的--javascript参数需额外安装 Node.js。更务实的做法是在 Markdown 中用!-- pdf:toc --markitdown渲染时生成一个 HTMLnav idtoc再用 CSS 的counter-reset和counter-increment实现自动编号。实操心得模板制作最耗时的环节是“字体嵌入”。Word 模板中若用了非系统字体如思源黑体需在config.yaml中指定word_font_path: /usr/share/fonts/opentype/noto/NotoSansCJKsc-Regular.otfpython-docx会尝试加载此字体。若加载失败会回退到Times New Roman但公式和代码块仍保持清晰。4. 实操过程与核心环节实现手把手跑通 ROS2 实验报告工作流4.1 项目初始化从零创建一个 ROS2 报告项目我们以“ROS2 机器人运动控制实验”为例演示完整搭建过程。假设你已在 Linux 服务器上完成 Python 环境配置3.11.8 依赖。步骤 1创建项目骨架mkdir ros2-control-report cd ros2-control-report mkdir -p src/assets/{figs,equations} templates/{word,ppt,css} touch src/index.md config.yaml markitdown.py requirements.txt步骤 2编写config.yaml# config.yaml output_dir: dist image_dpi: 150 pdf_font_path: /usr/share/fonts/opentype/noto/NotoSansCJKsc-Regular.otf word_template: templates/word/ros2_report.docx ppt_template: templates/ppt/ros2_presentation.pptx css_file: templates/css/pdf.css chapter_order: - src/chapter1.md - src/chapter2.md - src/chapter3.md步骤 3编写src/index.md含 frontmatter--- title: ROS2 机器人运动控制实验报告 author: ROS2 实验室 date: 2024-05-20 version: v1.0 toc: true pdf_header: ROS2 实验室 | 机密 word_template: templates/word/ros2_report.docx --- # 实验概述 本实验基于 ROS2 Humble验证差速驱动机器人在 Gazebo 仿真环境中的运动控制性能。 !-- include:src/chapter1.md -- !-- include:src/chapter2.md -- !-- include:src/chapter3.md -- !-- pdf:page-break -- # 参考文献 1. ROS2 Documentation, https://docs.ros.org/en/humble/ 2. Gazebo Tutorials, http://gazebosim.org/tutorials步骤 4编写src/chapter1.md硬件与环境# 第一章实验环境搭建 ## 1.1 硬件平台 机器人采用 TurtleBot3 Waffle Pi搭载 Raspberry Pi 4B。 ## 1.2 软件环境 - ROS2 Humble (Ubuntu 22.04) - Gazebo Fortress - Rviz2 !-- pdf:page-break --步骤 5准备模板文件templates/word/ros2_report.docx按 3.3 节方法制作确保有标题 1、标题 2、正文样式页眉含{{pdf_header}}占位符。templates/ppt/ros2_presentation.pptx按 3.3 节方法制作母版中定义title-only、content、picture三种版式。templates/css/pdf.css包含字体声明、.page-break规则、page页眉页脚。此时项目目录结构已完备下一步是让markitdown.py活起来。4.2 核心脚本markitdown.py实现精简版含关键注释以下是markitdown.py的核心骨架约 250 行我已移除错误处理等冗余代码聚焦主干逻辑#!/usr/bin/env python3 # -*- coding: utf-8 -*- markitdown: Markdown to Word/PPT/PDF converter import sys import os import yaml import mistune from pathlib import Path from typing import Dict, Any, List, Optional # 导入渲染器 from renderers.word_renderer import WordRenderer from renderers.ppt_renderer import PPTRenderer from renderers.pdf_renderer import PDFRenderer class MarkItDown: def __init__(self, config_path: str): with open(config_path, r, encodingutf-8) as f: self.config yaml.safe_load(f) self.src_dir Path(src) self.output_dir Path(self.config.get(output_dir, dist)) self.output_dir.mkdir(exist_okTrue) def parse_markdown(self, md_path: str) - Dict[str, Any]: 解析 Markdown 文件返回 AST 和 frontmatter md_content (self.src_dir / md_path).read_text(encodingutf-8) # 提取 frontmatterYAML 头部 if md_content.startswith(---): end md_content.find(\n---, 4) if end ! -1: frontmatter yaml.safe_load(md_content[4:end]) md_content md_content[end4:] else: frontmatter {} else: frontmatter {} # 用 mistune 解析为 AST parser mistune.create_markdown( plugins[strikethrough, footnotes, table, url], renderermistune.AstRenderer() ) ast parser(md_content) return {frontmatter: frontmatter, ast: ast} def render_all(self, output_types: List[str]): 主渲染入口 # 解析主文档 main_doc self.parse_markdown(index.md) # 合并章节递归处理 include self._resolve_includes(main_doc[ast], self.src_dir) # 渲染各格式 for output_type in output_types: if output_type word: renderer WordRenderer(self.config, main_doc[frontmatter]) renderer.render(main_doc[ast]) elif output_type ppt: renderer PPTRenderer(self.config, main_doc[frontmatter]) renderer.render(main_doc[ast]) elif output_type pdf: renderer PDFRenderer(self.config, main_doc[frontmatter]) renderer.render(main_doc[ast]) def _resolve_includes(self, ast: List[Dict], base_path: Path): 递归解析 !-- include:file.md -- 指令 i 0 while i len(ast): node ast[i] if node.get(type) html_block and node.get(text, ).strip().startswith(!-- include:): # 提取文件路径 include_path node[text].strip()[13:-4].strip() # 去掉 !-- include: 和 -- included_doc self.parse_markdown(include_path) # 将被包含文档的 AST 插入当前位置 ast[i:i1] included_doc[ast] i len(included_doc[ast]) # 跳过新插入的节点 else: i 1 if __name__ __main__: if len(sys.argv) 2: print(Usage: python markitdown.py [word|ppt|pdf|all]) sys.exit(1) config_path config.yaml if not Path(config_path).exists(): print(fConfig file {config_path} not found!) sys.exit(1) m MarkItDown(config_path) output_types sys.argv[1:] if all in output_types: output_types [word, ppt, pdf] m.render_all(output_types)提示_resolve_includes方法是关键。它遍历 AST找到html_block类型的节点即 HTML 注释提取include路径递归调用parse_markdown并将结果 AST 插入原 AST。这种“AST 级别包含”比字符串拼接更安全避免了 Markdown 语法冲突如#标题在包含文件中被错误解析。4.3 渲染器实现WordRenderer 的核心逻辑含 poi 设置技巧WordRenderer是markitdown中最复杂的渲染器因为它要精确控制 Word 的每一个像素。以下是其核心逻辑renderers/word_renderer.pyfrom docx import Document from docx.shared import Inches, Pt, RGBColor from docx.enum.text import WD_PARAGRAPH_ALIGNMENT from docx.oxml.ns import qn from docx.oxml import OxmlElement class WordRenderer: def __init__(self, config: dict, frontmatter: dict): self.config config self.frontmatter frontmatter self.template_path Path(config[word_template]) self.doc Document(self.template_path) # 加载模板中的样式 self.styles self.doc.styles def render(self, ast: list): # 清空模板中的默认内容 for paragraph in self.doc.paragraphs[:]: p paragraph._element p.getparent().remove(p) for table in self.doc.tables[:]: t table._element t.getparent().remove(t) # 渲染 AST self._render_node_list(ast) # 插入目录需在最后因为目录依赖所有标题 if self.frontmatter.get(toc, False): self._insert_toc() # 保存 output_path Path(self.config[output_dir]) / f{self.frontmatter.get(title, report)}.docx self.doc.save(output_path) print(fWord report saved to {output_path}) def _render_node_list(self, nodes: list): for node in nodes: if node.get(type) heading: self._render_heading(node) elif node.get(type) paragraph: self._render_paragraph(node) elif node
返回列表