# -*- coding: utf-8 -*-
"""出版社课件署名与版权页文字替换（只处理加校徽后的这批文件，不动原件）。

替换：同济大学 → 广东金融学院 ；朱少民 → 周宇文
删除：含"版权所有"或"仅限于教学使用"的整段（版权提示行）

要点：
  - 递归进入组合形状与表格单元格；
  - 段落文字可能被拆在多个 run 里，命中后把新文字写入首个 run、清空其余 run，
    因此不会出现"改了一半"的残留；
  - 删除版权段时移除整个段落元素（若该文本框只剩这一段则改为清空文字，避免空文本框架异常）。

用法：python _fix_text.py <文件/目录...>
"""
import os
import re
import sys

from pptx import Presentation
from pptx.util import Emu, Pt

EMU = 914400.0


def _est_width(text, pt):
    """按字号估算文本宽度（英寸）：中日韩字符按 1 em，其余按 0.55 em。"""
    units = sum(1.0 if ord(ch) > 0x2E80 else 0.55 for ch in text)
    return units * pt / 72.0


def _fit(shape, para, slide_width, new_text):
    """替换后文字变长时：先扩宽文本框（保持中心、限制在页内），仍不够再等比缩小字号。"""
    sizes = [r.font.size.pt for r in para.runs if r.font.size]
    pt = max(sizes) if sizes else 18.0
    need = _est_width(new_text, pt) * 1.12      # 留 12% 安全余量，避免字体度量差异导致折行
    box_w = shape.width / EMU
    if need > box_w:
        cx = shape.left / EMU + box_w / 2.0
        new_w = min(need * 1.08, slide_width - 0.2)
        left = max(0.1, min(cx - new_w / 2.0, slide_width - 0.1 - new_w))
        shape.left, shape.width = int(left * EMU), int(new_w * EMU)
        box_w = new_w
    if need > box_w:                      # 扩框后仍不够，等比缩字号
        scale = box_w / need * 0.97
        for r in para.runs:
            if r.font.size:
                r.font.size = Pt(max(8.0, r.font.size.pt * scale))

REPLACE = (('同济大学', '广东金融学院'), ('朱少民', '周宇文'))
KILL = ('版权所有', '仅限于教学使用')


def iter_text_frames(shapes):
    """产出 (text_frame, shape)：shape 用于替换后做宽度/字号适配。"""
    for s in shapes:
        if s.shape_type == 6:                       # GROUP
            yield from iter_text_frames(s.shapes)
            continue
        if s.has_text_frame:
            yield s.text_frame, s
        if getattr(s, 'has_table', False) and s.has_table:
            for row in s.table.rows:
                for cell in row.cells:
                    yield cell.text_frame, s


def fix_paragraphs(tf, stats, shape=None, slide_width=10.0):
    for para in list(tf.paragraphs):
        text = para.text
        if not text.strip():
            continue
        if any(k in text for k in KILL):
            p = para._p
            parent = p.getparent()
            if parent is not None and len(parent.findall(p.tag)) > 1:
                parent.remove(p)
            else:
                for r in para.runs:
                    r.text = ''
            stats['killed'] += 1
            continue
        # 结束页原为"朱少民 / 同济大学"，统一成校名在前的写法
        if re.search(r'朱少民\s*/\s*同济大学', text):
            new = re.sub(r'朱少民\s*/\s*同济大学', '广东金融学院 / 周宇文', text)
        else:
            new = text
            for a, b in REPLACE:
                if a in new:
                    new = new.replace(a, b)
        if new != text:
            runs = para.runs
            if runs:
                runs[0].text = new
                for r in runs[1:]:
                    r.text = ''
            if shape is not None:
                _fit(shape, para, slide_width, new)
            stats['replaced'] += 1


def main():
    targets = []
    for a in sys.argv[1:]:
        if os.path.isdir(a):
            targets += [os.path.join(a, f) for f in sorted(os.listdir(a))
                        if f.lower().endswith('.pptx') and not f.startswith('~$')]
        else:
            targets.append(a)
    if not targets:
        print(__doc__)
        return 1
    grand = {'replaced': 0, 'killed': 0}
    for f in targets:
        prs = Presentation(f)
        stats = {'replaced': 0, 'killed': 0}
        containers = list(prs.slide_masters) + list(prs.slide_layouts) + list(prs.slides)
        slide_w = prs.slide_width / EMU
        for c in containers:
            for tf, shape in iter_text_frames(c.shapes):
                fix_paragraphs(tf, stats, shape, slide_w)
        # 演讲者备注里也清一遍
        for s in prs.slides:
            if s.has_notes_slide:
                tf = s.notes_slide.notes_text_frame
                if tf is not None:
                    fix_paragraphs(tf, stats)
        prs.save(f)
        grand['replaced'] += stats['replaced']
        grand['killed'] += stats['killed']
        print('%-44s 替换 %d 处 ｜ 删除版权段 %d 处'
              % (os.path.basename(f)[:42], stats['replaced'], stats['killed']))
    print('合计：替换 %d 处，删除版权段 %d 处' % (grand['replaced'], grand['killed']))
    return 0


if __name__ == '__main__':
    sys.exit(main())
