# -*- coding: utf-8 -*-
"""只做"换图 + 新图调色"，不改 PPT 主体配色。

替换对象（其余媒体与全部颜色值保持原样）：
  - image20.jpg：同济大学校名石碑照片（第 12/22/24/46/50/53 页章节页）10.0×4.7 in
  - image1.jpg ：版式"封面2"整页背景的校园照片（第 61 页结束页）10.0×5.62 in

新图来源：https://www.gduf.edu.cn/images/47.jpg（广金教学楼横幅 1920×500）
调色目标：配合本课件原有蓝色主色调 —— 降饱和、略降亮度、加冷色调（蓝通道微增）、
         轻微提高对比，使新照片与蓝色模板融为一体，而不是突兀的暖色照片。
"""
import colorsys
import os
import sys
import zipfile

from PIL import Image, ImageEnhance, ImageFilter

BASE = os.path.dirname(os.path.abspath(__file__))
PROJ = os.path.dirname(BASE)
SRC = os.path.join(PROJ, '课件PPT-加校徽', 'Ch1-引论-STMT-2022.pptx')
OUTDIR = os.path.join(PROJ, '课件PPT-GDUF版')
OUT = os.path.join(OUTDIR, 'Ch1-引论-GDUF版.pptx')
BANNER = os.path.join(BASE, 'gduf-47.jpg')

# 目标像素 = 页面显示尺寸（英寸）× 213 dpi；bias = 裁切窗口中心位置（主体建筑偏右）
TARGETS = {
    'image20.jpg': ((2130, 1000), 0.56),
    'image1.jpg': ((1780, 1000), 0.53),
}


def cover_fill(size, bias=0.5):
    """放大到恰好盖满目标比例，再按 bias 位置横向裁切（cover 裁切，不留模糊延伸）。

    size: 目标像素 (W,H)；bias: 裁切窗口中心在源图宽度上的相对位置（0.5=居中，
    主体偏右就给 0.5 以上的值）。
    """
    W, H = size
    src = Image.open(BANNER).convert('RGB')
    scale = max(W / src.width, H / src.height)          # 放大倍数：保证两个方向都盖满
    big = src.resize((round(src.width * scale), round(src.height * scale)), Image.LANCZOS)
    cx = int(big.width * bias)
    left = max(0, min(big.width - W, cx - W // 2))
    top = max(0, (big.height - H) // 2)
    out = big.crop((left, top, left + W, top + H))
    out = out.filter(ImageFilter.UnsharpMask(radius=2, percent=85, threshold=3))   # 放大后补锐度
    print('   源 %s → 放大 %.2fx → 裁切窗口 x=%d..%d' % (src.size, scale, left, left + W))
    return out


def cool_grade(im, target_val=0.60, target_sat=0.27):
    """按原图实测的"低饱和、较明亮"特征校准，并加轻微冷色，使之融入蓝色模板。

    原图实测：image1 明度 0.58 / 饱和 0.29；image20 明度 0.73 / 饱和 0.25。
    新图（广金照片）本身偏暗偏艳，这里把明度与饱和度拉回原图水平。
    """
    # 1) 饱和度对齐原图
    import numpy as _np
    a = _np.asarray(im.convert('RGB').resize((120, 68))).reshape(-1, 3) / 255.0
    hsv = _np.array([colorsys.rgb_to_hsv(*p) for p in a])
    sat = float(hsv[:, 1].mean())
    im = ImageEnhance.Color(im).enhance(max(0.35, min(1.8, target_sat / max(sat, 0.05))))
    # 2) 轻微去红 + 加冷，再叠模板主蓝（让新照片融入蓝色模板）
    r, g, b = im.split()
    r = r.point(lambda v: int(v * 0.96))
    b = b.point(lambda v: min(255, int(v * 1.06)))
    im = Image.merge('RGB', (r, g, b))
    overlay = Image.new('RGB', im.size, (31, 78, 121))   # 模板主蓝 #1F4E79
    im = Image.blend(im, overlay, 0.18)
    # 3) 叠色后再把明度校准到原图水平（顺序很关键，否则会被叠色压暗）
    a = _np.asarray(im.resize((120, 68))).reshape(-1, 3) / 255.0
    val = float(_np.array([colorsys.rgb_to_hsv(*p) for p in a])[:, 2].mean())
    im = ImageEnhance.Brightness(im).enhance(max(0.8, min(2.2, target_val / max(val, 0.05))))
    im = ImageEnhance.Contrast(im).enhance(1.03)
    return im


def main():
    os.makedirs(OUTDIR, exist_ok=True)
    imgs = {}
    for name, (size, bias) in TARGETS.items():
        tv, ts = (0.58, 0.27) if name == 'image1.jpg' else (0.70, 0.25)
        print('处理 %s：' % name)
        im = cool_grade(cover_fill(size, bias), target_val=tv, target_sat=ts)
        p = os.path.join(BASE, '_new_' + name)
        im.save(p, quality=93)
        imgs[name] = open(p, 'rb').read()
        print('生成替换图 %-13s %s（已按蓝色主调调色）' % (name, im.size))

    zin = zipfile.ZipFile(SRC)
    zout = zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED)
    n = 0
    for item in zin.infolist():
        data = zin.read(item.filename)
        base = os.path.basename(item.filename)
        if item.filename.startswith('ppt/media/') and base in imgs:
            data = imgs[base]; n += 1
        zout.writestr(item, data)
    zout.close(); zin.close()
    for name in TARGETS:
        p = os.path.join(BASE, '_new_' + name)
        if os.path.exists(p):
            os.remove(p)
    print('已生成 %s（仅替换 %d 个媒体，未改动任何颜色值）' % (OUT, n))
    return 0


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