写一个测试脚本(批量处理PPT文件母版里面的logo及文本)
写一个测试脚本(批量处理PPT文件母版里面的logo及文本),仅供测试学习
检测母版图片信息
import os
from pptx import Presentation
# ================== 配置参数 ==================
DEBUG_MODE = True # 调试模式开关
EMU_TO_PT = 12700 # 1磅 = 12700 EMU
# ================== 单位转换 ==================
def emu_to_pt(emu):
"""EMU转磅"""
return emu / EMU_TO_PT
def analyze_shapes(shape, slide_width_pt, indent=0):
"""递归分析形状信息"""
shape_info = []
# 基础信息
indent_str = " " * indent
info = (
f"{indent_str}形状: {shape.name or '无名'}, "
f"类型={shape.shape_type}, "
f"位置=({emu_to_pt(shape.left):.1f},{emu_to_pt(shape.top):.1f})磅, "
f"大小={emu_to_pt(shape.width):.1f}x{emu_to_pt(shape.height):.1f}磅"
)
# 额外信息
if shape.shape_type == 13: # 图片
info += " [图片]"
elif shape.has_text_frame:
info += f", 文本='{shape.text[:30]}...'"
shape_info.append(info)
# 递归处理组合形状
if shape.shape_type == 6: # 组合形状
for sub_shape in shape.shapes:
shape_info.extend(analyze_shapes(sub_shape, slide_width_pt, indent+1))
return shape_info
# ================== 文件处理 ==================
def analyze_ppt_file(ppt_path):
try:
prs = Presentation(ppt_path)
slide_width_pt = emu_to_pt(prs.slide_width)
print(f"\n🔍 分析文件: {ppt_path}")
print(f"幻灯片尺寸: {slide_width_pt:.1f}x{emu_to_pt(prs.slide_height):.1f}磅")
for i, slide_master in enumerate(prs.slide_masters, 1):
print(f"\n---- 母版 {i} '{slide_master.name}' ----")
# 分析母版本身
print("[母版形状]")
for shape in slide_master.shapes:
for line in analyze_shapes(shape, slide_width_pt):
print(line)
# 分析所有版式
for j, layout in enumerate(slide_master.slide_layouts, 1):
print(f"\n -- 版式 {j} '{layout.name}' --")
for shape in layout.shapes:
for line in analyze_shapes(shape, slide_width_pt, 1):
print(line)
return True
except Exception as e:
print(f"❌ 分析失败: {ppt_path} - {str(e)}")
return False
# ================== 主程序 ==================
def main():
root_dir = r"F:\04\1\11"
if not os.path.exists(root_dir):
print(f"❌ 目录不存在: {root_dir}")
return
print(f"🔍 开始扫描: {root_dir}")
ppt_files = []
for root, _, files in os.walk(root_dir):
for file in files:
if file.lower().endswith(('.pptx', '.ppt')):
ppt_files.append(os.path.join(root, file))
if not ppt_files:
print("⚠️ 未找到PPT文件")
return
print(f"📂 找到 {len(ppt_files)} 个文件")
for i, file_path in enumerate(ppt_files, 1):
print(f"\n[{i}/{len(ppt_files)}] ==========================")
analyze_ppt_file(file_path)
if __name__ == "__main__":
main()
处理代码:
import os
from pptx import Presentation
# ================= 配置参数 =================
DEBUG_MODE = False # 设置为 True 可打印调试信息
EMU_TO_PT = 12700 # 1磅 = 12700 EMU
# 删除右上角 LOGO 图片的范围(单位:pt)
SLIDE_WIDTH_PT = 720.0 # 幻灯片宽度
TOP_LIMIT_PT = 150.0 # 距离顶部 ≤ 150 pt(≈200px)
RIGHT_LIMIT_PT = 150.0 # 距离右侧 ≤ 150 pt(≈200px)
# 图片尺寸约束(防止误删)
MAX_IMAGE_WIDTH_PT = 100.0
MAX_IMAGE_HEIGHT_PT = 100.0
# 删除中间文本框的区域(单位:pt)
TEXT_REGION = {
"top": 100,
"left": 100,
"right": 650,
"bottom": 400
}
# =============== 删除逻辑函数 ===============
def is_target_image(shape):
"""判断是否为右上角图片"""
left_pt = shape.left / EMU_TO_PT
top_pt = shape.top / EMU_TO_PT
width_pt = shape.width / EMU_TO_PT
height_pt = shape.height / EMU_TO_PT
# 是否在右上角区域内
is_top_area = top_pt <= TOP_LIMIT_PT
is_right_area = left_pt >= SLIDE_WIDTH_PT - RIGHT_LIMIT_PT
is_reasonable_size = width_pt <= MAX_IMAGE_WIDTH_PT and height_pt <= MAX_IMAGE_HEIGHT_PT
return is_top_area and is_right_area and is_reasonable_size
def should_delete_text(shape):
"""判断是否为需要删除的文本框"""
left_pt = shape.left / EMU_TO_PT
top_pt = shape.top / EMU_TO_PT
return (
TEXT_REGION["left"] <= left_pt <= TEXT_REGION["right"] and
TEXT_REGION["top"] <= top_pt <= TEXT_REGION["bottom"]
)
def process_shape(shape, shapes_to_remove):
"""递归处理形状"""
if shape.shape_type == 6: # GROUP
for sub_shape in shape.shapes:
process_shape(sub_shape, shapes_to_remove)
elif shape.shape_type == 13: # PICTURE
if is_target_image(shape):
print(f"[匹配图片] {shape.name} at ({shape.left / EMU_TO_PT:.1f},{shape.top / EMU_TO_PT:.1f})")
shapes_to_remove.append(shape)
elif shape.has_text_frame:
if should_delete_text(shape):
print(f"[匹配文本] {shape.name} at ({shape.left / EMU_TO_PT:.1f},{shape.top / EMU_TO_PT:.1f}) 文本: {shape.text.strip()[:30]}")
shapes_to_remove.append(shape)
def process_shape_container(container, label=""):
shapes_to_remove = []
for shape in container.shapes:
process_shape(shape, shapes_to_remove)
for shape in shapes_to_remove:
print(f"✅ 删除: {shape.name} from {label}")
shape._element.getparent().remove(shape._element)
return bool(shapes_to_remove)
# =============== 处理单个PPT文件 ===============
def process_ppt_file(ppt_path):
try:
prs = Presentation(ppt_path)
if DEBUG_MODE:
print_all_pictures(prs)
modified = False
for slide_master in prs.slide_masters:
if process_shape_container(slide_master, "母版"):
modified = True
for layout in slide_master.slide_layouts:
if process_shape_container(layout, f"布局: {layout.name}"):
modified = True
if modified:
prs.save(ppt_path)
print(f"💾 保存成功: {ppt_path}")
return True
else:
print("❕ 未匹配任何内容")
return False
except Exception as e:
print(f"❌ 处理失败: {ppt_path} - {e}")
return False
# =============== 可选调试:输出所有图片 ===============
def print_all_pictures(prs):
print("📷 所有图片信息(单位:pt):")
for slide_master in prs.slide_masters:
print(f"--- 母版 ---")
for shape in slide_master.shapes:
print_shape_debug(shape)
for layout in slide_master.slide_layouts:
print(f"--- 布局: {layout.name} ---")
for shape in layout.shapes:
print_shape_debug(shape)
def print_shape_debug(shape):
if shape.shape_type == 13: # PICTURE
left = shape.left / EMU_TO_PT
top = shape.top / EMU_TO_PT
width = shape.width / EMU_TO_PT
height = shape.height / EMU_TO_PT
print(f"图片: {shape.name}, 位置=({left:.2f}, {top:.2f}), 大小=({width:.2f}, {height:.2f})")
elif shape.shape_type == 6:
for sub_shape in shape.shapes:
print_shape_debug(sub_shape)
# =============== 主程序入口 ===============
def main():
root_dir = r"F:\04\6\1" # 修改为你的目录路径
if not os.path.exists(root_dir):
print(f"❌ 目录不存在: {root_dir}")
return
print(f"🔍 开始扫描目录: {root_dir}")
ppt_files = []
for root, _, files in os.walk(root_dir):
for file in files:
if file.lower().endswith('.pptx'):
ppt_files.append(os.path.join(root, file))
if not ppt_files:
print("❗ 未找到任何 PPTX 文件")
return
print(f"✅ 共发现 {len(ppt_files)} 个 PPTX 文件")
modified_count = 0
for idx, file_path in enumerate(ppt_files, 1):
print(f"\n[{idx}/{len(ppt_files)}] 处理: {file_path}")
if process_ppt_file(file_path):
modified_count += 1
print(f"\n🎉 完成!共修改 {modified_count}/{len(ppt_files)} 个文件")
if __name__ == "__main__":
main()
进阶版(删除右上角logo、居中文本、居中图片)
import os
from pptx import Presentation
# ================= 配置参数 =================
DEBUG_MODE = False # 设置为 True 可打印调试信息
EMU_TO_PT = 12700 # 1磅 = 12700 EMU
# 删除右上角 LOGO 图片的范围(单位:pt)
SLIDE_WIDTH_PT = 720.0 # 幻灯片宽度
TOP_LIMIT_PT = 150.0 # 距离顶部 ≤ 150 pt(≈200px)
RIGHT_LIMIT_PT = 150.0 # 距离右侧 ≤ 150 pt(≈200px)
# 图片尺寸约束(防止误删)
MAX_IMAGE_WIDTH_PT = 100.0
MAX_IMAGE_HEIGHT_PT = 100.0
# 删除中间文本框的区域(单位:pt)
TEXT_REGION = {
"top": 100,
"left": 100,
"right": 650,
"bottom": 400
}
# =============== 删除逻辑函数 ===============
def is_target_image(shape):
left_pt = shape.left / EMU_TO_PT
top_pt = shape.top / EMU_TO_PT
width_pt = shape.width / EMU_TO_PT
height_pt = shape.height / EMU_TO_PT
# 删除图片1:右上角 LOGO 区域
is_top_area = top_pt <= TOP_LIMIT_PT
is_right_area = left_pt >= SLIDE_WIDTH_PT - RIGHT_LIMIT_PT
is_reasonable_size = width_pt <= MAX_IMAGE_WIDTH_PT and height_pt <= MAX_IMAGE_HEIGHT_PT
is_logo_image = is_top_area and is_right_area and is_reasonable_size
# 删除图片2:中间大图(位置 + 尺寸 精准匹配)
is_center_image = (
abs(left_pt - 236.6) <= 2.0 and
abs(top_pt - 108.0) <= 2.0 and
abs(width_pt - 246.8) <= 2.0 and
abs(height_pt - 189.0) <= 2.0
)
return is_logo_image or is_center_image
def should_delete_text(shape):
"""判断是否为需要删除的文本框"""
left_pt = shape.left / EMU_TO_PT
top_pt = shape.top / EMU_TO_PT
return (
TEXT_REGION["left"] <= left_pt <= TEXT_REGION["right"] and
TEXT_REGION["top"] <= top_pt <= TEXT_REGION["bottom"]
)
def process_shape(shape, shapes_to_remove):
"""递归处理形状"""
if shape.shape_type == 6: # GROUP
for sub_shape in shape.shapes:
process_shape(sub_shape, shapes_to_remove)
elif shape.shape_type == 13: # PICTURE
if is_target_image(shape):
print(f"[匹配图片] {shape.name} at ({shape.left / EMU_TO_PT:.1f},{shape.top / EMU_TO_PT:.1f})")
shapes_to_remove.append(shape)
elif shape.has_text_frame:
if should_delete_text(shape):
print(f"[匹配文本] {shape.name} at ({shape.left / EMU_TO_PT:.1f},{shape.top / EMU_TO_PT:.1f}) 文本: {shape.text.strip()[:30]}")
shapes_to_remove.append(shape)
def process_shape_container(container, label=""):
shapes_to_remove = []
for shape in container.shapes:
process_shape(shape, shapes_to_remove)
for shape in shapes_to_remove:
print(f"✅ 删除: {shape.name} from {label}")
shape._element.getparent().remove(shape._element)
return bool(shapes_to_remove)
# =============== 处理单个PPT文件 ===============
def process_ppt_file(ppt_path):
try:
prs = Presentation(ppt_path)
if DEBUG_MODE:
print_all_pictures(prs)
modified = False
for slide_master in prs.slide_masters:
if process_shape_container(slide_master, "母版"):
modified = True
for layout in slide_master.slide_layouts:
if process_shape_container(layout, f"布局: {layout.name}"):
modified = True
if modified:
prs.save(ppt_path)
print(f"💾 保存成功: {ppt_path}")
return True
else:
print("❕ 未匹配任何内容")
return False
except Exception as e:
print(f"❌ 处理失败: {ppt_path} - {e}")
return False
# =============== 可选调试:输出所有图片 ===============
def print_all_pictures(prs):
print("📷 所有图片信息(单位:pt):")
for slide_master in prs.slide_masters:
print(f"--- 母版 ---")
for shape in slide_master.shapes:
print_shape_debug(shape)
for layout in slide_master.slide_layouts:
print(f"--- 布局: {layout.name} ---")
for shape in layout.shapes:
print_shape_debug(shape)
def print_shape_debug(shape):
if shape.shape_type == 13: # PICTURE
left = shape.left / EMU_TO_PT
top = shape.top / EMU_TO_PT
width = shape.width / EMU_TO_PT
height = shape.height / EMU_TO_PT
print(f"图片: {shape.name}, 位置=({left:.2f}, {top:.2f}), 大小=({width:.2f}, {height:.2f})")
elif shape.shape_type == 6:
for sub_shape in shape.shapes:
print_shape_debug(sub_shape)
# =============== 主程序入口 ===============
def main():
root_dir = r"F:\04\6\1" # 修改为你的目录路径
if not os.path.exists(root_dir):
print(f"❌ 目录不存在: {root_dir}")
return
print(f"🔍 开始扫描目录: {root_dir}")
ppt_files = []
for root, _, files in os.walk(root_dir):
for file in files:
if file.lower().endswith('.pptx'):
ppt_files.append(os.path.join(root, file))
if not ppt_files:
print("❗ 未找到任何 PPTX 文件")
return
print(f"✅ 共发现 {len(ppt_files)} 个 PPTX 文件")
modified_count = 0
for idx, file_path in enumerate(ppt_files, 1):
print(f"\n[{idx}/{len(ppt_files)}] 处理: {file_path}")
if process_ppt_file(file_path):
modified_count += 1
print(f"\n🎉 完成!共修改 {modified_count}/{len(ppt_files)} 个文件")
if __name__ == "__main__":
main()