利用Python自動化處理PPT樣式與結構的解決方案
引言
PowerPoint(PPT)是職場中常用的辦公工具,但手動設計和調(diào)整樣式往往耗時耗力。本文將介紹一套基于Python的自動化解決方案,通過代碼實現(xiàn)以下功能:
- 提取PPT樣式:將PPT的文本格式、顏色、布局等信息保存為JSON文件。
- 應用樣式到模板:根據(jù)JSON定義的樣式生成新PPT。
- 幻燈片增刪與復制:靈活調(diào)整PPT結構,滿足動態(tài)內(nèi)容需求。
代碼功能概述
核心功能模塊
樣式提取與保存
- 函數(shù):
extract_ppt_with_style(ppt_path, output_json)
- 功能:遍歷PPT的每一頁,提取文本框的字體、顏色、段落對齊方式等樣式信息,并保存為結構化的JSON文件。
- 函數(shù):
樣式應用與生成
- 函數(shù):
apply_styles_to_ppt(template_path, json_path, output_pptx, data_json_llm)
- 功能:根據(jù)JSON定義的樣式,將文本內(nèi)容和格式應用到指定模板,生成符合要求的新PPT。
- 函數(shù):
幻燈片結構管理
- 函數(shù):
copy_slide_and_insert_after
、delete_slide
、copy_ppt
- 功能:復制、刪除幻燈片,并根據(jù)需求動態(tài)調(diào)整中間頁的數(shù)量(如擴展或壓縮內(nèi)容頁)。
- 函數(shù):
代碼詳解
1. 提取PPT樣式(extract_ppt_with_style)
def extract_ppt_with_style(ppt_path, output_json): prs = Presentation(ppt_path) data = [] for slide_idx, slide in enumerate(prs.slides): slide_data = { "slide_number": slide_idx + 1, "shapes": [] } for shape in slide.shapes: if not shape.has_text_frame: continue # 跳過非文本形狀 text_frame = shape.text_frame text_info = { "shape_name": shape.name, "paragraphs": [] } for paragraph in text_frame.paragraphs: para_info = { "alignment": str(paragraph.alignment), "runs": [] } for run in paragraph.runs: run_info = { "text": run.text, "font": { "name": run.font.name, "size": str(run.font.size), "bold": run.font.bold, "italic": run.font.italic, "color": { "type": "theme" if run.font.color.type == MSO_THEME_COLOR else "rgb", "theme_color": run.font.color.theme_color, "rgb": (run.font.color.rgb[0], run.font.color.rgb[1], run.font.color.rgb[2]) if run.font.color.rgb else None } } } para_info["runs"].append(run_info) text_info["paragraphs"].append(para_info) slide_data["shapes"].append(text_info) data.append(slide_data) # 保存并壓縮JSON with open(output_json, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) data = json_compress(data) with open("compress_" + output_json, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) return data
- 關鍵點:
- 樣式解析:記錄字體名稱、大小、粗體、斜體、顏色(主題色或RGB值)。
- 結構化存儲:JSON中每一頁(
slide
)包含多個形狀(shapes
),每個形狀包含段落(paragraphs
)和文本片段(runs
)。 - 壓縮優(yōu)化:
json_compress
函數(shù)簡化冗余數(shù)據(jù)(如通用形狀名稱),提升存儲效率。
2. 應用樣式生成PPT(apply_styles_to_ppt)
def apply_styles_to_ppt(template_path, json_path, output_pptx, data_json_llm): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) prs = Presentation(template_path) for slide_idx, slide in enumerate(prs.slides): for shape_idx, shape in enumerate(slide.shapes): if not shape.has_text_frame: continue text_frame = shape.text_frame for paragraph_idx, paragraph in enumerate(text_frame.paragraphs): for run_idx, run in enumerate(paragraph.runs): run_info = data[slide_idx]["shapes"][shape_idx]["paragraphs"][paragraph_idx]["runs"][run_idx] # 應用文本內(nèi)容 run.text = data_json_llm[slide_idx]["shapes"].pop()["paragraphs"] # 應用樣式 run.font.name = run_info["font"]["name"] run.font.bold = run_info["font"]["bold"] run.font.italic = run_info["font"]["italic"] # 處理顏色 color_data = run_info["font"]["color"] if color_data["type"] == "rgb": r, g, b = color_data["rgb"] run.font.color.rgb = RGBColor(r, g, b) elif color_data["type"] == "theme": theme_color = getattr(MSO_THEME_COLOR, color_data["theme_color"], MSO_THEME_COLOR.ACCENT_1) run.font.color.theme_color = theme_color prs.save(output_pptx)
- 關鍵點:
- 樣式復用:從JSON中讀取字體、顏色等信息,直接應用到模板PPT的對應位置。
- 動態(tài)內(nèi)容替換:通過
data_json_llm
參數(shù),可結合LLM(如GPT)生成的文本內(nèi)容動態(tài)填充PPT。
3. 幻燈片結構管理
3.1 復制與插入幻燈片
def copy_slide_and_insert_after(prs, source_index, target_index): source_slide = prs.slides[source_index] new_slide = prs.slides.add_slide(source_slide.slide_layout) # 復制形狀和關系 for shape in source_slide.shapes: new_el = deepcopy(shape.element) new_slide.shapes._spTree.insert_element_before(new_el, 'p:extLst') # 調(diào)整位置 slides = list(prs.slides._sldIdLst) new_slide_id = slides.pop() slides.insert(target_index + 1, new_slide_id) prs.slides._sldIdLst[:] = slides
- 功能:復制指定幻燈片并插入到目標位置后,保持布局和元素一致性。
3.2 刪除幻燈片
def delete_slide(prs, slide_index): if slide_index < 0 or slide_index >= len(prs.slides): print("無效的幻燈片索引") return xml_slides = list(prs.slides._sldIdLst) slides_id_to_delete = xml_slides[slide_index] prs.slides._sldIdLst.remove(slides_id_to_delete)
- 功能:通過移除幻燈片ID實現(xiàn)刪除,避免直接操作可能導致的格式錯誤。
3.3 動態(tài)擴展/壓縮PPT頁數(shù)
def copy_ppt(pages, template_path="template.pptx", modified_path="modified.pptx"): prs = Presentation(template_path) copy_pages = pages - 2 # 排除首尾固定頁 center_pages = len(prs.slides) - 2 if copy_pages < center_pages: # 刪除多余頁 for _ in range(center_pages - copy_pages): delete_slide(prs, len(prs.slides)-1) else: # 復制中間頁 n = (copy_pages // center_pages) * center_pages for _ in range(n): for i in range(1, center_pages+1): copy_slide_and_insert_after(prs, i, i) prs.save(modified_path)
- 應用場景:根據(jù)需求動態(tài)調(diào)整中間頁的數(shù)量(如擴展到5頁或壓縮到3頁),保持首尾頁固定。
使用示例
場景1:生成符合樣式的PPT
# 1. 提取原始PPT的樣式 extract_ppt_with_style("template.pptx", "output_styles.json") # 2. 生成新內(nèi)容(例如通過LLM) llm_json = [...] # LLM生成的文本內(nèi)容 # 3. 應用樣式生成最終PPT apply_styles_to_ppt("template.pptx", "output_styles.json", "new_ppt.pptx", llm_json)
場景2:動態(tài)調(diào)整PPT頁數(shù)
# 假設原始模板有5頁(首尾固定,中間3頁) copy_ppt(pages=7, template_path="template.pptx") # 最終生成7頁:1(首)+5(中間復制)+1(尾)
應用場景
- 企業(yè)報告自動化:根據(jù)數(shù)據(jù)動態(tài)生成季度報告,保持統(tǒng)一格式。
- 培訓材料生成:批量創(chuàng)建多套PPT,僅需調(diào)整中間內(nèi)容頁。
- 營銷素材管理:快速復制產(chǎn)品介紹模板,替換文本和樣式。
總結
本文提供的代碼庫實現(xiàn)了從PPT樣式提取、動態(tài)內(nèi)容生成到結構管理的全流程自動化。開發(fā)者可通過以下方式進一步優(yōu)化:
- 集成LLM:將文本生成部分與GPT等模型結合,實現(xiàn)從內(nèi)容到樣式的全自動化。
- 圖形處理:擴展對圖片、圖表樣式的解析與應用。
- 用戶界面:封裝為GUI工具,降低使用門檻。
通過這種方式,企業(yè)可大幅減少PPT制作時間,專注于內(nèi)容創(chuàng)新而非格式調(diào)整。
from pptx import Presentation from pptx.enum.dml import MSO_THEME_COLOR from pptx.dml.color import RGBColor from copy import deepcopy import json def extract_ppt_with_style(ppt_path, output_json): prs = Presentation(ppt_path) data = [] for slide_idx, slide in enumerate(prs.slides): slide_data = { "slide_number": slide_idx + 1, "shapes": [] } for shape in slide.shapes: if not shape.has_text_frame: continue # 跳過非文本形狀 text_frame = shape.text_frame text_info = { "shape_name": shape.name, "paragraphs": [] } for paragraph in text_frame.paragraphs: para_info = { "alignment": str(paragraph.alignment), "runs": [] } for run in paragraph.runs: run_info = { "text": run.text, "font": { "name": run.font.name, "size": str(run.font.size) if run.font.size else None, "bold": run.font.bold, "italic": run.font.italic, "color": { "type": "theme" if run.font.color.type == MSO_THEME_COLOR else "rgb", "theme_color": run.font.color.theme_color, "rgb": (run.font.color.rgb[0], run.font.color.rgb[1], run.font.color.rgb[2]) if run.font.color.rgb else None } }, # "highlight_color": str(run.highlight_color) # 修改:從 run 而非 run.font 獲取 } para_info["runs"].append(run_info) text_info["paragraphs"].append(para_info) slide_data["shapes"].append(text_info) data.append(slide_data) with open(output_json, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) data = json_compress(data) with open("compress" + "_" + output_json, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) return data def apply_styles_to_ppt(template_path, json_path, output_pptx, data_json_llm): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) prs = Presentation(template_path) for slide_idx, slide in enumerate(prs.slides): for shape_idx, shape in enumerate(slide.shapes): if not shape.has_text_frame: continue # 跳過非文本形狀 text_frame = shape.text_frame for paragraph_idx, paragraph in enumerate(text_frame.paragraphs): for run_idx, run in enumerate(paragraph.runs): run_info = data[slide_idx]["shapes"][shape_idx]["paragraphs"][paragraph_idx]["runs"][run_idx] text = data_json_llm[slide_idx]["shapes"].pop() # run.text = run_info["text"] run.text = text["paragraphs"] run.font.name = run_info["font"]["name"] # run.font.size = run_info["font"]["size"] run.font.bold = run_info["font"]["bold"] # run.font.size = run_info["font"]["size"] run.font.italic = run_info["font"]["italic"] # 假設 run_data 是從 JSON 中讀取的字典 color_data = run_info["font"]["color"] if color_data["type"] == "rgb": # 解析 RGB 值 r_str, g_str, b_str = color_data["rgb"] r = r_str g = g_str b = b_str run.font.color.rgb = RGBColor(r, g, b) elif color_data["type"] == "hex": # 解析十六進制顏色 hex_color = color_data["hex"].lstrip("#") r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16) run.font.color.rgb = RGBColor(r, g, b) elif color_data["type"] == "theme": # 使用主題顏色(如 MSO_THEME_COLOR.ACCENT_1) theme_color_name = color_data["theme_color"] theme_color = getattr(MSO_THEME_COLOR, theme_color_name, MSO_THEME_COLOR.ACCENT_1) run.font.color.theme_color = theme_color else: # 默認顏色(黑色) run.font.color.rgb = RGBColor(0, 0, 0) prs.save(output_pptx) def json_compress(json_data): for slide in json_data: for shape in slide["shapes"]: if "Shape" in shape["shape_name"]: shape["paragraphs"] = {} else: for paragraph in shape["paragraphs"]: for run in paragraph["runs"]: shape["paragraphs"] = run["text"] json_data_new = [] for slide in json_data: shapes = {"shapes": [], 'slide_number': slide['slide_number']} for shape in slide["shapes"]: if "Shape" in shape["shape_name"]: shape["paragraphs"] = {} else: shapes["shapes"].append(shape) json_data_new.append(shapes) return json_data_new def copy_slide_and_insert_after(prs, source_index, target_index): """ 復制源幻燈片并將其插入到目標幻燈片的后面。 :param prs: Presentation 對象 :param source_index: 源幻燈片的索引(從0開始) :param target_index: 目標幻燈片的索引(新幻燈片將插入到其后面) """ # 獲取源幻燈片 source_slide = prs.slides[source_index] # 創(chuàng)建新幻燈片(使用相同的布局) new_slide_layout = source_slide.slide_layout new_slide = prs.slides.add_slide(new_slide_layout) # 復制所有形狀(包括文本框、圖片、圖表等) for shape in source_slide.shapes: el = shape.element new_el = deepcopy(el) new_slide.shapes._spTree.insert_element_before(new_el, 'p:extLst') # 復制關系(如超鏈接、注釋等) for rel in source_slide.part.rels.values(): if "notesSlide" not in rel.reltype: # 排除注釋頁 # 使用 relate_to 方法而不是 add 方法 new_slide.part.relate_to( rel._target, rel.reltype ) # 調(diào)整幻燈片順序:將新幻燈片移動到目標位置的后面 slides = list(prs.slides._sldIdLst) new_position = target_index + 1 # 插入到目標幻燈片的后面 # 移除剛添加的新幻燈片(默認在最后) new_slide_id = slides.pop() # 插入到正確的位置 slides.insert(new_position, new_slide_id) prs.slides._sldIdLst[:] = slides def delete_slide(prs, slide_index): # prs = Presentation(template_path) """ 刪除給定索引處的幻燈片。 :param prs: Presentation 對象 :param slide_index: 要刪除的幻燈片的索引(從0開始) """ # 確保索引在范圍內(nèi) if slide_index < 0 or slide_index >= len(prs.slides): print("無效的幻燈片索引") return # 獲取幻燈片ID列表 xml_slides = list(prs.slides._sldIdLst) # 根據(jù)索引找到對應的幻燈片ID并移除 slides_id_to_delete = xml_slides[slide_index] prs.slides._sldIdLst.remove(slides_id_to_delete) # 保存修改后的PPT # prs.save(modified_path) def copy_ppt(pages, template_path="template.pptx", source_index=1, target_index=1, modified_path="modified_example.pptx"): prs = Presentation(template_path) copy_pages, center_pages = pages - 2, len(prs.slides) - 2 if copy_pages != center_pages: if copy_pages < center_pages: start_page_index = center_pages for _ in range(center_pages - copy_pages): delete_slide(prs, start_page_index) start_page_index -= 1 else: n = (copy_pages // center_pages) * center_pages m = (copy_pages // center_pages + 1) * center_pages - copy_pages start_page_index = center_pages for _ in range(n): for i in range(1, center_pages + 1): copy_slide_and_insert_after(prs, i, start_page_index) start_page_index += 1 if m: for _ in range(m): delete_slide(prs, start_page_index) start_page_index -= 1 prs.save(modified_path) if __name__ == '__main__': # 使用示例 # data=extract_ppt_with_style("template.pptx", "output_styles.json") # # prompt_text=f""" # # ppt json 模版 # {data} # # 模版使用說明 # - 每個 slide 的 shapes 的 結構 (元素個數(shù))是不可變得 # - 每個 slide 的 shapes 里面的字典的key 不可變 值是可以變 的 # - 第一 slide 是不可被復制的 且 必須在第一個位置 但是內(nèi)容 是可變的 slide_number 也是可變的 # - 最后一個 slide 也是不可復制的 且 必須在最后一個位置 但是內(nèi)容 是可變的 slide_number 也是可變的 # - 中簡的 slide 是可以被復制的 但是順序不能改變 # - 例如 中間 有 兩個 slide 2,3 如果你的ppt 中間需要5個 slide 那么復制 順序是 2,3,2,3,2 復制后可以改其他slide_number 名字 # # 明白上述模版使用要求之后 請 完成主題為:人工智能改變世界的ppt大綱 并且 使用上述模版 生成對應的json # """ llm_json =[] # copy_ppt(len(llm_json)) data = extract_ppt_with_style("modified_example.pptx", "output_styles.json") apply_styles_to_ppt("modified_example.pptx", "output_styles.json", "new_ppt.pptx", llm_json)
到此這篇關于利用Python自動化處理PPT樣式與結構的解決方案的文章就介紹到這了,更多相關Python自動化處理PPT內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python實現(xiàn)對文件進行單詞劃分并去重排序操作示例
這篇文章主要介紹了Python實現(xiàn)對文件進行單詞劃分并去重排序操作,涉及Python文件讀取、字符串遍歷、拆分、排序等相關操作技巧,需要的朋友可以參考下2018-07-07python中import,from……import的使用詳解
這篇文章主要介紹了python中import,from……import的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02PyCharm2021最新激活碼+激活碼補丁(親測最新版PyCharm2021.3激活成功)
這篇文章主要介紹了PyCharm2021最新激活碼+激活碼補丁,親測最新版PyCharm2021.3激活成功,PyCharm2020激活成功2020-09-09Python中使用多進程來實現(xiàn)并行處理的方法小結
本篇文章主要介紹了Python中使用多進程來實現(xiàn)并行處理的方法小結,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08一文帶你了解Python中的type,isinstance和issubclass
這篇文章主要為大家詳細介紹了Python中的type、isinstance和issubclass的使用,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-01-01Python實現(xiàn)可設置持續(xù)運行時間、線程數(shù)及時間間隔的多線程異步post請求功能
這篇文章主要介紹了Python實現(xiàn)可設置持續(xù)運行時間、線程數(shù)及時間間隔的多線程異步post請求功能,涉及Python網(wǎng)絡請求的創(chuàng)建、發(fā)送、響應、處理等相關操作技巧,需要的朋友可以參考下2018-01-01