python-pptx icon indicating copy to clipboard operation
python-pptx copied to clipboard

Replace text with formatting

Open cxin-william opened this issue 4 years ago • 8 comments

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

cxin-william avatar Feb 05 '21 04:02 cxin-william

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

ntoxlut avatar Jun 02 '21 21:06 ntoxlut

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

Did you solve it?

cxin-william avatar Feb 09 '22 01:02 cxin-william

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?

MartinPacker avatar Feb 09 '22 07:02 MartinPacker

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?

cxin-william avatar Feb 10 '22 02:02 cxin-william

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")

sharkykittens avatar Aug 03 '22 14:08 sharkykittens

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")

yana-xuyan avatar Feb 19 '24 11:02 yana-xuyan

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.

HReynaud avatar May 15 '24 23:05 HReynaud

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.

TianzeWang avatar May 19 '24 08:05 TianzeWang