保留 read/write 上的文本格式以塑造文本 python pptx

Preserve text format on read/write to shape text python pptx

我希望在形状的文本中执行文本替换。我使用的代码类似于以下代码段:

# define key/value
SRKeys, SRVals = ['x','y','z'], [1,2,3]

# define text
text = shape.text

# iterate through values and perform subs
for i in range(len(SRKeys)):
    # replace text
    text = text.replace(SRKeys[i], str(SRVals[i]))

# write text subs to comment box
shape.text = text

但是,如果初始 shape.text 具有格式化字符(例如粗体),则在读取时删除格式。有解决办法吗?

我唯一能想到的就是遍历字符并检查格式,然后在写入 shape.text 之前添加这些格式。

@usr2564301 走在正确的轨道上。字符格式(又名 "font")在 运行 级别指定。这就是 运行 的含义; "run"(序列)字符都共享相同的字符格式。

当您分配给 shape.text 时,您会用一个具有默认格式的新 运行 替换所有曾经存在的 运行。如果你想保留格式,你需要保留任何 运行s 不直接参与文本替换。

这不是一个小问题,因为不能保证 运行s 会打破单词边界。尝试打印出几个段落的 运行s,我想你会明白我的意思。

在粗略的伪代码中,我认为这是您需要采用的方法:

  • 搜索段落中的目标文本以确定其第一个字符的偏移量。
  • 遍历段落中的所有 运行s,保留每个 运行 之前的字符总数 运行ning,可能类似于 (run_idx, prefix_len, 长度): (0, 0, 8), (1, 8, 4), (2, 12, 9), 等等
  • 确定哪个 运行 是涉及您的搜索字符串的开始、结束和中间 运行。
  • 拆分搜索词开头的第一个 运行,拆分搜索词结尾的最后一个 运行,并删除 "middle" 中除第一个以外的所有内容运行s.
  • 更改中间的文本 运行 替换文本并从之前的(原始开始)克隆格式 运行。也许这最后一点是你在分段开始时做的。

这将保留任何不涉及搜索字符串的 运行,并保留 "replaced" 字中 "matched" 字的格式。

这需要一些当前 API 不直接支持的操作。对于那些你需要使用较低级别的 lxml 调用来直接操作 XML,尽管你可以从 python-pptx 对象中获取你需要的所有现有元素,而无需自己解析 XML。

这是我正在使用的代码的改编版本(灵感来自@scanny 的回答)。它替换幻灯片上所有形状(带文本框)的文本。

from pptx import Presentation

prs = Presentation('../../test.pptx')
slide = prs.slides[1]

# iterate through all shapes on slide
for shape in slide.shapes:
    if not shape.has_text_frame:
        continue
        
    # iterate through paragarphs in shape
    for p in shape.text_frame.paragraphs:
        # store formats and their runs by index (not dict because of duplicate runs)
        formats, newRuns = [], []

        # iterate through runs
        for r in p.runs:
            # get text
            text = r.text

            # replace text
            text = text.replace('s','xyz')

            # store run
            newRuns.append(text)

            # store format
            formats.append({'size':r.font.size,
                            'bold':r.font.bold,
                            'underline':r.font.underline,
                            'italic':r.font.italic})

        # clear paragraph
        p.clear()

        # iterate through new runs and formats and write to paragraph
        for i in range(len(newRuns)):
            # add run with text
            run = p.add_run()
            run.text = newRuns[i]

            # format run
            run.font.bold = formats[i]['bold']
            run.font.italic = formats[i]['italic']
            run.font.size = formats[i]['size']
            run.font.underline = formats[i]['underline']

prs.save('../../test.pptx')