135 lines
4.8 KiB
Python
135 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from PIL import Image, ImageDraw, ImageFont, ImageOps
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
OUTPUT_DIR = (
|
|
REPO_ROOT / "public" / "branding" / "taonier-logo-hand-spirit-muted-color-concepts"
|
|
)
|
|
CONTACT_SHEET_PATH = OUTPUT_DIR / "taonier-hand-spirit-muted-color-contact-sheet.png"
|
|
REFERENCE_IMAGE = (
|
|
REPO_ROOT
|
|
/ "public"
|
|
/ "branding"
|
|
/ "taonier-logo-hand-spirit-ref01-logo-refine-concepts"
|
|
/ "taonier-hand-spirit-ref01-logo-refine-01-flat-coral-cream.png"
|
|
)
|
|
|
|
ITEMS = [
|
|
("REF 上轮01", REFERENCE_IMAGE),
|
|
("01 雾玫鼠尾草", "taonier-hand-spirit-muted-color-01-dusty-rose-sage"),
|
|
("02 烟蓝杏橙", "taonier-hand-spirit-muted-color-02-smoke-blue-apricot"),
|
|
("03 雾紫陶土", "taonier-hand-spirit-muted-color-03-misty-lilac-clay"),
|
|
("04 黄油玫瑰茶", "taonier-hand-spirit-muted-color-04-butter-rose-tea"),
|
|
("05 陶蓝薄荷", "taonier-hand-spirit-muted-color-05-clay-blue-mint"),
|
|
("06 粉雾浆果", "taonier-hand-spirit-muted-color-06-powder-berry-cloud"),
|
|
("07 砂紫奶雾", "taonier-hand-spirit-muted-color-07-sand-violet"),
|
|
("08 低饱双色", "taonier-hand-spirit-muted-color-08-muted-duotone"),
|
|
]
|
|
|
|
|
|
def load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
|
|
for candidate in [
|
|
Path("C:/Windows/Fonts/msyh.ttc"),
|
|
Path("C:/Windows/Fonts/simhei.ttf"),
|
|
Path("C:/Windows/Fonts/simsun.ttc"),
|
|
]:
|
|
if candidate.exists():
|
|
return ImageFont.truetype(str(candidate), size)
|
|
return ImageFont.load_default()
|
|
|
|
|
|
def find_image(stem_or_path: str | Path) -> Path | None:
|
|
if isinstance(stem_or_path, Path):
|
|
return stem_or_path if stem_or_path.exists() else None
|
|
for extension in ("png", "webp", "jpg", "jpeg"):
|
|
candidate = OUTPUT_DIR / f"{stem_or_path}.{extension}"
|
|
if candidate.exists():
|
|
return candidate
|
|
return None
|
|
|
|
|
|
def normalize_square(image_path: Path) -> Image.Image:
|
|
image = Image.open(image_path).convert("RGB")
|
|
if image.size == (1024, 1024):
|
|
return image
|
|
if image.width == image.height:
|
|
normalized = image.resize((1024, 1024), Image.Resampling.LANCZOS)
|
|
else:
|
|
contained = ImageOps.contain(image, (1024, 1024), Image.Resampling.LANCZOS)
|
|
normalized = Image.new("RGB", (1024, 1024), "#fffdf8")
|
|
x = (1024 - contained.width) // 2
|
|
y = (1024 - contained.height) // 2
|
|
normalized.paste(contained, (x, y))
|
|
if image_path.is_relative_to(OUTPUT_DIR):
|
|
normalized.save(image_path, quality=95)
|
|
return normalized
|
|
|
|
|
|
def bw_preview(image: Image.Image, size: int) -> Image.Image:
|
|
thumb = ImageOps.grayscale(image).resize((size, size), Image.Resampling.LANCZOS)
|
|
return ImageOps.autocontrast(thumb).convert("RGB")
|
|
|
|
|
|
def main() -> None:
|
|
cell_size = 268
|
|
label_height = 54
|
|
test_height = 44
|
|
gap = 22
|
|
columns = 3
|
|
rows = 3
|
|
cell_total_height = cell_size + label_height + test_height
|
|
width = columns * cell_size + (columns + 1) * gap
|
|
height = rows * cell_total_height + (rows + 1) * gap
|
|
|
|
sheet = Image.new("RGB", (width, height), "#f0ebe5")
|
|
draw = ImageDraw.Draw(sheet)
|
|
label_font = load_font(18)
|
|
test_font = load_font(13)
|
|
|
|
for index, (label, stem_or_path) in enumerate(ITEMS):
|
|
row = index // columns
|
|
column = index % columns
|
|
x = gap + column * (cell_size + gap)
|
|
y = gap + row * (cell_total_height + gap)
|
|
|
|
image_path = find_image(stem_or_path)
|
|
if image_path is None:
|
|
continue
|
|
|
|
source = normalize_square(image_path)
|
|
sheet.paste(source.resize((cell_size, cell_size), Image.Resampling.LANCZOS), (x, y))
|
|
|
|
draw.rounded_rectangle(
|
|
(x, y + cell_size, x + cell_size, y + cell_size + label_height),
|
|
radius=8,
|
|
fill="#fffdf8",
|
|
)
|
|
text_box = draw.textbbox((0, 0), label, font=label_font)
|
|
text_x = x + (cell_size - (text_box[2] - text_box[0])) / 2
|
|
text_y = y + cell_size + (label_height - (text_box[3] - text_box[1])) / 2 - 2
|
|
draw.text((text_x, text_y), label, fill="#302a25", font=label_font)
|
|
|
|
test_y = y + cell_size + label_height
|
|
draw.rounded_rectangle(
|
|
(x, test_y, x + cell_size, test_y + test_height),
|
|
radius=8,
|
|
fill="#f7f3ed",
|
|
)
|
|
tiny = source.resize((32, 32), Image.Resampling.LANCZOS)
|
|
mono = bw_preview(source, 32)
|
|
sheet.paste(tiny, (x + 52, test_y + 6))
|
|
sheet.paste(mono, (x + 104, test_y + 6))
|
|
draw.text((x + 150, test_y + 13), "32px / BW", fill="#5c5148", font=test_font)
|
|
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
sheet.save(CONTACT_SHEET_PATH, quality=95)
|
|
print(CONTACT_SHEET_PATH)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|