news 2026/10/20 5:48:28

用Python打造演示文稿生成工具:内容与样式分离的PPTX自动化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Python打造演示文稿生成工具:内容与样式分离的PPTX自动化实践

1. 为什么我要自己动手做一个演示文稿生成工具

先说说我做这个项目的起因。日常工作里,演示文稿这件事几乎躲不开——项目汇报、方案评审、内部培训、客户提案,一年下来少说也要产出几十份。最开始我也是老老实实打开软件,一页一页地排版、调字体、对齐文本框,一份二十页的稿子折腾三四个小时是常态。后来我尝试用各种在线模板、AI 生成工具,结果发现两个极端:要么模板好看但内容填不进去,要么内容生成得快但排版惨不忍睹,最后还是得手动返工。

真正让我下决心动手的,是一次连续三天的密集汇报。那几天我要把同一套技术方案拆成三个不同受众的版本——给管理层看的要突出收益和风险,给研发看的要突出架构和接口,给运维看的要突出部署和监控。内容有七成是重叠的,但我却在重复劳动上耗掉了大量时间。当时我就在想,能不能有一个工具,让我把内容结构化地写一遍,然后按不同模板自动生成不同风格的演示文稿?

这就是ppt-master的起点。它不是一个在线服务,也不是某个商业软件,而是我自己攒的一套本地工具链,核心目标就三个:内容与样式分离、批量生成、可版本管理。说白了,你把内容写成结构化的文本,工具负责把它渲染成演示文稿,样式由模板决定,改样式不用动内容,改内容不用管样式。

这套东西适合谁?如果你经常需要产出结构相似的演示文稿,或者你的团队需要统一汇报风格,又或者你像我一样对“重复排版”深恶痛绝,那这套思路应该能帮到你。它不需要你懂前端框架,但需要你愿意花半小时理解结构化写作的逻辑。下面我把整个设计思路、核心实现、踩过的坑,完整地拆开讲一遍。

2. 整体设计思路与方案选型

2.1 核心思路:把演示文稿当成“数据”而不是“文档”

传统做法是把演示文稿当成一个文档来编辑,你打开软件,所见即所得地拖拽、输入。这种方式对单份稿子很友好,但一旦涉及批量、复用、版本管理,就非常痛苦。因为文档格式是二进制的(或者复杂的 XML 包),你没法用文本工具去 diff、去合并、去批量替换。

我的核心思路是反过来:把演示文稿的内容抽象成结构化数据,把样式抽象成模板,渲染过程就是数据加模板生成最终文件。这个思路其实和静态网站生成器(比如把 Markdown 渲染成 HTML)是一模一样的,只不过输出格式从 HTML 变成了演示文稿文件。

这样一来,内容可以用纯文本写,能进版本控制,能多人协作,能批量替换;样式可以独立迭代,换一套模板就换一种视觉风格,内容一个字都不用改。这个分离带来的好处,在我后来的实际使用中被反复验证——有一次客户临时要求把所有蓝色主色调改成橙色,我只改了一个配置文件,三分钟重新生成了全部十二份稿子。

2.2 为什么选 Python 而不是其他语言

选型这块我考虑过几个方案。用 JavaScript 的话,生态里有现成的库可以操作演示文稿文件,前端工具链也成熟;用 Go 的话,编译成单文件分发方便,性能好。但最后我还是选了 Python,理由很实际:

  • 文本处理生态最顺手。我的内容源是结构化文本(后面会讲具体格式),Python 在解析、转换、模板渲染这块的库最丰富,写起来最快。
  • 和数据处理无缝衔接。很多演示文稿的数据其实来自表格、数据库、接口,Python 在这块是主场,我可以直接从数据源拉数据填进模板。
  • 调试成本低。这种工具类项目,改一改跑一跑是常态,Python 的交互式环境让我能快速验证想法,不用反复编译。

当然 Python 也有缺点,比如打包分发麻烦、运行需要环境。但对我这种自用为主的场景,这些都不是问题。如果你要分发给不装 Python 的同事,可以用打包工具打成可执行文件,这个后面会提。

2.3 输出格式的选择:为什么是 PPTX

