165 lines
5.5 KiB
Python
165 lines
5.5 KiB
Python
# -*- 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()
|