ppt function to replace images
This commit is contained in:
83
utils.py
83
utils.py
@@ -8,6 +8,89 @@ from plots import JPMCPlotsMixin
|
||||
|
||||
import marimo as mo
|
||||
|
||||
from pptx import Presentation
|
||||
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
||||
|
||||
def pptx_replace_named_image(presentation_path, target_tag, new_image_path, save_path):
|
||||
"""
|
||||
Finds and replaces specific images in a PowerPoint presentation while
|
||||
preserving their original position, size, and aspect ratio.
|
||||
|
||||
This function performs a 'surgical' replacement: it records the coordinates
|
||||
of the existing image, removes it from the slide's XML, and inserts a
|
||||
new image into the exact same bounding box. It identifies the target
|
||||
image by searching for a specific string within the Shape Name
|
||||
(Selection Pane) or Alt Text.
|
||||
|
||||
Args:
|
||||
presentation_path (str): The file path to the source .pptx file.
|
||||
target_tag (str): The unique identifier to look for (e.g., 'HERO_IMAGE').
|
||||
This is case-sensitive and checks both the shape name and alt text.
|
||||
new_image_path (str): The file path to the new image (PNG, JPG, etc.).
|
||||
save_path (str): The file path where the modified presentation will be saved.
|
||||
|
||||
Returns:
|
||||
None: Saves the file directly to the provided save_path.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the source presentation or new image is not found.
|
||||
PermissionError: If the save_path is currently open or locked.
|
||||
"""
|
||||
prs = Presentation(presentation_path)
|
||||
|
||||
for i, slide in enumerate(prs.slides):
|
||||
# Iterate over a list copy of shapes to safely modify the slide during iteration
|
||||
print(f"Processing Slide {i + 1}...")
|
||||
print(f"Total Shapes: {len(slide.shapes)} shapes")
|
||||
|
||||
for shape in list(slide.shapes):
|
||||
print(f"Checking shape: {shape.name} of type {shape.shape_type}...")
|
||||
|
||||
shape_name = shape.name or ""
|
||||
alt_text = ""
|
||||
|
||||
# More robust strategy: Check for alt text in ANY valid element property
|
||||
# This allows replacing Pictures, Placeholders, GraphicFrames, etc.
|
||||
try:
|
||||
# Check for common property names used by python-pptx elements to store non-visual props
|
||||
# nvPicPr (Picture), nvSpPr (Shape/Placeholder), nvGrpSpPr (Group),
|
||||
# nvGraphicFramePr (GraphicFrame), nvCxnSpPr (Connector)
|
||||
nvPr = None
|
||||
for attr in ['nvPicPr', 'nvSpPr', 'nvGrpSpPr', 'nvGraphicFramePr', 'nvCxnSpPr']:
|
||||
if hasattr(shape._element, attr):
|
||||
nvPr = getattr(shape._element, attr)
|
||||
break
|
||||
|
||||
if nvPr is not None and hasattr(nvPr, 'cNvPr'):
|
||||
alt_text = nvPr.cNvPr.get("descr", "")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print(f"Alt Text for shape '{shape_name}': {alt_text}")
|
||||
|
||||
if target_tag in shape_name or target_tag in alt_text:
|
||||
print(f"Found it! Replacing {shape_name}...")
|
||||
|
||||
try:
|
||||
# Record coordinates
|
||||
left, top, width, height = shape.left, shape.top, shape.width, shape.height
|
||||
|
||||
# Remove old shape
|
||||
old_element = shape._element
|
||||
old_element.getparent().remove(old_element)
|
||||
|
||||
# Add new image at the same spot
|
||||
slide.shapes.add_picture(str(new_image_path), left, top, width, height)
|
||||
except AttributeError:
|
||||
print(f"Could not replace {shape_name} - might be missing dimensions.")
|
||||
|
||||
else:
|
||||
print(f"Skipping shape '{shape_name}' with alt text '{alt_text}'")
|
||||
|
||||
prs.save(save_path)
|
||||
print(f"Successfully saved to {save_path}")
|
||||
|
||||
|
||||
def extract_voice_label(html_str: str) -> str:
|
||||
"""
|
||||
Extract voice label from HTML string and convert to short format.
|
||||
|
||||
Reference in New Issue
Block a user