scanny / python-pptx

Create Open XML PowerPoint documents in Python
MIT License
2.37k stars 512 forks source link

Replace text with formatting #684

Open cxin-william opened 3 years ago

cxin-william commented 3 years ago

Dear scanny,Thank you very much for your reply When I do a function, I encounter a problem. First of all, I scan the text in PPT, and then replace some text. In this process, I find that the font has become the default font. How can I keep the font style unchanged

def fill_text(prs, text_format, data):
    for i in text_format:
        idx, sh_idx, text = i['sl_idx'], i['sh_idx'], i['text']
        p = i['paragraph']
        pgh = prs.slides[idx].shapes[sh_idx].text_frame.paragraphs[p]
        for id, elm in enumerate(pgh._element.content_children):
            if id == 0:
              continue
            pgh._element.remove(elm)
        prs.slides[idx].shapes[sh_idx].text_frame.paragraphs[p].runs[
          0].text = SFormatter().vformat(text, [], data)
    return prs

I scanned out the text to be replaced in text_format, and the text in data

WX20210205-114755 WX20210205-114802

Originally posted by @cxin-william in https://github.com/scanny/python-pptx/issues/683#issuecomment-773724457

ntoxlut commented 3 years ago

I am also facing the same challenge when I am replacing text in a shape.

cxin-william commented 2 years ago

I am also facing the same challenge when I am replacing text in a shape.

Did you solve it?

MartinPacker commented 2 years ago

I might not be understanding the code but the method remove suggests to me you are replacing the whole object. What you replace it with probably has the default font. How about just updating the text of the object rather than removing and replacing it?

cxin-william commented 2 years ago

I might not be understanding the code but the method remove suggests to me you are replacing the whole object. What you replace it with probably has the default font. How about just updating the text of the object rather than removing and replacing it?

Do you have a good way to replace only text?

sharkykittens commented 2 years ago

In case anyone in future needs this, this seems to work for me. Reverses the strings while keeping formatting.

presentation = pptx.Presentation("test.pptx")

for slide_count,slide in enumerate(presentation.slides):
    print("Slide: {}".format(slide_count))
    for shape_count,shape in enumerate(slide.shapes):
        print("Shape: {}".format(shape_count))
        if shape.has_text_frame and shape.text:

            for para_count,pgh in enumerate(shape.text_frame.paragraphs):
                #print(para_count)
                #print(pgh.runs)
                if pgh.runs:
                    for i in range(len(pgh.runs)):
                        #print(pgh.runs[i].font.size)
                        #print(pgh.runs[i].text)
                        pgh.runs[i].text = pgh.runs[i].text[::-1]
                        #print(pgh.runs[i].font.size)

presentation.save("test_out.pptx")
yana-xuyan commented 6 months ago

Inspired by @sharkykittens, maybe we can try the following code to replace the text without changing the format.

presentation = pptx.Presentation("test.pptx")

for slide_count,slide in enumerate(presentation.slides):
    print("Slide: {}".format(slide_count))
    for shape_count,shape in enumerate(slide.shapes):
        print("Shape: {}".format(shape_count))
        if shape.has_text_frame and shape.text:

            for para_count,pgh in enumerate(shape.text_frame.paragraphs):
                if pgh.runs:
                    for i in range(len(pgh.runs)):
                        pgh.runs[i].text = pgh.runs[i].text[:0] + "The text that you want to replace with."

presentation.save("test_out.pptx")
HReynaud commented 3 months ago

For anyone who stumbles upon this thread, here is handy function to replace text at the paragraph level while keeping the format. It assumes that the format is the same for all runs.

from copy import deepcopy

def replace_paragraph_text_retaining_initial_formatting(paragraph, new_text):
    font = deepcopy(paragraph.runs[0].font)
    paragraph.text = new_text
    for run in paragraph.runs:
        run._r.insert(0, deepcopy(font._rPr))

Inspired by scanny and sedrew.

I could not figure out a proper way to handle '\v' at the run level and this solved it.

TianzeWang commented 3 months ago

Can refer to this stackoverflow answer: https://stackoverflow.com/questions/45247042/how-to-keep-original-text-formatting-of-text-with-python-powerpoint?newreg=75b9ab8c12e747f596e2f0be1d66314e

So the basic idea is: Instead of replacing the "text" of the shape, replace the "text" in the run.