# -*- coding: utf-8 -*- """在当前 PPT 业绩章节开头插入「科技创新类六大评审维度」总览页""" import copy from pptx import Presentation from pptx.util import Inches, Pt, Emu from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.shapes import MSO_SHAPE F = r"E:\SynologyDrive1\project\2026-06-09 长沙市紧缺急需人才项目申报\紧缺急需-陈越-2026-9-11.pptx" MAIN = RGBColor(0x40, 0xE0, 0xD0) DARK = RGBColor(0x33, 0x33, 0x33) GREY = RGBColor(0x55, 0x55, 0x55) WHITE = RGBColor(0xFF, 0xFF, 0xFF) TEAL = RGBColor(0x00, 0x8B, 0x8B) FONT = "Noto Sans SC" def set_text(shape, text, size, color, bold=False, align=PP_ALIGN.LEFT): tf = shape.text_frame tf.word_wrap = True p = tf.paragraphs[0] runs = p.runs if runs: runs[0].text = text for r in runs[1:]: r.text = "" runs[0].font.size = Pt(size) runs[0].font.color.rgb = color runs[0].font.bold = bold runs[0].font.name = FONT else: r = p.add_run() r.text = text r.font.size = Pt(size) r.font.color.rgb = color r.font.bold = bold r.font.name = FONT p.alignment = align def card(slide, x, y, w, h, line_color=MAIN): sh = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(x), Inches(y), Inches(w), Inches(h)) sh.fill.solid() sh.fill.fore_color.rgb = WHITE sh.line.color.rgb = line_color sh.line.width = Pt(1.25) sh.shadow.inherit = False try: sh.adjustments[0] = 0.08 except Exception: pass sh.text_frame.text = "" return sh def chip(slide, x, y, w, h, text, fill=MAIN, color=WHITE, size=12): sh = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(x), Inches(y), Inches(w), Inches(h)) sh.fill.solid() sh.fill.fore_color.rgb = fill sh.line.fill.background() sh.shadow.inherit = False try: sh.adjustments[0] = 0.5 except Exception: pass tf = sh.text_frame tf.word_wrap = False tf.vertical_anchor = MSO_ANCHOR.MIDDLE p = tf.paragraphs[0] p.alignment = PP_ALIGN.CENTER r = p.add_run() r.text = text r.font.name = FONT r.font.size = Pt(size) r.font.bold = True r.font.color.rgb = color return sh def textbox(slide, x, y, w, h, text, size=11, color=GREY, bold=False, line=1.25): box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h)) tf = box.text_frame tf.word_wrap = True tf.margin_left = 0 tf.margin_right = 0 tf.margin_top = 0 tf.margin_bottom = 0 p = tf.paragraphs[0] p.alignment = PP_ALIGN.LEFT p.line_spacing = line r = p.add_run() r.text = text r.font.name = FONT r.font.size = Pt(size) r.font.bold = bold r.font.color.rgb = color return box def add_slide_like(prs, template, title): new = prs.slides.add_slide(template.slide_layout) for ph in list(new.placeholders): ph.element.getparent().remove(ph.element) sp_tree = new.shapes._spTree for shp in list(template.shapes)[:2]: sp_tree.insert_element_before(copy.deepcopy(shp.element), "p:extLst") set_text(new.shapes[1], title, 32, DARK, bold=True) return new def move_slide(prs, old_index, new_index): lst = prs.slides._sldIdLst items = list(lst) lst.remove(items[old_index]) lst.insert(new_index, items[old_index]) def main(): prs = Presentation(F) tpl = prs.slides[4] # P5 两项目概览页 dims = [ ("个人资质", "湖南大学应用物理学本科 · 17 年激光控制研发 · 总工程师、省工程中心副主任 · 第一/主要发明人 2 项发明专利"), ("研究方向产业契合度", "激光控制 ↔ 长沙“智能建造”产业链 · 湘江新区激光装备集群 · 高端激光控制国产替代、对标德国 ScanLab"), ("科研成果水平", "2 项省级重点攻关项目 · 多轴同步 ≤1ms · 轮廓精度 ≤0.03mm · 15μm 级高精度拼接"), ("成果转化与团队能力", "成果落地大族激光等行业龙头 · 国内最大消费级激光控制方案供应商 · 团队 117 人、年研发投入 1798 万"), ("发展潜力与平台支撑", "湖南省激光控制系统工程中心 · 国防科大技术背景 · 3D 打印市场 630 亿(2030)"), ("未来三年规划", "标准筑基 → 消费落地 → 芯片自主(EtherCAT / ESP32 / MCU+FPGA)· 开放 API/SDK · AI 就绪"), ] s = add_slide_like(prs, tpl, "02 业绩贡献 - 科技创新类评审维度") cw, ch = 3.77, 2.42 x0, y0 = 0.8, 1.55 for i, (name, desc) in enumerate(dims): col, row = i % 3, i // 3 x = x0 + col * (cw + 0.2) y = y0 + row * (ch + 0.12) card(s, x, y, cw, ch) chip(s, x + 0.22, y + 0.2, 0.42, 0.42, str(i + 1), fill=MAIN, size=13) textbox(s, x + 0.78, y + 0.24, 2.8, 0.38, name, size=14, color=DARK, bold=True) textbox(s, x + 0.24, y + 0.78, cw - 0.48, ch - 0.95, desc, size=10.5, color=GREY, line=1.3) # 移到 index 4(原 P5 之前) move_slide(prs, len(prs.slides._sldIdLst) - 1, 4) prs.save(F) print("已保存,总页数:", len(prs.slides._sldIdLst)) # 校验 prs2 = Presentation(F) for i, sl in enumerate(prs2.slides, 1): txt = [] for sh in sl.shapes: if sh.has_text_frame: t = " ".join(p.text for p in sh.text_frame.paragraphs).strip() if t: txt.append(t) print(f"P{i}: " + " | ".join(txt)[:70]) if __name__ == "__main__": main()