用python-docx实现红头文件模板化排版:字体、红线与版式全攻略
简介红头文件是党政机关、企事业单位发布正式公文时采用的规范格式本模板面向行政文秘、办公室工作人员及需要撰写公文的新手旨在减少排版失误、提升发文效率。资源包内含1个doc文档大小约39KB以带批注的样例形式呈现文件头、发文字号、标题、主送机关、正文、附件、落款与日期、抄送栏、主题词等完整要素并附有各项格式要求的排版说明如标题可用二号宋体加粗居中、正文用三号仿宋体单倍行距、附件名称后不加标点且下空三行等细节。目前已有773人学习下载。借助该模板使用者既可对照规则逐项调整版式也可直接套用规范框架快速生成符合国家公文标准的红头文件模板中的批注位置直观标注与示例一一对应特别适合日常办公、单位内部通知及对外发文的格式参考与快速排版。1. 红头文件排版真正难的不是文笔是对不齐拿到一份红头文件模板.doc多数人第一反应是往里面填字结果填到一半就发现版式全乱标题不居中、发文字号压红线、附件名字不对齐、盖章之后日期位置飘了。这不是打字的问题而是红头文件的版式从来不是「排出来好看」它背后是一整套可量化的排版参数哪种文字用几号字、哪一行距离红线空几行、落款右空几个全角字符每一项都有明确要求。这篇内容以红头文件模板.doc为底把公文格式里涉及的字体字号、行距、页边距、红色反线、版记要素逐一拆开落到 Word 的操作参数和可复现的脚本上。适合两类人看一类是在企事业单位里要按规范出文的办公室人员另一类是给单位做公文模板、批量生成通知文件的技术人员。2. 把公文规范落进 Word 样式字体字号与段落格式的参数表红头文件的版式规则绝大多数是「文字级」的约束。如果 Word 文档里每个段落都手动调字体、改字号、敲空格模板用不了几回就会走样。正确做法是先把这些规则固化成 Word 样式Style再基于样式填写内容。2.1 先记住这张字体映射表公文中文字号有一个对应关系二号等于 22pt三号等于 16pt四号等于 14pt。模板中反复出现的「三号仿宋体字」「二号宋体字」在 Word 里设置字号时直接填「三号」即可但脚本生成文档时需要换算成磅值。文档部位字体字号排布要求发文机关标识红头宋体或小标宋加粗二号22pt红色居中发文字号仿宋_GB2312三号16pt红线之上居中文件标题宋体或小标宋可加粗二号22pt红线下空 2 行居中主送机关仿宋_GB2312三号16pt左侧顶格全角冒号正文仿宋_GB2312三号16pt首行缩进 2 字符单倍行距一级标题一、黑体三号16pt首行缩进 2 字符二级标题一楷体_GB2312三号16pt首行缩进 2 字符附件说明仿宋_GB2312三号16pt正文下空 1 行左空 2 字主题词、抄送、印发机关黑体、宋体、仿宋三号16pt版记区按指定位置排布需要留意的是规范里说的「单倍行距」在 Word 中直接选单倍行距会受字体度量影响同一份文档在不同机器上打开行距可能不同。我一般在模板里把正文行距改为固定值 28 磅或 29 磅视觉上接近单倍行距而且跨机器稳定。2.2 用 python-docx 把 Normal 样式改成「三号仿宋正文」如果每次新建文档都要手动调一遍正文样式那模板就失去了意义。下面这段脚本把Normal样式直接改成公文字体参数后续所有基于该样式写入的段落都会自动带上三号仿宋和固定行距from docx import Document from docx.shared import Pt from docx.enum.text import WD_LINE_SPACING from docx.oxml.ns import qn doc Document() # 正文样式三号仿宋固定行距 28 磅 normal doc.styles[Normal] normal.font.name 仿宋_GB2312 normal.font.size Pt(16) # 三号 16pt normal.element.rPr.rFonts.set(qn(w:eastAsia), 仿宋_GB2312) pf normal.paragraph_format pf.line_spacing_rule WD_LINE_SPACING.EXACTLY pf.line_spacing Pt(28)这段代码里最关键的是rFonts.set(qn(w:eastAsia), 仿宋_GB2312)它设置的是中文字体。如果只写font.nameWord 会把该设置应用到ascii字体上中文仍然显示为默认的宋体这是脚本生成公文时最常踩的坑。行距用WD_LINE_SPACING.EXACTLY固定为 28 磅而不是用单倍行距原因是仿宋_GB2312 在部分 Windows 机器上会回退为仿宋回退后字体度量变化单倍行距下的行高会不一致。固定行距牺牲了一点灵活性但换来的是所有机器打开版式一致。2.3 首行缩进 2 字符别用「缩进 0.74cm」代替正文首行缩进 2 字符在 Word 里可以直接设置但 python-docx 的paragraph_format.first_line_indent只能接收长度单位。如果按三号字 16pt 换算成 32pt 设置一旦文档里混入其他字号的文字缩进量不会自动跟随。更严谨的做法是直接写入 Word 的firstLineChars属性def set_first_line_indent_chars(paragraph, chars2): 按字符数设置首行缩进跟随字号变化 pPr paragraph._p.get_or_add_pPr() ind pPr.find(qn(w:ind)) if ind is None: ind pPr.makeelement(qn(w:ind), {}) pPr.append(ind) ind.set(qn(w:firstLineChars), str(chars * 100)) # 200 表示 2 字符 ind.set(qn(w:firstLine), 568) # 回退值约 1cm兼容旧解析器firstLineChars的值以百分之一字符为单位200就是 2 个字符。设置之后无论段落里是三号还是四号字缩进都会自动保持 2 个汉字的宽度。firstLine作为回退值给不支持字符单位的旧版解析器使用。2.4 自建「公文标题」样式绕开 Word 内置标题公文的「一、」「一」标题有指定的字体但直接用 Word 内置的 Heading 1、Heading 2 会带入大纲级别和导航样式而且内置标题默认带蓝色需要覆盖的属性太多。建议新建独立样式from docx.enum.style import WD_STYLE_TYPE def make_gongwen_style(doc, name, font_name, size_pt, boldFalse): st doc.styles.add_style(name, WD_STYLE_TYPE.PARAGRAPH) st.font.name font_name st.font.size Pt(size_pt) st.font.bold bold st.element.rPr.rFonts.set(qn(w:eastAsia), font_name) st.paragraph_format.line_spacing_rule WD_LINE_SPACING.EXACTLY st.paragraph_format.line_spacing Pt(28) return st # 一级标题黑体三号二级标题楷体三号 make_gongwen_style(doc, 公文一级标题, 黑体, 16) make_gongwen_style(doc, 公文二级标题, 楷体_GB2312, 16)样式名不要以Heading开头避免 Word 自动附加标题编号。用add_style创建的段落样式在下拉框中同样可选不影响查找替换也不会污染导航窗格。提示黑体、仿宋、楷体在部分 Office 环境里安装的是「黑体」「仿宋」「楷体」不带_GB2312后缀。如果目标机器上没有仿宋_GB2312Word 会自动替换版式通常会发生变化。做模板时最好先确认发文单位实际安装的字体名称再决定用哪一套。3. 页面设置与红色反线版心、页码和发文字号如何一次对齐红头文件的页面参数比普通文档严格得多尤其是页边距和红线。页边距错了版心宽度就不对发文字号与红线的相对位置也会跟着错。3.1 页面参数与版心尺寸标准公文用 A4 纸21.0cm × 29.7cm上边距 3.7cm、下边距 3.5cm、左边距 2.8cm、右边距 2.6cm。由此得出版心宽度为21.0 - 2.8 - 2.6 15.6cm版心高度为29.7 - 3.7 - 3.5 22.5cm。红色分隔反线的长度就等于版心宽度。用 python-docx 设置页面参数from docx.shared import Cm section doc.sections[0] section.page_width Cm(21.0) section.page_height Cm(29.7) section.top_margin Cm(3.7) section.bottom_margin Cm(3.5) section.left_margin Cm(2.8) section.right_margin Cm(2.6)这些值与 Word「页面设置」对话框里的输入值一一对应。需要注意部分打印机有不可打印区域如果预览时发现红线或页码被裁切先检查打印机属性里的可打印边距而不是直接改页面边距。3.2 红色反线用段落下边框不要用形状「红头文件」的标志性元素就是标题下方的红色分隔线。常见做法是在文档里插入一条直线形状但形状在页面缩放、跨 Word/WPS 打开、转 PDF 时都可能产生偏移。更可靠的方式是给一个空段落设置下边框from docx.oxml import OxmlElement from docx.oxml.ns import qn def add_red_line_paragraph(doc): 添加一个只有下边框的空段落作为红色反线 p doc.add_paragraph() pPr p._p.get_or_add_pPr() pBdr OxmlElement(w:pBdr) bottom OxmlElement(w:bottom) bottom.set(qn(w:val), single) # 单实线 bottom.set(qn(w:sz), 12) # 线宽12/8 pt 1.5pt bottom.set(qn(w:space), 1) # 线与段落的间距 bottom.set(qn(w:color), FF0000) # 红色 pBdr.append(bottom) pPr.append(pBdr) return pw:sz的单位是八分之一磅12表示 1.5pt 线宽这是公文红线比较常见的粗细。线和段落之间留 1 磅空隙避免文字紧贴红线。因为边框默认随段落宽度也就是版心宽度延伸所以这条红线天然与版心等宽。红头的完整顺序是发文机关标识红头居中发文字号紧随其后居中排布再下方是这条红色反线红线下空 2 行写文件标题。模板里发文字号与红线的相对位置就要靠这个段落顺序来保证。3.3 页码单页右空 1 字双页左空 1 字公文的页码在版心下边缘之下用四号半角宋体阿拉伯数字数字左右各放一条一字线比如「— 1 —」。按照标准单页码居右空 1 字双页码居左空 1 字也就是说奇数页和偶数页的页码不在同一个水平位置上。在 Word 中设置时不勾选「首页不同」但要勾选「奇偶页不同」然后分别在奇数页页脚和偶数页页脚插入页码。奇数页页脚的页码右对齐再设置右缩进 1 个字符偶数页页脚的页码左对齐设置左缩进 1 个字符。页码格式选择「— 1 —」这种带一字线的样式如果样式列表里没有可以插入普通页码后手动在域前后输入「—」。用 python-docx 设置奇偶页不同section.even_and_odd_headers True这行代码开启奇偶页不同的页眉页脚模式。之后文档中会出现evenPageFooter和default两套页脚分别处理双页和单页的页码排布。页码本身是域PAGE字段python-docx 没有直接的 API 插入域通常需要手动拼 OOXML所以这个场景我一般建议在 Word 里把页脚模板做好后用「文档部件」保存或者直接基于现有红头文件模板.doc修改页脚而不是从零用脚本生成页脚。4. 红头文件高频踩坑点发文字号、附件、落款与版记的细节前面把框架搭好了真正让模板「看起来不专业」的往往是几个细节位置。这些位置出错率最高而且人眼检查时容易忽略值得单独列一遍。4.1 发文字号六角括号与位置检查项正确写法常见错误写法年份括号××××字〔2024〕8号××××字[2024]8号括号字体与发文字号同字体输入法自动变成半角位置红色反线之上居中偏左或压到红线顺序机关代字 年份 序号年份写最前发文字号的年份必须用六角括号「〔〕」不是方括号、不是圆括号。六角括号在中文输入法里通过「特殊符号」插入常见的坑是打字时图省事用了方括号文件归档后按年份检索不到。发文字号整体用三号仿宋体居中排在红色反线上方。模板中如果红线位置固定发文字号段落就不要加首行缩进或左右缩进直接居中对齐。4.2 附件说明对齐的是序号不是冒号附件说明在正文下空 1 行左空 2 字标注「附件」。多个附件时按下列版式排列附件1. ×××××××××××××××× 2. ××××××××××××××××注意第二行「2.」的首字和第一行「1.」对齐而不是和「附件」对齐。附件名称后不加标点符号末尾也不要句号。如果附件名称很长需要回行回行后的文字要与首行的「1.」后面的内容对齐而不是顶到左边界。这些细节用 Word 的标尺缩进比敲空格可靠建议用「悬挂缩进」实现。落款是另一个高发错误区。成文日期右空 4 个全角字符不是把日期右对齐就算完成。正确做法是日期段落右对齐后再设置右缩进 4 字符约 64pt。加盖公章时公章要压在成文日期上俗称「骑年盖月」但这在纯电子文档里可以退化为「不盖」。如果模板需要打印盖章落款区域上方要预留出至少两行空白且发文机关署名与日期要左右居中对齐后整体随印章位置微调。4.3 版记区主题词、抄送与印发机关版记位于文件末尾模板涉及三个要素元素字体字号排布位置「主题词」三个字黑体三号居左顶格后接全角冒号主题词词目宋体三号词目之间空 1 字抄送机关仿宋_GB2312三号左空 1 字机关名称间用逗号末尾句号印发机关仿宋_GB2312三号印发机关与日期左右各空 1 字主题词是很多单位历史模板的保留项现行通用做法里已经不做强制要求但只要模板里有这个区域字体和位置就得按上述参数来。词目之间空 1 字用「全角空格」或「宋体空格」都行关键是不要用 Tab否则在不同字体下宽度不一致。抄送机关名称之间用逗号全角最后以句号结束印发的日期右空 1 字。4.4 一份可执行的检查清单出文前照这个顺序过一遍能拦截掉大多数版式问题打开「显示/隐藏编辑标记」确认全文没有多余的手动换行符。全选正文检查字体是否为「仿宋_GB2312」且字号为「三号」。检查红线上方是否有发文字号年份是否用了六角括号。检查标题是否在黑线下空 2 行的位置且居中、无首行缩进。检查所有「1.」「1」标题样式是否为黑体或楷体。检查落款日期的右缩进是否为 4 字符不要只右对齐。切换奇偶页视图确认页码左右位置正确。5. 模板化输出用 python-docx 把红头文件变成可复现脚本到这一步红头文件的所有排版参数已经拆完了。如果只是做单份文件手动设置完全够用。但如果你要给单位维护一个长期使用的公文模板或者需要批量生成通知建议把这些参数固化成脚本。5.1 一个完整的生成骨架下面这个函数把发文机关标识、发文字号、红线、标题、正文段落串起来形成一个可复用的生成逻辑def build_redhead_doc(doc, org_name, file_no, title, receiver, paragraphs): # 1. 发文机关标识红色二号宋体加粗居中 p doc.add_paragraph() p.alignment WD_ALIGN_PARAGRAPH.CENTER run p.add_run(org_name) run.font.size Pt(22) run.font.bold True run.font.color.rgb RGBColor(0xFF, 0x00, 0x00) set_zh_font(run, 宋体) # 2. 发文字号三号仿宋居中 p doc.add_paragraph() p.alignment WD_ALIGN_PARAGRAPH.CENTER run p.add_run(file_no) run.font.size Pt(16) set_zh_font(run, 仿宋_GB2312) # 3. 红色反线 add_red_line_paragraph(doc) # 4. 空 2 行后写标题 doc.add_paragraph() doc.add_paragraph() p doc.add_paragraph() p.alignment WD_ALIGN_PARAGRAPH.CENTER run p.add_run(title) run.font.size Pt(22) run.font.bold True set_zh_font(run, 宋体) # 5. 主送机关顶格 p doc.add_paragraph() run p.add_run(receiver) run.font.size Pt(16) set_zh_font(run, 仿宋_GB2312) # 6. 正文段落 for text in paragraphs: p doc.add_paragraph(text) set_first_line_indent_chars(p, 2)set_zh_font是一个统一封装同时设置font.name和w:eastAsia。所有字体设置都通过这个函数走就不会出现「西文字体改了、中文字体没改」的隐患。这个骨架没有包含附注、落款、主题词等尾部要素实际使用时照着第 2、3 章的参数继续追加段落即可。提示把 4 个空参数org_name / file_no / title / paragraphs设计成函数入参后续接入 OA 系统或直接读取数据库生成公文时只需要把数据映射到这几个参数上不用改版式代码。5.2 生成后用脚本验证版式是否写入成功生成完 docx 后肉眼检查容易漏项。由于 docx 本质是 zip 压缩包可以直接用脚本读回样式验证字体和段落属性是否真的写入了from docx import Document from docx.oxml.ns import qn d Document(redhead_output.docx) # 检查正文字体是否为仿宋_GB2312 normal d.styles[Normal] rpr normal.element.rPr if rpr is not None and rpr.rFonts is not None: east_asia rpr.rFonts.get(qn(w:eastAsia)) print(正文中文字体:, east_asia) assert east_asia 仿宋_GB2312, 字体设置丢失 # 检查行距是否为固定值 28 磅 from docx.enum.text import WD_LINE_SPACING ls normal.paragraph_format.line_spacing lsr normal.paragraph_format.line_spacing_rule print(行距值:, ls, 行距规则:, lsr)这一步能快速发现两类问题一是Normal样式的字体没生效多是因为只设置了font.name而漏掉了w:eastAsia二是在 WPS 和 Word 之间来回打开后固定行距被改回了单倍行距脚本读取到的line_spacing_rule不再是EXACTLY。遇到这种情况说明文件被某个中间环节重写了样式需要重新套用模板而不是在现有文件上继续改。把这套检查和生成逻辑放到一起后续每次出文都先跑脚本、再打开 Word 确认红线位置和落款缩进红头文件的版式问题就能在提交前被拦下来。本文还有配套的精品资源点击获取