演示文稿的格式有好几种,我最终选了 PPTX,原因很直接:它是目前兼容性最好的开放格式。几乎所有主流办公软件都能打开和编辑,而且它本质上是一个 ZIP 包,里面是 XML 文件,这意味着我可以用程序去操作它的每一个细节——每一页、每一个文本框、每一个形状。

相比之下,有些格式是纯展示的,生成出来就没法再编辑;有些格式是私有的,程序操作起来处处受限。PPTX 的开放性让我既能生成,又能让用户拿到后继续手动微调,这个“可编辑性”在实际工作中太重要了——毕竟工具再智能,也总有需要人工润色的地方。

提示:PPTX 文件本质是 ZIP 包,你可以把后缀改成 .zip 解压,看到里面的 XML 结构。理解这一点对排查生成问题非常关键,后面讲排查技巧时会用到。

2.4 模板机制的设计取舍

模板这块我纠结了很久。一种做法是让用户直接提供一个“样板文件”,我读取它的样式然后套用;另一种做法是用代码定义样式,用户改代码。前者对非程序员友好,后者对程序员友好。

最后我选了一个折中方案:模板用配置文件加样板文件结合。配置文件定义颜色、字体、字号、间距这些参数,样板文件定义版式布局(标题在哪、内容在哪、图片占多大)。这样改颜色字体不用碰样板,改布局不用碰配置。对于最常见的需求(换配色、换字体),用户只需要改一个 YAML 文件,门槛很低。

这个取舍的逻辑是:把变化频率高的东西做成易改的,把变化频率低的东西做成稳定的。配色字体经常换,所以做成配置;版式布局很少动,所以做成样板。事实证明这个判断是对的,我后来百分之九十的调整都只动了配置文件。

3. 核心细节解析与实操要点

3.1 内容源格式:为什么用 Markdown 加 Front Matter

内容源我选了 Markdown,理由和写博客一样:纯文本、易读易写、工具链成熟。但纯 Markdown 不够,因为演示文稿需要一些元信息——比如这一页用什么版式、这一页的标题层级、要不要分栏。所以我在 Markdown 基础上加了一个约定:用 Front Matter 定义页面元信息,用标题层级定义内容结构。

具体来说,每一页对应一个 Markdown 文件,文件开头用 YAML 格式的 Front Matter 声明这一页的版式、标题、备注等。正文部分用标准的 Markdown 语法写要点。这样写出来的内容,人读起来清爽,程序解析起来也简单。

--- layout: title-content title: 项目背景与目标 notes: 这一页重点讲清楚为什么要做这件事 --- ## 当前面临的三个核心问题 - 内容重复劳动严重,同一套内容要维护多个版本 - 样式调整牵一发而动全身,改一个颜色要动几十页 - 版本管理困难,二进制文件无法有效 diff 和合并 ## 我们的目标 - 内容与样式彻底分离 - 一次编写,多模板输出 - 全流程可版本控制

这个格式的好处是,即使你不用我的工具,这份 Markdown 本身也是一份结构清晰的讲稿大纲。工具只是把它“翻译”成了演示文稿而已。

3.2 版式系统的抽象:把布局变成可复用的组件

版式系统是整个工具的核心。我的做法是把常见的页面布局抽象成若干“版式组件”,每个组件定义了标题区、内容区、图片区的位置和尺寸。内容源里通过layout字段指定用哪个版式,渲染时就按对应版式摆放元素。

我最初只做了三种版式:纯标题页、标题加要点页、标题加图片页。后来在实际使用中逐步扩展到了十几种,包括两栏对比、三栏卡片、时间线、表格页、引用页等等。每增加一种版式,其实就是定义一组坐标和尺寸,工作量不大,但复用价值很高。

这里有个关键经验:版式不要做得太细,要留出弹性。我一开始想把每种内容都做一个专属版式,结果版式数量爆炸,维护成本极高。后来我改成“粗版式加内容自适应”——比如“标题加要点页”这个版式,内容区是一个弹性容器,要点多了自动缩小字号和行距,要点少了自动放大。这样用少量版式覆盖了大部分场景。

3.3 样式配置的层级与继承

