I’m trying to insert a picture that is re-sized to fit the dimensions of the picture placeholder from a template using python-pptx. I don’t believe the API has direct access to this from what I can find out in the docs. Is there any suggestion of how I might be able to do this, using the library or other?
I have a running code that will insert a series of images into a set of template slides to automatically create a report using Powerpoint.
Here is the function that is doing the majority of the work relevant. Other parts of the app are creating the Presentation and inserting a slide etc.
def insert_images(slide, slide_num, images_path, image_df): """ Insert images into a slide. :param slide: = slide object from Presentation class :param slide_num: the template slide number for formatting :param images_path: the directory to the folder with all the images :param image_df: Pandas data frame regarding information of each image in images_path :return: None """ placeholders = get_image_placeholders(slide) #print(placeholders) image_pool = image_df[image_df['slide_num'] == slide_num] try: assert len(placeholders) == len(image_pool.index) except AssertionError: print('Length of placeholders in slide does not match image naming.') i = 0 for idx, image in image_pool.iterrows(): #print(image) image_path = os.path.join(images_path, image.path) pic = slide.placeholders[placeholders[i]].insert_picture(image_path) #print(image.path) # TODO: Add resize - get dimensions of pic placeholder line = pic.line print(image['view']) if image['view'] == 'red': line.color.rgb = RGBColor(255, 0, 0) elif image['view'] == 'green': line.color.rgb = RGBColor(0, 255, 0) elif image['view'] == 'blue': line.color.rgb = RGBColor(0, 0, 255) else: line.color.rgb = RGBColor(0, 0, 0) line.width = Pt(2.25) i+=1
The issue is that when I insert a picture into the picture placeholder, the image is cropped, not re-sized. I don’t want the user to know the dimensions to hard code into my script. If the image used is relatively large it can crop a very large portion and just not be usable.
Advertisement
Answer
The picture object returned by PicturePlaceholder.insert_picture()
has the same position and size as the placeholder it derives from. It is cropped to completely fill that space. Either the tops and bottoms are cropped or the left and right sides, depending on the relative aspect ratio of the placeholder and the image you insert. This is the same behavior PowerPoint exhibits when you insert a picture into a picture placeholder.
If you want to remove the cropping, simply set all cropping values to 0:
picture = placeholder.insert_picture(...) picture.crop_top = 0 picture.crop_left = 0 picture.crop_bottom = 0 picture.crop_right = 0
This will not change the position (of the top-left corner) but will almost always change the size, making it either wider or taller (but not both).
So this solves the first problem easily, but of course presents you with a second one, which is how to position the picture where you want it and how to scale it appropriately without changing the aspect ratio (stretching or squeezing it).
This depends a great deal on what you’re trying to accomplish and what outcome you find most pleasing. This is why it is not automatic; it’s just not possible to predict.
You can find the “native” width and height of the image like this:
width, height = picture.image.size # ---width and height are int pixel-counts
From there you’ll need to compare aspect ratios of the original placeholder and the image you inserted and either adjust the width or height of the picture shape.
So say you wanted to keep the same position but maintain the width and height of the placeholder as respective maximums such that the entire picture fits in the space but has a “margin” either on the bottom or the right:
available_width = picture.width available_height = picture.height image_width, image_height = picture.image.size placeholder_aspect_ratio = float(available_width) / float(available_height) image_aspect_ratio = float(image_width) / float(image_height) # Get initial image placeholder left and top positions pos_left, pos_top = picture.left, picture.top picture.crop_top = 0 picture.crop_left = 0 picture.crop_bottom = 0 picture.crop_right = 0 # ---if the placeholder is "wider" in aspect, shrink the picture width while # ---maintaining the image aspect ratio if placeholder_aspect_ratio > image_aspect_ratio: picture.width = int(image_aspect_ratio * available_height) picture.height = available_height # ---otherwise shrink the height else: picture.height = int(available_width/image_aspect_ratio) picture.width = available_width # Set the picture left and top position to the initial placeholder one picture.left, picture.top = pos_left, pos_top # Or if we want to center it vertically: # picture.top = picture.top + int(picture.height/2)
This could be elaborated to “center” the image within the original space and perhaps to use “negative cropping” to retain the original placeholder size.
I haven’t tested this and you might need to make some adjustments, but hopefully this gives you an idea how to proceed. This would be a good thing to extract to its own function, like adjust_picture_to_fit(picture)
.