You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

python-docx多段落格式设置时旧格式被覆盖问题咨询

问题:python-docx设置新段落格式时旧段落格式被覆盖

我尝试使用Python的python-docx模块批量修改Word文档段落格式,编写了如下代码:

from docx import Document
from docx.shared import Pt
from docx.shared import Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.enum.section import WD_ORIENTATION
from content import report_content, provinces, report_date, introduction, intro_content

alignment_dict = {'justify': WD_PARAGRAPH_ALIGNMENT.JUSTIFY, 'center': WD_PARAGRAPH_ALIGNMENT.CENTER, 'centre': WD_PARAGRAPH_ALIGNMENT.CENTER, 'right': WD_PARAGRAPH_ALIGNMENT.RIGHT, 'left': WD_PARAGRAPH_ALIGNMENT.LEFT}
orientation_dict = {'portrait': WD_ORIENTATION.PORTRAIT, 'landscape': WD_ORIENTATION.LANDSCAPE}

document = Document()

def change_orientation(orientation='portrait', set_left_margin=1.0, set_right_margin=1.0):
    section = document.sections[-1]
    new_width, new_height = section.page_height, section.page_width
    section.orientation = orientation_dict[orientation]
    section.page_width = new_width
    section.page_height = new_height
    section.left_margin = Inches(set_left_margin)
    section.right_margin = Inches(set_right_margin)

def add_logo(path, align):
    document.add_picture(path, width=Inches(4.5), height=Inches(1.5))
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = alignment_dict[align]

def add_content(content, space_after, font_name='Arial', font_size=11, line_spacing=0, space_before=0, align='justify', keep_together=True, keep_with_next=False, page_break_before=False, widow_control=False, set_bold=False, set_italic=False, set_underline=False, set_all_caps=False):
    paragraph = document.add_paragraph(content)
    style = document.styles['Normal']
    font = style.font
    font.name = font_name
    font.size = Pt(font_size)
    font.bold = set_bold
    font.italic = set_italic
    font.all_caps = set_all_caps
    font.underline = set_underline
    paragraph_format = paragraph.paragraph_format
    paragraph_format.alignment = alignment_dict.get(align.lower())
    paragraph_format.space_before = Pt(space_before)
    paragraph_format.space_after = Pt(space_after)
    paragraph_format.line_spacing = line_spacing
    paragraph_format.keep_together = keep_together
    paragraph_format.keep_with_next = keep_with_next
    paragraph_format.page_break_before = page_break_before
    paragraph_format.widow_control = widow_control

def create_numbered_list():
    pass

def add_subheading(subheading, level):
    document.add_heading(subheading, level)

change_orientation(orientation='landscape', set_left_margin=0.5, set_right_margin=0.5)
add_logo('logo.png', 'center')
add_content(report_content, align='Center', space_before=40, space_after=20, line_spacing=1, font_name='Arial', set_bold=True, set_all_caps=True)
add_content(provinces, align='Center', space_before=20, space_after=20, line_spacing=1, font_name='Arial', set_bold=True, set_all_caps=True)
add_content(report_date, align='Center', space_before=20, space_after=20, line_spacing=1, font_name='Arial', set_bold=True, set_all_caps=True)
document.add_page_break()
add_subheading(introduction, level=1)
add_content(intro_content, space_after=20, space_before=20)
document.save('demo.docx')

但遇到了一个问题:每次调用add_content方法设置新段落的格式时,之前添加的旧段落格式都会被修改成当前段落的格式,没办法保留原有格式。请问这个问题的原因是什么?


解答

这个问题我之前也碰到过,核心原因其实很明确:你在add_content函数里直接修改了文档的全局Normal样式

看这段关键代码:

style = document.styles['Normal']
font = style.font
font.name = font_name
font.size = Pt(font_size)
# ... 其他字体属性修改

Word文档里所有默认创建的段落,都是基于Normal这个全局样式的。当你修改这个样式的字体、字号、加粗等属性时,所有使用该样式的段落——包括之前已经添加的旧段落——都会自动同步更新这些属性,这就导致了旧段落格式被覆盖的问题。

给你两种解决方案,按需选择:

1. 直接修改段落的Run属性(适合简单场景)

不需要碰全局样式,直接操作新添加段落的Run对象,这样只会影响当前段落:

def add_content(content, space_after, font_name='Arial', font_size=11, line_spacing=0, space_before=0, align='justify', keep_together=True, keep_with_next=False, page_break_before=False, widow_control=False, set_bold=False, set_italic=False, set_underline=False, set_all_caps=False):
    paragraph = document.add_paragraph(content)
    # 直接修改当前段落的run字体属性
    run = paragraph.runs[0]
    run.font.name = font_name
    run.font.size = Pt(font_size)
    run.font.bold = set_bold
    run.font.italic = set_italic
    run.font.all_caps = set_all_caps
    run.font.underline = set_underline
    # 段落格式设置保持不变
    paragraph_format = paragraph.paragraph_format
    paragraph_format.alignment = alignment_dict.get(align.lower())
    paragraph_format.space_before = Pt(space_before)
    paragraph_format.space_after = Pt(space_after)
    paragraph_format.line_spacing = line_spacing
    paragraph_format.keep_together = keep_together
    paragraph_format.keep_with_next = keep_with_next
    paragraph_format.page_break_before = page_break_before
    paragraph_format.widow_control = widow_control

2. 创建自定义样式(适合需要重复复用格式的场景)

如果有多个段落需要用相同格式,推荐创建专属的自定义样式,既不会影响全局Normal样式,还能统一管理格式:

def add_content(content, space_after, font_name='Arial', font_size=11, line_spacing=0, space_before=0, align='justify', keep_together=True, keep_with_next=False, page_break_before=False, widow_control=False, set_bold=False, set_italic=False, set_underline=False, set_all_caps=False):
    # 生成唯一的样式名称,避免重复创建
    style_name = f"CustomParaStyle_{font_name}_{font_size}_{set_bold}_{set_italic}"
    # 检查样式是否已存在,不存在则创建
    if style_name not in document.styles:
        style = document.styles.add_style(style_name, 1)  # 1代表段落样式类型
        # 设置字体属性到自定义样式
        font = style.font
        font.name = font_name
        font.size = Pt(font_size)
        font.bold = set_bold
        font.italic = set_italic
        font.all_caps = set_all_caps
        font.underline = set_underline
        # 设置段落格式到自定义样式
        para_style = style.paragraph_format
        para_style.alignment = alignment_dict.get(align.lower())
        para_style.space_before = Pt(space_before)
        para_style.space_after = Pt(space_after)
        para_style.line_spacing = line_spacing
        para_style.keep_together = keep_together
        para_style.keep_with_next = keep_with_next
        para_style.page_break_before = page_break_before
        para_style.widow_control = widow_control
    else:
        style = document.styles[style_name]
    # 使用自定义样式添加段落
    paragraph = document.add_paragraph(content, style=style_name)

这样修改后,每个段落的格式就会独立保存,不会再互相影响了。


内容的提问来源于stack exchange,提问作者Arnab

火山引擎 最新活动