样式配置我设计了三层继承:全局默认、模板覆盖、页面覆盖。全局默认定义最基础的字体、颜色、间距;模板覆盖针对某套模板调整;页面覆盖针对某一页做特殊处理。渲染时按优先级合并,页面覆盖优先级最高。

这个继承机制解决了一个很实际的问题:大部分页面用统一风格,但偶尔有一页需要特殊强调(比如结论页用大字号、警示页用红色)。如果没有继承机制,我要么给这一页单独做模板,要么手动改生成后的文件,都很麻烦。有了继承,我只需要在这一页的 Front Matter 里写几行覆盖配置就行。

# 全局配置示例 global: font_family: "思源黑体" font_size: title: 32 heading: 24 body: 18 color: primary: "#2C5F8D" accent: "#E8A33D" text: "#333333"

3.4 渲染引擎的工作流程

渲染引擎的工作流程分四步:解析内容源、合并样式配置、计算布局、写入文件。这四步里,最复杂的是布局计算,因为要处理文本换行、自动缩放、元素避让这些细节。

文本换行这块我踩过坑。最初我简单地按字符数估算换行,结果中英文混排时完全不准——中文一个字占的宽度和英文一个字母差很多。后来我改成了按“显示宽度”计算,中文算两个单位,英文算一个单位,再结合字号和容器宽度算每行能放多少字。这个估算不需要绝对精确,因为最终渲染时办公软件会自己调整,我只需要保证大致不溢出就行。

自动缩放是另一个难点。当内容超出容器时,我有两个选择:要么缩小字号,要么分页。我的策略是优先缩小字号,缩到下限还放不下才分页。字号下限我设的是正文 14 号,再小就影响阅读了。这个阈值可以根据实际场景调整,比如投影用的稿子下限可以设高一点,打印用的可以设低一点。

4. 实操过程与核心环节实现

4.1 环境准备与依赖安装

先把环境搭起来。我用的是 Python 3.9 以上版本,核心依赖就两个:一个操作 PPTX 文件的库,一个解析 Markdown 和 YAML 的库。安装很简单:

pip install python-pptx pyyaml markdown

python-pptx是操作 PPTX 的主力库,它能创建幻灯片、添加文本框、设置样式、插入图片,基本上你能在软件里手动做的操作,它都能用代码完成。pyyaml负责解析 Front Matter 和配置文件,markdown负责把 Markdown 正文转成结构化的 HTML 片段,方便我提取标题、列表、加粗这些元素。

注意:python-pptx对某些高级特性(比如复杂的动画、SmartArt)支持有限。如果你的模板里用了这些,生成时可能会丢失。我的建议是模板尽量用基础元素,把复杂效果留给后期手动加。

4.2 项目目录结构设计

目录结构我改过好几版,最后定下来的是这样:

ppt-master/ ├── content/ # 内容源,每页一个 md 文件 │ ├── 01-cover.md │ ├── 02-background.md │ └── ... ├── templates/ # 模板目录 │ ├── default/ │ │ ├── config.yaml # 样式配置 │ │ └── layouts.yaml # 版式定义 │ └── dark/ │ ├── config.yaml │ └── layouts.yaml ├── output/ # 生成结果 ├── assets/ # 图片、图标等资源 └── build.py # 主程序

这个结构的关键是内容和模板完全分离。content目录里只有内容,templates目录里只有样式,两者通过命令行参数关联。这样我可以拿同一份内容,分别用default和dark两套模板生成两份风格完全不同的稿子。

4.3 内容解析的关键代码

内容解析这块,核心是把 Markdown 文件拆成 Front Matter 和正文两部分,然后分别处理。Front Matter 用 YAML 解析成字典,正文用 Markdown 库转成 HTML 再提取元素。

import yaml import markdown from pathlib import Path def parse_content_file(filepath): text = Path(filepath).read_text(encoding='utf-8') # 分离 Front Matter 和正文 if text.startswith('---'): parts = text.split('---', 2) meta = yaml.safe_load(parts[1]) body = parts[2].strip() else: meta = {} body = text.strip() # 正文转 HTML 再解析元素 html = markdown.markdown(body) elements = extract_elements(html) return { 'meta': meta, 'elements': elements }

