Files
tyx_AI_xhs/scripts/render-wp4-04-source-references.py

131 lines
8.8 KiB
Python

from __future__ import annotations
import argparse
import os
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
HOME = Path.home()
DYNAMIC_ROOT = Path(os.environ.get("DADA_DYNAMIC_ASSET_ROOT", HOME / "Desktop" / "sticker_interactive" / "单模板归档" / "templates"))
STICKER_ROOT = Path(os.environ.get("DADA_STATIC_STICKER_ROOT", HOME / "Desktop" / "贴纸素材"))
COLOR_ROOT = Path(os.environ.get("DADA_COLOR_ASSET_ROOT", HOME / "Desktop" / "sticker_colour" / "单模板归档" / "styles"))
TEXT_ROOT = Path(os.environ.get("DADA_TEXT_ASSET_ROOT", HOME / "Desktop" / "sticker_text"))
UI_FONT = Path("C:/Windows/Fonts/msyh.ttc")
FONT081 = TEXT_ROOT / "字体" / "面板全量采集" / "font_panel_full_20260722" / "resources" / "font_packages" / "FONT081_Lexend Deca" / "font_files" / "02034l0o6r57rxed4027b5689e0dxe7e142r0vi8920akeqto.ttf"
def ui_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont:
candidate = Path("C:/Windows/Fonts/msyhbd.ttc") if bold else UI_FONT
return ImageFont.truetype(candidate, size)
FONT_PATHS = {
"15974853bc3294ef68e7e6d58fe74fd7": DYNAMIC_ROOT / "DYN002" / "fonts" / "15974853bc3294ef68e7e6d58fe74fd7" / "0202b90o6r57rxed4027b5689e0dxe7e142r0yho9d0lljuqj.ttf",
"46f8336813e4c48d06a1aef294fdccf6": DYNAMIC_ROOT / "DYN016" / "fonts" / "46f8336813e4c48d06a1aef294fdccf6" / "9fbfbb420cea1df916d7c7c7ac90b1c88b61e117-PingFang-SC-Semibold-2.ttf",
"53ca6b704728520da50c145eabb2e635": DYNAMIC_ROOT / "DYN007" / "fonts" / "53ca6b704728520da50c145eabb2e635" / "fab6c26a0b21d5e9b57fb5238843ac1fb77a2ce6-HYZhengYuan.ttf",
"cca5efc0e02fb1bf62349bd68ef30fc1": DYNAMIC_ROOT / "DYN015" / "fonts" / "cca5efc0e02fb1bf62349bd68ef30fc1" / "e11ced673fc7e63e8b0b4730166d29845d8bebae-NotoSansCJKsc-Regular.otf",
"dd25b35dcb7ba4476cbaa9a9592e39e2": DYNAMIC_ROOT / "DYN001" / "fonts" / "dd25b35dcb7ba4476cbaa9a9592e39e2" / "0202b90o6r57rxed4027b5689e0dxe7e142r0ygbx80porvko.ttf",
"e4210c9872f0c279b35273f230809821": DYNAMIC_ROOT / "DYN011" / "fonts" / "e4210c9872f0c279b35273f230809821" / "06b980259e2104e1211a6819a61bc5ddeca77dcb-DJB-Get-Digital-1.ttf",
"f4bfd4132df2d6be97ceabadf3853505": DYNAMIC_ROOT / "DYN008" / "fonts" / "f4bfd4132df2d6be97ceabadf3853505" / "6ce05a147aedabbb610d9cb3e75bbe60c064c3f5-BarlowCondensed-SemiBold.ttf",
"FONT081": FONT081,
}
MODELS = {
"DYN001": ((130, 42), [("image28.png", -17.562, 2.203, 219, 67)], [("温州", 0, 0, 21, "dd25b35dcb7ba4476cbaa9a9592e39e2", "#FFFFFF", "mm")]),
"DYN002": ((215, 48), [("image29.png", 94.5, 0.5, 232, 77)], [("温州", -94.5, 10.5, 19, "15974853bc3294ef68e7e6d58fe74fd7", "#FFFFFF", "lm"), ("WENZHOU", 0, 36, 10, "15974853bc3294ef68e7e6d58fe74fd7", "#FFFFFF", "lm")]),
"DYN003": ((215, 54), [("image30.png", -93.857, -23.635, 235, 85)], [("温州", -54, 9, 18, "dd25b35dcb7ba4476cbaa9a9592e39e2", "#FFFFFF", "lm"), ("温州", -53, -16, 24, "dd25b35dcb7ba4476cbaa9a9592e39e2", "#FFFFFF", "lm")]),
"DYN004": ((180, 48), [("image32.png", -21.067, -1, 39, 76)], [("温州", -28, 0, 26, "dd25b35dcb7ba4476cbaa9a9592e39e2", "#FFFFFF", "lm"), ("27°59'39.48\"N", -29.844, 36, 14, "15974853bc3294ef68e7e6d58fe74fd7", "#FFFFFF", "lm"), ("120°41'57.84\"E", 71.523, 36, 14, "15974853bc3294ef68e7e6d58fe74fd7", "#FFFFFF", "lm")]),
"DYN007": ((190, 34), [], [("@dada", 0, 0, 50, "53ca6b704728520da50c145eabb2e635", "#295E8B", "mm")]),
"DYN008": ((140, 180), [("backendui0.png", -125, 0, 5, 185)], [("Aug.", -112, -32, 70, "f4bfd4132df2d6be97ceabadf3853505", "#FFFFFF", "lm"), ("09:07", -114.024, 97, 130, "f4bfd4132df2d6be97ceabadf3853505", "#FFFFFF", "lm")]),
"DYN011": ((130, 40), [("backendui0.png", -120, -19, 8, 12)], [("26", -85, 0, 64, "e4210c9872f0c279b35273f230809821", "#FF6D2F", "mm"), ("08", 0, 0, 64, "e4210c9872f0c279b35273f230809821", "#FF6D2F", "mm"), ("03", 85, 0, 64, "e4210c9872f0c279b35273f230809821", "#FF6D2F", "mm")]),
"DYN012": ((118, 78), [], [("09", -60, 0, 100, "FONT081", "#FFFFFF", "mm"), ("07", 60, 0, 100, "FONT081", "#FFFFFF", "mm"), ("AM", 94, -65, 20, "FONT081", "#FFFFFF", "mm")]),
"DYN015": ((300, 42), [("imager2_2.png", -140, 0, 60, 60)], [("Dada Creator", -100, 0, 40, "cca5efc0e02fb1bf62349bd68ef30fc1", "#666666", "lm")]),
"DYN016": ((160, 48), [("image21.png", -61.842, 16.483, 186, 59)], [("@dada", 0, 0, 27, "46f8336813e4c48d06a1aef294fdccf6", "#FFFFFF", "lm")]),
}
def crop_rgba(image: Image.Image, padding: int = 8) -> Image.Image:
bbox = image.getbbox()
if not bbox:
return image
left, top, right, bottom = bbox
return image.crop((max(0, left - padding), max(0, top - padding), min(image.width, right + padding), min(image.height, bottom + padding)))
def render_dynamic(template_id: str) -> Image.Image:
(half_width, half_height), image_layers, text_layers = MODELS[template_id]
logical = Image.new("RGBA", (half_width * 2 + 40, half_height * 2 + 40), (0, 0, 0, 0))
center = (logical.width / 2, logical.height / 2)
for filename, x, y, width, height in image_layers:
source = Image.open(DYNAMIC_ROOT / template_id / "resource" / filename).convert("RGBA").resize((width, height), Image.Resampling.LANCZOS)
logical.alpha_composite(source, (round(center[0] + x - width / 2), round(center[1] + y - height / 2)))
draw = ImageDraw.Draw(logical)
if template_id == "DYN012":
draw.rectangle((center[0] - 3.5, center[1] - 21, center[0] + 3.5, center[1] + 21), fill="#FFFFFF")
for value, x, y, size, font_id, color, anchor in text_layers:
draw.text((center[0] + x, center[1] + y), value, font=ImageFont.truetype(FONT_PATHS[font_id], size), fill=color, anchor=anchor)
return crop_rgba(logical)
def sheet(title: str, items: list[tuple[str, Image.Image]], output: Path, row_height: int = 140) -> None:
width = 420
image = Image.new("RGB", (width, 62 + row_height * len(items)), "#F4F5F7")
draw = ImageDraw.Draw(image)
draw.text((20, 18), title, font=ui_font(24, True), fill="#111111")
for index, (label, item) in enumerate(items):
top = 62 + index * row_height
draw.rounded_rectangle((12, top + 6, width - 12, top + row_height - 6), radius=4, fill="#30343B")
fitted = item.copy()
fitted.thumbnail((330, row_height - 48), Image.Resampling.LANCZOS)
image.paste(fitted, ((width - fitted.width) // 2, top + 12), fitted if fitted.mode == "RGBA" else None)
draw.text((20, top + row_height - 30), label, font=ui_font(16, True), fill="#FFFFFF")
image.save(output)
def build_sources(output: Path) -> None:
output.mkdir(parents=True, exist_ok=True)
ordinary = [
("STK001 · part1 原版草莓", crop_rgba(Image.open(STICKER_ROOT / "sticker_part1" / "01af6384c2a962d17f55736f9895b505.png").convert("RGBA"))),
("STK002 · part2 原版小狗", crop_rgba(Image.open(STICKER_ROOT / "sticker_part2" / "011db0fb6ac4184e4a708374003bce66.png").convert("RGBA"))),
]
colors = [(color_id, crop_rgba(Image.open(COLOR_ROOT / color_id / "preview.png").convert("RGBA"))) for color_id in ("COLOR001", "COLOR002", "COLOR008", "COLOR016")]
dynamics = [(template_id + (" · 原字体缺失,同用 FONT081" if template_id == "DYN012" else ""), render_dynamic(template_id)) for template_id in MODELS]
sheet("原始归档 PNG", ordinary, output / "source-ordinary.png", 190)
sheet("原始归档 renderer_source.py 预览", colors, output / "source-colors.png", 150)
sheet("原始 Prefab / PNG / 字体参数还原", dynamics, output / "source-dynamic.png", 150)
def compose(source: Path, web: Path, destination: Path, title: str) -> None:
left = Image.open(source).convert("RGB")
right = Image.open(web).convert("RGB")
column_width = 500
left.thumbnail((column_width - 24, 2200), Image.Resampling.LANCZOS)
right.thumbnail((column_width - 24, 2200), Image.Resampling.LANCZOS)
height = max(left.height, right.height) + 108
result = Image.new("RGB", (column_width * 2, height), "#ECEEF1")
draw = ImageDraw.Draw(result)
draw.text((24, 18), title, font=ui_font(28, True), fill="#111111")
draw.text((24, 62), "左:原始归档参考", font=ui_font(18, True), fill="#434A54")
draw.text((column_width + 24, 62), "右:当前网页实拍", font=ui_font(18, True), fill="#434A54")
result.paste(left, ((column_width - left.width) // 2, 98))
result.paste(right, (column_width + (column_width - right.width) // 2, 98))
result.save(destination)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("output", type=Path)
parser.add_argument("--compose", action="store_true")
args = parser.parse_args()
build_sources(args.output)
if args.compose:
for kind, title in (("ordinary", "普通贴纸对比"), ("colors", "颜色卡对比"), ("dynamic", "动态贴纸对比")):
compose(args.output / f"source-{kind}.png", args.output / f"web-{kind}.png", args.output / f"comparison-{kind}.png", title)
if __name__ == "__main__":
main()