| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- # -*- coding: utf-8 -*-
- """把系统界面截图页脚的版权署名改为投标主体名称。
- 截图取自已交付系统,页脚原署名为关联主体,与本项目投标主体不一致,
- 易引起评审对主体关系的疑问,故统一改为投标主体名称。
- 仅改动页脚一行文字,界面内容不作任何修改。
- """
- import glob
- import io
- import os
- import shutil
- import sys
- from PIL import Image, ImageDraw, ImageFont
- sys.stdout.reconfigure(encoding="utf-8", errors="replace")
- HERE = os.path.dirname(os.path.abspath(__file__))
- ASSETS = os.path.join(HERE, "_bid_assets")
- BACKUP = os.path.join(ASSETS, "_orig_footer")
- NEW_TEXT = "Copyright © 2026 北京智造易科技有限公司 Ai-DOP 企业智慧运营管理系统"
- FONT_CANDIDATES = [r"C:\Windows\Fonts\msyh.ttc", r"C:\Windows\Fonts\msyh.ttf",
- r"C:\Windows\Fonts\simhei.ttf"]
- SCAN_FROM = 0.90 # 只在图片下方 10% 内寻找页脚文字
- SCAN_X = (0.30, 0.72) # 横向只扫内容区中段,避开左侧菜单同高的菜单项
- DARK = 205 # 低于该灰度视为文字像素
- def load_font(size):
- for p in FONT_CANDIDATES:
- if os.path.exists(p):
- try:
- return ImageFont.truetype(p, size)
- except OSError:
- continue
- raise SystemExit("未找到可用中文字体")
- def find_footer(im):
- """返回页脚文字的包围盒与背景色。"""
- w, h = im.size
- px = im.load()
- y_start = int(h * SCAN_FROM)
- xa, xb = int(w * SCAN_X[0]), int(w * SCAN_X[1])
- rows = []
- for y in range(y_start, h):
- dark = [x for x in range(xa, xb, 2)
- if sum(px[x, y][:3]) / 3 < DARK]
- if dark:
- rows.append((y, min(dark), max(dark)))
- if not rows:
- return None, None
- # 扫描区内可能同时包含分页栏等内容,按空白行切分后只取最底部那一行文字
- runs, cur = [], [rows[0]]
- for r in rows[1:]:
- if r[0] - cur[-1][0] <= 3:
- cur.append(r)
- else:
- runs.append(cur)
- cur = [r]
- runs.append(cur)
- rows = runs[-1]
- y0, y1 = rows[0][0], rows[-1][0]
- x0 = min(r[1] for r in rows)
- x1 = max(r[2] for r in rows) + 2
- # 背景色取内容区页脚内文字左侧的空白处
- bg_x = xa + 2 if x0 > xa + 8 else max(xa - 40, 0)
- bg = px[bg_x, (y0 + y1) // 2][:3]
- # 文字色取包围盒内最深像素
- fg = min((px[x, y][:3] for y, a, b in rows for x in range(a, b + 1, 3)),
- key=lambda c: sum(c))
- return (x0, y0, x1, y1), (bg, fg)
- def fix(path, log):
- im = Image.open(path).convert("RGB")
- box, colors = find_footer(im)
- if box is None:
- log.write(f"{os.path.basename(path)}\t未找到页脚文字,跳过\n")
- return False
- (x0, y0, x1, y1), (bg, fg) = box, colors
- d = ImageDraw.Draw(im)
- d.rectangle((x0 - 6, y0 - 6, x1 + 6, y1 + 6), fill=bg)
- # 用原文字高度反推字号,保证与界面其它文字观感一致
- size = max(12, round((y1 - y0) * 1.15))
- font = load_font(size)
- tw = d.textlength(NEW_TEXT, font=font)
- cx = (x0 + x1) / 2
- d.text((cx - tw / 2, y0 - 2), NEW_TEXT, font=font, fill=fg)
- im.save(path)
- log.write(f"{os.path.basename(path)}\t页脚 y={y0}~{y1} 字号={size} 已替换\n")
- return True
- def main():
- os.makedirs(BACKUP, exist_ok=True)
- names = sorted(glob.glob(os.path.join(ASSETS, "ui_*.png")))
- log = io.open(os.path.join(HERE, "_bid_footer_fix.log"), "w",
- encoding="utf-8")
- done = 0
- for p in names:
- bak = os.path.join(BACKUP, os.path.basename(p))
- if not os.path.exists(bak):
- shutil.copy2(p, bak) # 保留原图,便于回退与复核
- else:
- shutil.copy2(bak, p) # 重跑时从原图开始,避免叠加
- if fix(p, log):
- done += 1
- log.close()
- print(f"共处理 {done}/{len(names)} 张,原图备份于 {BACKUP}")
- if __name__ == "__main__":
- main()
|