extract_elements这个函数负责从 HTML 里提取标题、段落、列表、加粗等元素,转成我自己的数据结构。这一步是整个流程的基础,提取得越准确,后面渲染越省心。

4.4 布局计算的核心逻辑

布局计算是渲染引擎里最需要动脑子的部分。我以“标题加要点页”为例讲讲思路。这个版式分三个区域:标题区在顶部,占页面高度的 15%;内容区在中间,占 70%;页脚区在底部,占 15%。

内容区里,每个要点是一个段落。我需要计算每个段落占多少行,累加看是否超出内容区高度。如果超出,先尝试缩小字号,字号缩到下限还超出,就把超出的部分挪到下一页。

def calculate_layout(elements, layout_def, style): content_height = layout_def['content']['height'] current_size = style['font_size']['body'] min_size = style['font_size']['body_min'] while current_size >= min_size: total_height = 0 for elem in elements: lines = estimate_lines(elem['text'], layout_def['content']['width'], current_size) total_height += lines * current_size * style['line_height'] if total_height <= content_height: return current_size, elements, [] current_size -= 1 # 字号到下限还放不下,分页 return split_content(elements, layout_def, style)

estimate_lines就是前面说的按显示宽度估算行数。这个函数不需要绝对精确,因为最终渲染时办公软件会微调,我只要保证不溢出就行。

4.5 生成 PPTX 的实操步骤

生成 PPTX 这块,python-pptx的 API 用起来很直观。核心步骤是:创建演示文稿对象、添加幻灯片、在幻灯片上添加文本框和形状、设置样式、保存文件。

from pptx import Presentation from pptx.util import Inches, Pt from pptx.dml.color import RGBColor def build_presentation(pages, template_config): prs = Presentation() # 设置幻灯片尺寸为 16:9 prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) for page in pages: slide = prs.slides.add_slide(prs.slide_layouts[6]) # 空白版式 layout = template_config['layouts'][page['meta']['layout']] # 添加标题 title_box = slide.shapes.add_textbox( Inches(layout['title']['x']), Inches(layout['title']['y']), Inches(layout['title']['width']), Inches(layout['title']['height']) ) title_frame = title_box.text_frame title_frame.text = page['meta']['title'] # 设置字体样式 for paragraph in title_frame.paragraphs: for run in paragraph.runs: run.font.size = Pt(template_config['font_size']['title']) run.font.name = template_config['font_family'] run.font.color.rgb = RGBColor.from_string( template_config['color']['primary'].lstrip('#')) # 添加内容要点 content_box = slide.shapes.add_textbox(...) # ... 类似的处理 prs.save('output/presentation.pptx')

这段代码是简化版,实际实现里还要处理列表缩进、加粗、图片插入、页脚页码等等。但核心逻辑就是这样:按版式定义的位置和尺寸,把内容元素一个个摆上去。

4.6 批量生成与多模板输出

批量生成是我最常用的功能。命令行传两个参数:内容目录和模板名,程序自动读取所有内容文件,按文件名排序,逐个渲染,最后合并成一个 PPTX。

python build.py --content ./content --template default --output ./output/default.pptx python build.py --content ./content --template dark --output ./output/dark.pptx

两条命令,同一份内容,生成两份风格完全不同的稿子。这个能力在需要给不同受众准备材料时特别有用。我甚至做过一个极端测试:把同一份内容用五套模板生成五份,然后让同事盲选哪份最适合某个场景,结果大家选出来的风格差异很大,说明模板切换确实有实际价值。

5. 常见问题与排查技巧实录

5.1 生成的文件打不开或提示损坏

这是最常见的问题,通常有几个原因。第一个是文件被占用——如果你在生成时还开着同名的 PPTX 文件,写入会失败。解决办法很简单,关掉文件再生成。第二个是路径里有特殊字符,某些库对中文路径或空格处理不好,建议输出路径用纯英文。第三个是内容里有非法字符,比如某些控制字符,会导致 XML 结构出错。

排查这类问题,我有个笨但有效的办法:把生成的 PPTX 后缀改成 .zip,解压看里面的 XML。如果某个 XML 文件明显不完整或者有乱码,就能定位到是哪一页、哪个元素出了问题。这个方法我用了无数次,比看报错信息还直接。

