Search code examples
pythonpython-pptx

How to copy a slide with images using python pptx?


My end goal is to change the theme of a presentation. To do this, I have created a source template and a new template (with the correct theme). I iterate over each slide in the source template then add a new slide to the new template with the contents of the source using the code from https://stackoverflow.com/a/56074651/3206926. If there is a better way to do this I'd love to hear it.

This works great for text and text boxes, however the test image cannot be displayed in the new powerpoint (show in the image below):

enter image description here

Code

def copy_slide_from_external_prs(self, src, idx, newPrs):

    # specify the slide you want to copy the contents from
    src_slide = src.slides[idx]

    # Define the layout you want to use from your generated pptx
    slide_layout = newPrs.slide_layouts[2]

    # create now slide, to copy contents to
    curr_slide = newPrs.slides.add_slide(slide_layout)


    # remove placeholders
    for p in [s.element for s in curr_slide.shapes if 'Text Placeholder' in s.name or 'Title' in s.name]:
        p.getparent().remove(p)

    # now copy contents from external slide, but do not copy slide properties
    # e.g. slide layouts, etc., because these would produce errors, as diplicate
    # entries might be generated
    for shp in src_slide.shapes:
        el = shp.element
        newel = copy.deepcopy(el)

        curr_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
    
    return newPrs

I was trying many different solutions and tried creating a new Picture using the image.blob property in the source image. However, then the image does not have an element. Do I need to convert the blob to a PNG, save it, then create a new image using that saved PNG?

There must be a better way to do this. Again, I just want to change the theme.


Solution

  • Here is the workaround I developed. I first check if the shape is an image, and if it is, I write the image to a local directory. Then I add a picture to the slide using that saved image. Finally, I delete the locally saved image.

    Now this copy_slide function works for images:

    def copy_slide_from_external_prs(src, idx, newPrs):
    
        # specify the slide you want to copy the contents from
        src_slide = src.slides[idx]
    
        # Define the layout you want to use from your generated pptx
        SLD_LAYOUT = 5
        slide_layout = prs.slide_layouts[SLD_LAYOUT]
    
        # create now slide, to copy contents to
        curr_slide = newPrs.slides.add_slide(slide_layout)
    
        # create images dict
        imgDict = {}
    
        # now copy contents from external slide, but do not copy slide properties
        # e.g. slide layouts, etc., because these would produce errors, as diplicate
        # entries might be generated
        for shp in src_slide.shapes:
            if 'Picture' in shp.name:
                # save image
                with open(shp.name+'.jpg', 'wb') as f:
                    f.write(shp.image.blob)
    
                # add image to dict
                imgDict[shp.name+'.jpg'] = [shp.left, shp.top, shp.width, shp.height]
            else:
                # create copy of elem
                el = shp.element
                newel = copy.deepcopy(el)
    
                # add elem to shape tree
                curr_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
    
        # add pictures
        for k, v in imgDict.items():
            curr_slide.shapes.add_picture(k, v[0], v[1], v[2], v[3])
            os.remove(k)