5.2 文字溢出容器或显示不全

文字溢出是布局计算不准导致的。前面说过,我的行数估算是近似的,遇到极端情况(比如超长英文单词、大量标点符号)会估不准。解决办法有两个:一是调低字号下限,给更多缩放空间;二是给容器加一点余量,比如实际可用高度只按 90% 算。

还有一个隐蔽的原因:字体不一致。我在配置里指定了“思源黑体”,但生成文件的机器上如果没装这个字体,办公软件会用默认字体替代,而默认字体的宽度可能不一样,导致原本算好的布局溢出。所以我的经验是,尽量用系统自带字体,或者把字体文件一起分发。

5.3 图片显示异常或位置偏移

图片问题通常出在尺寸和比例上。python-pptx插入图片时,如果不指定尺寸,它会按原始尺寸插入,大图会超出页面。我的做法是统一按容器尺寸等比缩放,保持宽高比,居中放置。

def add_image(slide, img_path, container): # 获取原图尺寸 from PIL import Image with Image.open(img_path) as img: orig_w, orig_h = img.size # 计算等比缩放后的尺寸 ratio = min(container['width'] / orig_w, container['height'] / orig_h) new_w = orig_w * ratio new_h = orig_h * ratio # 居中计算 left = container['x'] + (container['width'] - new_w) / 2 top = container['y'] + (container['height'] - new_h) / 2 slide.shapes.add_picture(img_path, Inches(left), Inches(top), Inches(new_w), Inches(new_h))

这段代码的关键是ratio的计算,取宽高缩放比例中较小的那个,保证图片完整显示在容器内。居中计算则是让图片在容器里视觉平衡。

5.4 常见问题速查表

问题现象可能原因排查方法解决方案
文件打不开文件被占用检查是否开着同名文件关闭文件后重新生成
文件打不开路径含特殊字符检查输出路径改用纯英文路径
文字溢出行数估算不准检查该页内容长度调低字号下限或加容器余量
文字溢出字体缺失检查目标机器字体改用系统自带字体
图片超界未指定尺寸检查图片插入代码按容器等比缩放
图片模糊原图分辨率低检查原图尺寸换高分辨率图或缩小显示尺寸
样式不生效配置层级冲突检查页面覆盖配置确认优先级顺序
生成速度慢图片过多过大检查资源目录压缩图片或改用链接引用

5.5 几个我踩过的坑和独家技巧

第一个坑是中文编码。早期版本我在 Windows 上生成的文件,到 Mac 上打开中文全是乱码。后来发现是文件读写时没指定编码,默认用了系统编码。解决办法是全程强制 UTF-8,读写文件都加encoding='utf-8'。

第二个坑是列表嵌套。Markdown 支持多级列表,但python-pptx的段落缩进需要手动设置。我一开始没处理,所有列表都顶格显示,层级关系全丢了。后来我在解析时记录了每个列表项的层级,渲染时按层级设置缩进量。

第三个技巧是用备注存讲稿。演示文稿的备注功能很好用,但手动填很麻烦。我在 Front Matter 里加了一个notes字段,生成时自动写入备注区。这样我写内容的时候顺手把讲稿也写了,演示时直接看备注,不用另外准备。

第四个技巧是生成前先预览。我写了一个简单的预览模式,不生成 PPTX,而是把每页的布局用字符画出来,快速检查有没有明显的溢出或错位。这个模式在调试版式时特别高效,不用反复打开办公软件。

6. 工具选型与扩展思路

6.1 为什么没有做成图形界面

很多人问我为什么不做一个图形界面,点点鼠标就能用。我想过,但最后没做,原因是这个工具的核心用户是愿意写结构化文本的人。如果一个人愿意用 Markdown 写内容,那命令行对他来说不是障碍;如果一个人只愿意用鼠标拖拽,那这个工具的价值对他来说也不大,因为他本来就不需要内容与样式分离。

当然,如果将来要推广给更多人用,加一个简单的图形界面是可行的。但那是另一个阶段的事,现阶段我优先保证核心功能的稳定和灵活。

6.2 可以扩展的方向

这套工具目前满足了我的核心需求,但还有不少可以扩展的地方。比如接入数据源,从表格或接口直接拉数据填模板,适合做数据汇报类的稿子。比如支持更多输出格式,同一份内容除了生成 PPTX,还能生成 PDF、网页、甚至长图,适合不同分发场景。再比如模板市场,把常用模板做成可分享的包,大家互相交换。

我个人最想加的是智能排版——根据内容自动推荐最合适的版式。比如检测到内容是两两对比,就自动用两栏版式;检测到是时间顺序,就自动用时间线版式。这个用一些简单的规则就能实现,不需要复杂的算法。

6.3 给想自己动手的人的建议

如果你想自己搭一套类似的工具,我的建议是从最小可用版本开始。不要一上来就设计复杂的模板系统、支持所有 Markdown 语法、处理各种边界情况。先做一个能生成纯文字页面的版本,跑通“解析内容、计算布局、写入文件”这个核心流程,然后再逐步加功能。

我自己的第一版只支持标题和要点,版式只有一种,样式配置写死在代码里。但就是那个简陋的版本,让我验证了核心思路的可行性。后面所有的功能都是在这个基础上长出来的。先跑通,再优化,这个原则在工具类项目里特别重要。

另外,多用自己的工具。我强迫自己用这套工具产出了十几份实际汇报材料,过程中发现的问题比测试时多得多。真实使用场景会逼着你解决那些“理论上不会发生”的问题,而这些问题的解决才是工具成熟的标志。

最后分享一个小心得:把生成结果也纳入版本控制。虽然 PPTX 是二进制文件,diff 不友好,但保留每次生成的结果,能让你在出问题时快速回滚。我的做法是每次生成时在文件名里带上时间戳和模板名,比如report-20240115-default.pptx,这样历史版本一目了然,需要对比时直接打开两个文件看就行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 5:48:28

砍掉本地开发环境后,团队效率翻倍:云端开发实践

1. 一次“断臂求生”式的决定先说说背景。我所在的团队维护着一个中等规模的业务系统&#xff0c;前后端加起来十几个服务&#xff0c;日常开发靠本地环境跑。很多团队都是这么干的&#xff0c;但我们那时候已经被本地环境折磨到快崩溃了。现象很典型&#xff1a;前端同学电脑上…

作者头像 李华
网站建设 2026/10/20 5:47:54

工业组件4关键点检测:U-net亚像素级定位实战

1. 工业组件关键点检测到底在解决什么问题第一次接触“工业组件4个关键点检测”这个需求时&#xff0c;很多人脑子里冒出来的第一个念头是&#xff1a;直接上目标检测不就完了&#xff1f;画四个框&#xff0c;每个框代表一个关键点&#xff0c;多省事。我一开始也这么想过&…

作者头像 李华
网站建设 2026/10/20 5:47:41

artcraft实战:从零搭建素材库与创意落地系统

1. 从“artcraft”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“artcraft”这个标题&#xff0c;我脑子里蹦出来的第一反应是&#xff1a;这大概率不是一个单纯的绘画工具&#xff0c;也不是一个纯粹的手工教程合集。把“art”和“craft”拼在一起&#xff0c;本身…

作者头像 李华
网站建设 2026/10/20 5:47:35

DHCP与NAT实战指南:从原理到配置,打通内网到公网链路

1. 从一个真实组网场景说起&#xff1a;为什么DHCP和NAT是家庭与企业网络的“隐形支柱”很多人第一次接触网络配置&#xff0c;都是从“为什么我的电脑插上网线就能上网”这个问题开始的。你打开电脑&#xff0c;没有手动填过IP地址、子网掩码、网关和DNS&#xff0c;但浏览器就…

作者头像 李华
网站建设 2026/10/20 5:47:31

artcraft工作流:从创意想法到可交付成品的工程化实践指南

1. 从“artcraft”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“artcraft”这个词&#xff0c;我脑子里蹦出来的第一反应是“艺术”加“手艺”的组合。这不是一个随便拼出来的名字&#xff0c;它背后指向的是一类非常具体的东西——把创意想法变成看得见摸得着的实…

作者头像 李华
网站建设 2026/10/20 5:47:25

芯参谋(60):BDC有刷直流电机驱动电路设计指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华