Spaces:
Sleeping
Sleeping
Update src/pyscripts/img_process.py
Browse files- src/pyscripts/img_process.py +955 -168
src/pyscripts/img_process.py
CHANGED
|
@@ -1,179 +1,966 @@
|
|
| 1 |
-
from
|
| 2 |
-
|
| 3 |
-
import gradio as gr
|
| 4 |
-
import sys
|
| 5 |
import os
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
from src.pyscripts import file_process, oss, img_process
|
| 9 |
-
from datetime import datetime
|
| 10 |
-
from collections import namedtuple
|
| 11 |
-
from pydantic import BaseModel
|
| 12 |
import requests
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
if os.path.exists("src/assets/output"):
|
| 140 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
else:
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
def
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
| 157 |
if os.path.exists("src/assets/output"):
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
|
| 171 |
if __name__ == "__main__":
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
#
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 2 |
+
import re
|
|
|
|
|
|
|
| 3 |
import os
|
| 4 |
+
import io
|
| 5 |
+
from dataclasses import dataclass
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
import requests
|
| 7 |
+
from pypinyin import pinyin, Style
|
| 8 |
+
import rembg
|
| 9 |
+
import math
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
font_path_fangzhenghei = "src/assets/FangZhengHeiTiJianTi-1.ttf"
|
| 13 |
+
font_path_zihun59 = "src/assets/字魂59号-创粗黑.ttf"
|
| 14 |
+
font_path_notosans = "src/assets/NotoSansHans-Regular.otf"
|
| 15 |
+
font_path_feihuasong = "src/assets/FeiHuaSongTi-2.ttf"
|
| 16 |
+
font_path_wendingPLjianbaosong = "src/assets/gbsn00lp-2.ttf"
|
| 17 |
+
font_path_SourceHanSans = "src/assets/SourceHanSansCN-VF-2.otf"
|
| 18 |
+
font_path_notosansM = "src/assets/NotoSansHans-Medium.otf"
|
| 19 |
+
font_path_notosansB = "src/assets/NotoSansHans-Bold.otf"
|
| 20 |
+
font_path_notosansL = "src/assets/NotoSansHans-Light.otf"
|
| 21 |
+
font_path_pingfang = "src/assets/PingFang.ttc"
|
| 22 |
+
font_path_pingfangM = "src/assets/pingfang-M.ttf"
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class text_info:
|
| 27 |
+
text: str
|
| 28 |
+
font_size: int
|
| 29 |
+
position: list
|
| 30 |
+
color: tuple = (0, 0, 0)
|
| 31 |
+
max_height: int = 1000
|
| 32 |
+
max_width: int = 0
|
| 33 |
+
text_width: int = 0
|
| 34 |
+
text_height: int = 0
|
| 35 |
+
line_space: int = 10
|
| 36 |
+
font_path: str = None
|
| 37 |
+
em_font_path: str = None
|
| 38 |
+
|
| 39 |
+
def __post_init__(self):
|
| 40 |
+
self.wrap_text()
|
| 41 |
+
self.get_text_width()
|
| 42 |
+
self.get_text_height()
|
| 43 |
+
|
| 44 |
+
def wrap_text(self):
|
| 45 |
+
"""
|
| 46 |
+
resize to fix max_height and max_width
|
| 47 |
+
"""
|
| 48 |
+
words = re.findall(r"[a-zA-Z\d]+|[\u4e00-\u9fa5]|\n|.", self.text)
|
| 49 |
+
self.font = ImageFont.truetype(self.font_path, self.font_size)
|
| 50 |
+
|
| 51 |
+
lines = []
|
| 52 |
+
current_line = ""
|
| 53 |
+
for word in words:
|
| 54 |
+
if word == "\n":
|
| 55 |
+
lines.append(current_line.strip())
|
| 56 |
+
current_line = ""
|
| 57 |
+
continue
|
| 58 |
+
test_line = current_line + word
|
| 59 |
+
test_bbox = self.font.getbbox(test_line, anchor="la")
|
| 60 |
+
test_width = test_bbox[2] - test_bbox[0] # 计算宽度
|
| 61 |
+
if test_width <= self.max_width and "\n" not in test_line:
|
| 62 |
+
current_line = test_line
|
| 63 |
+
else:
|
| 64 |
+
lines.append(current_line.strip())
|
| 65 |
+
current_line = word
|
| 66 |
+
if current_line:
|
| 67 |
+
lines.append(current_line.strip())
|
| 68 |
+
self.lines = lines
|
| 69 |
+
test_bbox = self.font.getbbox("\n".join(lines), anchor="la")
|
| 70 |
+
self.text_height = test_bbox[3] - test_bbox[1]
|
| 71 |
+
self.text_width = test_bbox[2] - test_bbox[0] # 计算宽度
|
| 72 |
+
# print(lines, self.text_height, self.text_width)
|
| 73 |
+
key_lines = [line for line in lines if line.startswith("【】")]
|
| 74 |
+
if len(key_lines) > 0:
|
| 75 |
+
key_line = max(key_lines, key=len)
|
| 76 |
+
else:
|
| 77 |
+
key_line = False
|
| 78 |
+
if self.text_height > self.max_height or self.text_width > self.max_width or (key_line and not re.search("^" + re.escape(key_line) + "$", self.text, re.M)):
|
| 79 |
+
self.font_size -= 2
|
| 80 |
+
self.wrap_text()
|
| 81 |
+
|
| 82 |
+
def get_text_width(self):
|
| 83 |
+
text_bbox = self.font.getbbox(self.text, anchor="la")
|
| 84 |
+
self.text_width = text_bbox[2] - text_bbox[0]
|
| 85 |
+
|
| 86 |
+
def get_text_height(self):
|
| 87 |
+
text_bbox = self.font.getbbox(self.text, anchor="la")
|
| 88 |
+
self.text_height = text_bbox[3] - text_bbox[1]
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def write_text_on_image(img, text_info):
|
| 92 |
+
# 创建一个可以在给定图像上绘图的对象
|
| 93 |
+
draw = ImageDraw.Draw(img)
|
| 94 |
+
# 加载字体,如果没有提供字体路径,则使用系统默认字体
|
| 95 |
+
font = ImageFont.load_default().font_variant(size=text_info.font_size)
|
| 96 |
+
em_font = ImageFont.load_default().font_variant(size=text_info.font_size)
|
| 97 |
+
if text_info.font_path:
|
| 98 |
+
font = ImageFont.truetype(text_info.font_path, text_info.font_size)
|
| 99 |
+
if text_info.em_font_path:
|
| 100 |
+
em_font = ImageFont.truetype(text_info.em_font_path, text_info.font_size)
|
| 101 |
+
# 在图片上写入文字
|
| 102 |
+
# draw.text(text_info.position, text_info.text, fill=text_info.color, font=font)
|
| 103 |
+
y_text = text_info.position[1]
|
| 104 |
+
wrapped_lines = text_info.lines
|
| 105 |
+
text_bbox_chinese = font.getbbox("中文行高")
|
| 106 |
+
for line in wrapped_lines:
|
| 107 |
+
text_bbox = font.getbbox(line)
|
| 108 |
+
line_width = text_bbox[2] - text_bbox[0] # 获取宽度
|
| 109 |
+
line_height = max(text_bbox[3] - text_bbox[1], text_bbox_chinese[3] - text_bbox_chinese[1])
|
| 110 |
+
# print(text_bbox[3] - text_bbox[1], text_bbox_chinese[3] - text_bbox_chinese[1], text_info.font_size)
|
| 111 |
+
if re.search(r"^【.*】.*?" + re.escape(line), text_info.text, re.M) or re.match(r"^【.*】.*?$", line):
|
| 112 |
+
draw.text((text_info.position[0], y_text), line.replace("【】", ""), fill=text_info.color, font=em_font)
|
| 113 |
+
else:
|
| 114 |
+
draw.text((text_info.position[0], y_text), line, fill=text_info.color, font=font)
|
| 115 |
+
# 更新y坐标以添加行间距并为下一行做准备
|
| 116 |
+
y_text += line_height # + text_info.line_space
|
| 117 |
+
return img, y_text
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def embed_image(background, foreground, offset_y=0, offset_x=None, standard_fg_size=None):
|
| 121 |
+
"""
|
| 122 |
+
粘贴图片
|
| 123 |
+
|
| 124 |
+
Args:
|
| 125 |
+
background (_type_): 背景图片
|
| 126 |
+
foreground (_type_): 前景图片
|
| 127 |
+
offset_y (int, optional): 纵向偏移. Defaults to 1500.
|
| 128 |
+
standard_fg_size (tuple, optional): 前景缩放大小. Defaults to (1600, 450).
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
_type_: _description_
|
| 132 |
+
"""
|
| 133 |
+
# 获取背景图片的尺寸
|
| 134 |
+
if standard_fg_size is None:
|
| 135 |
+
standard_fg_size = foreground.size
|
| 136 |
+
standard_fg_width, standard_fg_height = standard_fg_size
|
| 137 |
+
bg_width, bg_height = background.size
|
| 138 |
+
fg_width, fg_height = foreground.size
|
| 139 |
+
fg_resize_percent = max(fg_width / standard_fg_width, fg_height / standard_fg_height)
|
| 140 |
+
# print((fg_width / standard_fg_width, fg_height / standard_fg_height))
|
| 141 |
+
foreground = foreground.resize((int(fg_width // fg_resize_percent), int(fg_height // fg_resize_percent)))
|
| 142 |
+
fg_width, fg_height = foreground.size
|
| 143 |
+
if offset_x is None:
|
| 144 |
+
offset_x = (bg_width - fg_width) // 2 # 横轴偏移使小图居中
|
| 145 |
+
|
| 146 |
+
background.paste(foreground, (int(offset_x), int(offset_y)), foreground)
|
| 147 |
+
return background
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
img_path_dict = {
|
| 151 |
+
"old": "src/assets/InvitationTemplateCompressed.pdf",
|
| 152 |
+
"common": "src/assets/旧空白邀请函图片.jpg",
|
| 153 |
+
"观礼": "src/assets/带名字.jpg",
|
| 154 |
+
"演讲": "src/assets/带名字和会.jpg",
|
| 155 |
+
"致辞": "src/assets/带名字和会.jpg",
|
| 156 |
+
}
|
| 157 |
+
font_path = "src/assets/ZhouZiSongTi7000Zi-2.otf"
|
| 158 |
+
font_path = "src/assets/FangZhengHeiTiJianTi-1.ttf"
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def writeInvitationPDF(person, debug=False):
|
| 162 |
+
"旧空白邀请函图片.jpg"
|
| 163 |
+
font_size = 40
|
| 164 |
+
resized_name_font_size = font_size * 5 / len(person.name + person.title)
|
| 165 |
+
resized_conference_font_size = font_size * 9 / len(person.conference)
|
| 166 |
+
img_path = img_path_dict[person.category]
|
| 167 |
+
image = Image.open(img_path)
|
| 168 |
+
draw = ImageDraw.Draw(image)
|
| 169 |
+
text_color = (255, 255, 255) # 白色 RGB
|
| 170 |
+
|
| 171 |
+
draw.text(
|
| 172 |
+
(182, 150 + max(0, font_size - resized_name_font_size)),
|
| 173 |
+
person.name + person.title,
|
| 174 |
+
fill=text_color,
|
| 175 |
+
font=ImageFont.truetype(font_path, min(font_size, resized_name_font_size)),
|
| 176 |
+
)
|
| 177 |
+
draw.text(
|
| 178 |
+
(102, 532 + max(0, font_size - resized_conference_font_size)),
|
| 179 |
+
person.conference,
|
| 180 |
+
fill=text_color,
|
| 181 |
+
font=ImageFont.truetype(font_path, min(font_size, resized_conference_font_size)),
|
| 182 |
+
)
|
| 183 |
+
imgStream = io.BytesIO()
|
| 184 |
+
# image.save(imgStream)
|
| 185 |
+
# doc.save(pdfStream)
|
| 186 |
+
# doc.close()
|
| 187 |
+
if debug:
|
| 188 |
+
image.save("output/image_with_text.jpg")
|
| 189 |
+
|
| 190 |
+
return imgStream
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def write_text(
|
| 194 |
+
text, font_path, image, H_PADDING_RATION, font_size, max_height=None, color=(255, 255, 255), em_font_path=None, alignment="left",
|
| 195 |
+
start_y=0, start_x=0, max_width=None, part_width=10000
|
| 196 |
+
):
|
| 197 |
+
bg_width = image.size[0]
|
| 198 |
+
if max_width is None:
|
| 199 |
+
max_width = bg_width * (1 - 2 * H_PADDING_RATION)
|
| 200 |
+
if max_height is None:
|
| 201 |
+
max_height = font_size * len(text.split("\n"))
|
| 202 |
+
text = text_info(
|
| 203 |
+
text=text,
|
| 204 |
+
font_size=font_size,
|
| 205 |
+
position=[start_x, start_y],
|
| 206 |
+
color=color,
|
| 207 |
+
max_width=max_width,
|
| 208 |
+
max_height=max_height,
|
| 209 |
+
font_path=font_path,
|
| 210 |
+
em_font_path=em_font_path,
|
| 211 |
+
)
|
| 212 |
+
if alignment == "right":
|
| 213 |
+
text.position[0] = bg_width * (1 - H_PADDING_RATION) - text.text_width
|
| 214 |
+
elif alignment == "medium":
|
| 215 |
+
text.position[0] = (min(part_width, bg_width) - text.text_width) // 2 + start_x
|
| 216 |
+
elif alignment == "left":
|
| 217 |
+
text.position[0] = bg_width * H_PADDING_RATION
|
| 218 |
+
print(text)
|
| 219 |
+
image, next_height = write_text_on_image(image, text)
|
| 220 |
+
return text, image, next_height
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def make_exhibitor_poster(logo_link, slogan, content):
|
| 224 |
+
background = Image.open("src/assets/展商海报白板.jpg")
|
| 225 |
+
H_PADDING_RATION = 0.08
|
| 226 |
+
V_PADDING = 25
|
| 227 |
+
V_START = 950
|
| 228 |
+
MAX_HEIGHT = 520
|
| 229 |
+
res = requests.get(logo_link)
|
| 230 |
+
foreground = Image.open(io.BytesIO(res.content))
|
| 231 |
+
# foreground = Image.open(logo_path_list[FOREGROUND_ID])
|
| 232 |
+
background = embed_image(background, foreground, offset_y=750, standard_fg_size=(1600 // 2, 450 // 2))
|
| 233 |
+
|
| 234 |
+
text = slogan
|
| 235 |
+
font_size = 110 // 2
|
| 236 |
+
font_path = font_path_fangzhenghei
|
| 237 |
+
slogan, background, next_height = write_text(
|
| 238 |
+
text,
|
| 239 |
+
font_path,
|
| 240 |
+
background,
|
| 241 |
+
H_PADDING_RATION,
|
| 242 |
+
font_size,
|
| 243 |
+
color=(0, 0, 0),
|
| 244 |
+
alignment="medium",
|
| 245 |
+
start_y=V_START,
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
content, background, next_height = write_text(
|
| 249 |
+
content,
|
| 250 |
+
font_path_SourceHanSans,
|
| 251 |
+
background,
|
| 252 |
+
H_PADDING_RATION,
|
| 253 |
+
100,
|
| 254 |
+
max_height=MAX_HEIGHT,
|
| 255 |
+
color=(0, 0, 0),
|
| 256 |
+
em_font_path=font_path_fangzhenghei,
|
| 257 |
+
alignment="left",
|
| 258 |
+
start_y=next_height + V_PADDING,
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
img_stream = io.BytesIO()
|
| 262 |
+
background.save(img_stream, format="JPEG")
|
| 263 |
+
img_stream.seek(0)
|
| 264 |
+
background.save("output/展商海报.jpg")
|
| 265 |
+
return img_stream
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def cut_image_blank(image):
|
| 269 |
+
# 获取图片的宽度和高度
|
| 270 |
+
width, height = image.size
|
| 271 |
+
# 初始化边界框
|
| 272 |
+
left = width
|
| 273 |
+
top = height
|
| 274 |
+
right = 0
|
| 275 |
+
bottom = 0
|
| 276 |
+
# 遍历图片的每个像素,找到非透明的区域
|
| 277 |
+
for x in range(width):
|
| 278 |
+
for y in range(height):
|
| 279 |
+
pixel = image.getpixel((x, y))
|
| 280 |
+
if pixel[3] != 0: # 检查透明度通道
|
| 281 |
+
if x < left:
|
| 282 |
+
left = x
|
| 283 |
+
if x > right:
|
| 284 |
+
right = x
|
| 285 |
+
if y < top:
|
| 286 |
+
top = y
|
| 287 |
+
if y > bottom:
|
| 288 |
+
bottom = y
|
| 289 |
+
if right < left or bottom < top:
|
| 290 |
+
return image
|
| 291 |
+
cropped_image = image.crop((left, top, right + 1, bottom + 1))
|
| 292 |
+
return cropped_image
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def rm_img_bg(image):
|
| 296 |
+
image = rembg.remove(image)
|
| 297 |
+
cropped_image = cut_image_blank(image)
|
| 298 |
+
return cropped_image
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def compose_guest_poster(name, name_pinyin, guest_photo, titles):
|
| 302 |
+
|
| 303 |
+
background = Image.open("src/assets/Guest04.jpg").convert("RGBA")
|
| 304 |
+
background = background.resize((1080 * 2, 2400 * 2))
|
| 305 |
+
|
| 306 |
+
title_list = titles.split("\n")
|
| 307 |
+
# guest = Image.open("src/assets/Guest.png").convert("RGBA")
|
| 308 |
+
# higher_mask = Image.open("src/assets/Guest01.png").convert("RGBA")
|
| 309 |
+
H_PADDING_RATION = 0.08
|
| 310 |
+
V_PADDING = 5
|
| 311 |
+
V_START = 3020
|
| 312 |
+
bg_width, bg_height = background.size
|
| 313 |
+
background = embed_image(background, guest_photo, offset_y=1300, standard_fg_size=(2160, 2600))
|
| 314 |
+
lower_mask = Image.open("src/assets/Guest03.png").convert("RGBA")
|
| 315 |
+
background = embed_image(background, lower_mask, standard_fg_size=background.size)
|
| 316 |
+
higher_mask = Image.open("src/assets/Guest02.png").convert("RGBA")
|
| 317 |
+
background = embed_image(background, higher_mask, standard_fg_size=background.size)
|
| 318 |
+
seal = Image.open("src/assets/Guest01.png").convert("RGBA")
|
| 319 |
+
background = embed_image(background, seal, standard_fg_size=background.size)
|
| 320 |
+
# background = background.convert("RGB")
|
| 321 |
+
|
| 322 |
+
font_size = 210
|
| 323 |
+
font_path = font_path_zihun59
|
| 324 |
+
text = name
|
| 325 |
+
name, background, next_height = write_text(
|
| 326 |
+
text,
|
| 327 |
+
font_path,
|
| 328 |
+
background,
|
| 329 |
+
H_PADDING_RATION,
|
| 330 |
+
font_size,
|
| 331 |
+
alignment="right",
|
| 332 |
+
start_y=V_START,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
next_height += 50
|
| 336 |
+
font_size = 100
|
| 337 |
+
font_path = font_path_notosans
|
| 338 |
+
if name_pinyin == "":
|
| 339 |
+
pinyin_list = pinyin(name.text, style=Style.NORMAL)
|
| 340 |
+
name_pinyin = (pinyin_list[0][0] + " " + "".join([item[0] for item in pinyin_list[1:]])).title()
|
| 341 |
+
text = name_pinyin
|
| 342 |
+
name_pinyin, background, next_height = write_text(
|
| 343 |
+
text,
|
| 344 |
+
font_path,
|
| 345 |
+
background,
|
| 346 |
+
H_PADDING_RATION,
|
| 347 |
+
font_size,
|
| 348 |
+
alignment="right",
|
| 349 |
+
start_y=next_height + V_PADDING,
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
next_height += 100
|
| 353 |
+
font_size = 70
|
| 354 |
+
font_path = font_path_notosans
|
| 355 |
+
for title in title_list:
|
| 356 |
+
text = title
|
| 357 |
+
title, background, next_height = write_text(
|
| 358 |
+
text,
|
| 359 |
+
font_path,
|
| 360 |
+
background,
|
| 361 |
+
H_PADDING_RATION,
|
| 362 |
+
font_size,
|
| 363 |
+
alignment="right",
|
| 364 |
+
start_y=next_height + V_PADDING,
|
| 365 |
+
)
|
| 366 |
+
background = background.convert("RGB")
|
| 367 |
+
img_stream = io.BytesIO()
|
| 368 |
+
background.save(img_stream, format="JPEG")
|
| 369 |
+
img_stream.seek(0)
|
| 370 |
if os.path.exists("src/assets/output"):
|
| 371 |
+
background.save("src/assets/output/嘉宾海报.jpg")
|
| 372 |
+
return img_stream
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
def make_guest_poster(name, name_pinyin, photo_link, original_photo_link, titles):
|
| 376 |
+
if photo_link != "":
|
| 377 |
+
res = requests.get(photo_link)
|
| 378 |
+
guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 379 |
+
elif original_photo_link != "":
|
| 380 |
+
res = requests.get(original_photo_link)
|
| 381 |
+
original_guest_photo = Image.open(io.BytesIO(res.content))
|
| 382 |
+
guest_photo = rm_img_bg(original_guest_photo)
|
| 383 |
+
if os.path.exists("src/assets/output"):
|
| 384 |
+
guest_photo.save("src/assets/output/rmbg.png")
|
| 385 |
else:
|
| 386 |
+
return None
|
| 387 |
+
img_stream = compose_guest_poster(name, name_pinyin, guest_photo, titles)
|
| 388 |
+
return img_stream
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def crop_round_img(image, circle_center, circle_radius):
|
| 392 |
+
mask = Image.new("L", image.size, 0)
|
| 393 |
+
draw = ImageDraw.Draw(mask)
|
| 394 |
+
draw.ellipse((circle_center[0] - circle_radius, circle_center[1] - circle_radius, circle_center[0] + circle_radius, circle_center[1] + circle_radius), fill=255)
|
| 395 |
+
output_image = Image.new("RGBA", image.size)
|
| 396 |
+
output_image.paste(image, (0, 0), mask)
|
| 397 |
+
output_image = output_image.crop(
|
| 398 |
+
(circle_center[0] - circle_radius, circle_center[1] - circle_radius, circle_center[0] + circle_radius, circle_center[1] + circle_radius)
|
| 399 |
+
)
|
| 400 |
+
# output_image = cut_image_blank(output_image)
|
| 401 |
+
|
| 402 |
if os.path.exists("src/assets/output"):
|
| 403 |
+
output_image.save("src/assets/output/cropped_circle_image.png")
|
| 404 |
+
return output_image
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
def make_conference_poster(conference_name, conference_name_eng, conference_name_full, conference_info, conference_rundown):
|
| 408 |
+
|
| 409 |
+
BACKGROUND_COLOR = (1, 6, 143)
|
| 410 |
+
|
| 411 |
+
background = Image.open("src/assets/schedule/top_background.jpg").convert("RGBA")
|
| 412 |
+
bar = Image.open("src/assets/schedule/bar.png").convert("RGBA")
|
| 413 |
+
point_bar = Image.open("src/assets/schedule/point_bar.png").convert("RGBA")
|
| 414 |
+
|
| 415 |
+
H_PADDING_RATION = 0.06
|
| 416 |
+
V_PADDING = 80
|
| 417 |
+
V_START = 1100
|
| 418 |
+
bg_width, bg_height = background.size
|
| 419 |
+
print(background.size)
|
| 420 |
+
print(point_bar.size)
|
| 421 |
+
schedule_top, top_next_height = make_conference_schedule_top(
|
| 422 |
+
background, conference_name, conference_name_eng, conference_name_full, conference_info, bar, H_PADDING_RATION, V_START
|
| 423 |
+
)
|
| 424 |
+
schedule_components = [schedule_top]
|
| 425 |
+
|
| 426 |
+
##########################################################
|
| 427 |
+
for rundown in conference_rundown:
|
| 428 |
+
rundown_pic = write_rundown(BACKGROUND_COLOR, rundown, point_bar, H_PADDING_RATION, bg_width)
|
| 429 |
+
# if rundown["type"] == "主持人":
|
| 430 |
+
# schedule_components = schedule_components[:1] + [rundown_pic] + schedule_components[1:]
|
| 431 |
+
# else:
|
| 432 |
+
schedule_components.append(rundown_pic)
|
| 433 |
+
widths, heights = zip(*(i.size for i in schedule_components))
|
| 434 |
+
schedule_poster = Image.new("RGB", (bg_width, sum(heights)))
|
| 435 |
+
x_offset = 0
|
| 436 |
+
for img in schedule_components:
|
| 437 |
+
schedule_poster.paste(img, (0, x_offset))
|
| 438 |
+
x_offset += img.size[1]
|
| 439 |
+
|
| 440 |
+
img_stream = io.BytesIO()
|
| 441 |
+
schedule_poster.save(img_stream, format="JPEG")
|
| 442 |
+
img_stream.seek(0)
|
| 443 |
+
if os.path.exists("src/assets/output"):
|
| 444 |
+
schedule_poster.save("src/assets/output/schedule.jpg")
|
| 445 |
+
return img_stream
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def write_rundown(BACKGROUND_COLOR, rundown, point_bar, H_PADDING_RATION, bg_width):
|
| 449 |
+
background = Image.new("RGB", (bg_width, 6000), BACKGROUND_COLOR)
|
| 450 |
+
next_height = 0
|
| 451 |
+
photo_size_ratio = 0.12
|
| 452 |
+
V_PADDING = 80
|
| 453 |
+
background = embed_image(
|
| 454 |
+
background,
|
| 455 |
+
point_bar,
|
| 456 |
+
offset_y = V_PADDING,
|
| 457 |
+
)
|
| 458 |
+
time_width = 0
|
| 459 |
+
font_size = 130
|
| 460 |
+
font_path = font_path_notosansM
|
| 461 |
+
# print(point_bar.size)
|
| 462 |
+
if rundown["type"] != "主持人":
|
| 463 |
+
text = rundown["start_time"][:-3] + "-" + rundown["end_time"][:-3]
|
| 464 |
+
text_ = text_info(text, font_size, [0, 0],
|
| 465 |
+
max_height=font_size,
|
| 466 |
+
max_width=bg_width * (1 - H_PADDING_RATION * 2.5),
|
| 467 |
+
font_path=font_path)
|
| 468 |
+
start_y = V_PADDING + (point_bar.height - text_.font_size) // 2
|
| 469 |
+
conference_rundown_time, background, next_height = write_text(
|
| 470 |
+
text, font_path, background, H_PADDING_RATION * 1.5, font_size,
|
| 471 |
+
start_y=start_y,
|
| 472 |
+
alignment="left"
|
| 473 |
+
)
|
| 474 |
+
time_width = conference_rundown_time.text_width
|
| 475 |
+
rundown["title"] = rundown["title"] if "title" in rundown else ""
|
| 476 |
|
| 477 |
+
text = rundown["type"] # rundown["title"] if rundown["type"] != "主持人" else "主持人"
|
| 478 |
+
pioneer_part_width = H_PADDING_RATION * 1.5 * bg_width + time_width + font_size // 2
|
| 479 |
+
text_ = text_info(
|
| 480 |
+
text,
|
| 481 |
+
font_size,
|
| 482 |
+
[0, 0],
|
| 483 |
+
max_height=font_size,
|
| 484 |
+
max_width=bg_width - pioneer_part_width - H_PADDING_RATION * bg_width, font_path=font_path)
|
| 485 |
+
start_y = V_PADDING + (point_bar.height - text_.font_size) // 2
|
| 486 |
+
conference_rundown_type, background, next_height = write_text(
|
| 487 |
+
text,
|
| 488 |
+
font_path,
|
| 489 |
+
background,
|
| 490 |
+
pioneer_part_width / bg_width,
|
| 491 |
+
font_size,
|
| 492 |
+
start_y=start_y,
|
| 493 |
+
max_width=bg_width - pioneer_part_width - H_PADDING_RATION * bg_width,
|
| 494 |
+
alignment="left",
|
| 495 |
+
)
|
| 496 |
|
| 497 |
|
| 498 |
+
next_height = V_PADDING + point_bar.size[1] + V_PADDING
|
| 499 |
+
|
| 500 |
+
if rundown["type"] != "主持人":
|
| 501 |
+
text = rundown["title"] # rundown["title"] if rundown["type"] != "主持人" else "主持人"
|
| 502 |
+
pioneer_part_width = H_PADDING_RATION * 1.5 * bg_width + time_width + font_size // 2
|
| 503 |
+
text_ = text_info(
|
| 504 |
+
text,
|
| 505 |
+
font_size,
|
| 506 |
+
[0, 0],
|
| 507 |
+
max_height=font_size,
|
| 508 |
+
max_width=bg_width - pioneer_part_width - H_PADDING_RATION * bg_width, font_path=font_path)
|
| 509 |
+
# start_y = V_PADDING + (point_bar.height - text_.font_size) // 2
|
| 510 |
+
conference_rundown_type, background, next_height = write_text(
|
| 511 |
+
text,
|
| 512 |
+
font_path,
|
| 513 |
+
background,
|
| 514 |
+
pioneer_part_width / bg_width,
|
| 515 |
+
font_size,
|
| 516 |
+
start_y=next_height,
|
| 517 |
+
max_width=bg_width - 2 * H_PADDING_RATION * bg_width,
|
| 518 |
+
alignment="medium",
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
for person in rundown["people"]:
|
| 523 |
+
# if person["photo_link"] != "":
|
| 524 |
+
next_height += V_PADDING
|
| 525 |
+
background, next_height = write_person_line(background, person, H_PADDING_RATION, next_height, photo_size_ratio)
|
| 526 |
+
|
| 527 |
+
background = background.convert("RGB")
|
| 528 |
+
background = background.crop((0, 0, background.size[0], next_height + V_PADDING))
|
| 529 |
+
if os.path.exists("src/assets/output"):
|
| 530 |
+
background.save("src/assets/output/schedule_rundown.jpg")
|
| 531 |
+
return background
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def write_person_line(background, person, H_PADDING_RATION, start_y, photo_size_ratio):
|
| 535 |
+
bg_width = background.size[0]
|
| 536 |
+
text = person["name"]
|
| 537 |
+
font_size = 120
|
| 538 |
+
font_path = font_path_zihun59
|
| 539 |
+
alignment = "left"
|
| 540 |
+
pioneer_part_width = H_PADDING_RATION * bg_width + font_size
|
| 541 |
+
|
| 542 |
+
radius = int(photo_size_ratio * bg_width)
|
| 543 |
+
if person["photo_link"] != "":
|
| 544 |
+
round_photo = write_round_photo(background, person, H_PADDING_RATION * bg_width + radius, start_y + radius, radius)
|
| 545 |
+
pioneer_part_width += round_photo.size[1]
|
| 546 |
+
|
| 547 |
+
person["title"] = person["title"].split("\n")
|
| 548 |
+
|
| 549 |
+
name_padding = 50
|
| 550 |
+
title_padding = 20
|
| 551 |
+
|
| 552 |
+
people_text_y = start_y + (2 * radius - font_size - name_padding - 80 * len(person["title"])) // 2
|
| 553 |
+
conference_rundown_people_name, background, next_height = write_text(
|
| 554 |
+
text,
|
| 555 |
+
font_path,
|
| 556 |
+
background,
|
| 557 |
+
pioneer_part_width / bg_width,
|
| 558 |
+
font_size,
|
| 559 |
+
start_y=people_text_y,
|
| 560 |
+
max_width=bg_width - pioneer_part_width - H_PADDING_RATION * bg_width,
|
| 561 |
+
alignment=alignment
|
| 562 |
+
)
|
| 563 |
+
next_height += name_padding
|
| 564 |
+
font_size = 80
|
| 565 |
+
font_path = font_path_notosansM
|
| 566 |
+
# pioneer_part_width = H_PADDING_RATION * bg_width + round_photo.size[1] + font_size
|
| 567 |
+
for i, text in enumerate(person["title"]):
|
| 568 |
+
conference_rundown_people_title, background, next_height = write_text(
|
| 569 |
+
text,
|
| 570 |
+
font_path,
|
| 571 |
+
background,
|
| 572 |
+
pioneer_part_width / bg_width,
|
| 573 |
+
font_size,
|
| 574 |
+
start_y=next_height + title_padding,
|
| 575 |
+
max_width=bg_width - pioneer_part_width - H_PADDING_RATION * bg_width,
|
| 576 |
+
max_height=radius * 1.2,
|
| 577 |
+
alignment=alignment,
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
return background, start_y + radius * 2
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
def write_round_photo(background, person, start_x, start_y, radius):
|
| 584 |
+
photo = Image.new("RGBA", (100, 100), (125, 125, 125, 0))
|
| 585 |
+
res = requests.get(person["photo_link"])
|
| 586 |
+
photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 587 |
+
width, height = photo.size
|
| 588 |
+
circle_center = (width // 2, height // 4)
|
| 589 |
+
circle_radius = min(width, height // 2) // 2
|
| 590 |
+
round_photo = crop_round_img(photo, circle_center, circle_radius)
|
| 591 |
+
round_photo = round_photo.resize((radius*2, radius*2))
|
| 592 |
+
background = embed_image(background, round_photo, offset_y=start_y-radius, offset_x=start_x-radius)
|
| 593 |
+
|
| 594 |
+
return round_photo
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
def make_conference_schedule_top(background, conference_name, conference_name_eng, conference_name_full, conference_info, bar, H_PADDING_RATION, V_START):
|
| 598 |
+
font_size = 250
|
| 599 |
+
font_path = font_path_pingfangM
|
| 600 |
+
text = conference_name
|
| 601 |
+
conference_name, background, next_height = write_text(
|
| 602 |
+
text,
|
| 603 |
+
font_path,
|
| 604 |
+
background,
|
| 605 |
+
H_PADDING_RATION,
|
| 606 |
+
font_size,
|
| 607 |
+
start_y=V_START,
|
| 608 |
+
alignment="left",
|
| 609 |
+
)
|
| 610 |
+
|
| 611 |
+
font_size = 110
|
| 612 |
+
font_path = font_path_notosans
|
| 613 |
+
text = conference_name_eng
|
| 614 |
+
V_PADDING = 120
|
| 615 |
+
conference_name_eng, background, next_height = write_text(
|
| 616 |
+
text, font_path, background, H_PADDING_RATION, font_size, start_y=next_height + V_PADDING, alignment="left"
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
font_size = 160
|
| 620 |
+
font_path = font_path_notosansM
|
| 621 |
+
text = "——" + conference_name_full + "——"
|
| 622 |
+
V_PADDING = 180
|
| 623 |
+
conference_name_full, background, next_height = write_text(
|
| 624 |
+
text, font_path, background, H_PADDING_RATION, font_size, start_y=next_height + V_PADDING, alignment="medium"
|
| 625 |
+
)
|
| 626 |
+
# write_text(" ", font_path, background, H_PADDING_RATION, 200, start_height=next_height + 400, alignment="medium")
|
| 627 |
+
INNER_PADDING = 60
|
| 628 |
+
next_height = next_height + V_PADDING - INNER_PADDING
|
| 629 |
+
background, next_height = write_conference_info(conference_info, background, bar, H_PADDING_RATION, INNER_PADDING, next_height)
|
| 630 |
+
background = background.convert("RGB")
|
| 631 |
+
background = background.crop((0, 0, background.size[0], next_height + V_PADDING))
|
| 632 |
+
if os.path.exists("src/assets/output"):
|
| 633 |
+
background.save("src/assets/output/schedule_top.jpg")
|
| 634 |
+
|
| 635 |
+
return background, next_height
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
def write_conference_info(conference_info, background, bar, H_PADDING_RATION, V_PADDING, next_height):
|
| 639 |
+
bg_width, vg_height = background.size
|
| 640 |
+
for i, info in enumerate(conference_info):
|
| 641 |
+
font_size = 110
|
| 642 |
+
font_path = font_path_notosansB
|
| 643 |
+
text = info[0]
|
| 644 |
+
bar = bar.resize((int(bg_width * 0.06), int(font_size * 0.7)))
|
| 645 |
+
# V_PADDING = 180
|
| 646 |
+
info_name, background, next_height = write_text(
|
| 647 |
+
text, font_path, background, H_PADDING_RATION, font_size, start_y=next_height + V_PADDING, alignment="left"
|
| 648 |
+
)
|
| 649 |
+
background = embed_image(
|
| 650 |
+
background, bar, offset_y=next_height - (bar.size[1] + info_name.font_size) // 2, offset_x=bg_width * H_PADDING_RATION + info_name.text_width
|
| 651 |
+
)
|
| 652 |
+
|
| 653 |
+
font_size = 80
|
| 654 |
+
text = info[1]
|
| 655 |
+
pioneer_part_width = bar.size[0] + info_name.text_width + font_size
|
| 656 |
+
info_content, background, next_height = write_text(
|
| 657 |
+
text,
|
| 658 |
+
font_path,
|
| 659 |
+
background,
|
| 660 |
+
H_PADDING_RATION + pioneer_part_width / bg_width,
|
| 661 |
+
font_size,
|
| 662 |
+
max_width=bg_width * (1 - 2 * H_PADDING_RATION) - info_name.text_width,
|
| 663 |
+
start_y=next_height - (font_size + info_name.font_size) // 2,
|
| 664 |
+
alignment="left",
|
| 665 |
+
)
|
| 666 |
+
|
| 667 |
+
return background, next_height
|
| 668 |
+
|
| 669 |
+
|
| 670 |
+
def getGuestComposePoster(conference_name, conference_eng_name, people):
|
| 671 |
+
|
| 672 |
+
top = Image.open("src/assets/GuestComposePoster/top.png").convert("RGBA")
|
| 673 |
+
card = Image.open("src/assets/GuestComposePoster/card.png").convert("RGBA")
|
| 674 |
+
V_PADDING = 80
|
| 675 |
+
background = Image.new("RGB", (top.size[0], card.size[1] * math.ceil(len(people) / 3) + V_PADDING), (255, 255, 255))
|
| 676 |
+
bg_width, bg_height = background.size
|
| 677 |
+
H_PADDING = (bg_width - 3*card.size[0]) / 2
|
| 678 |
+
V_START = 1200
|
| 679 |
+
print(top.size)
|
| 680 |
+
print(background.size)
|
| 681 |
+
|
| 682 |
+
font_size = 220
|
| 683 |
+
font_path = font_path_notosansL
|
| 684 |
+
text = conference_name
|
| 685 |
+
conference_name, top, next_height = write_text(
|
| 686 |
+
text,
|
| 687 |
+
font_path,
|
| 688 |
+
top,
|
| 689 |
+
H_PADDING / bg_width,
|
| 690 |
+
font_size,
|
| 691 |
+
start_y=V_START,
|
| 692 |
+
alignment="medium",
|
| 693 |
+
)
|
| 694 |
+
font_size = 250
|
| 695 |
+
font_path = font_path_notosansL
|
| 696 |
+
text = conference_eng_name
|
| 697 |
+
conference_eng_name, top, next_height = write_text(
|
| 698 |
+
text,
|
| 699 |
+
font_path,
|
| 700 |
+
top,
|
| 701 |
+
H_PADDING / bg_width,
|
| 702 |
+
font_size,
|
| 703 |
+
start_y=next_height + V_PADDING,
|
| 704 |
+
max_height=700,
|
| 705 |
+
alignment="medium",
|
| 706 |
+
)
|
| 707 |
+
|
| 708 |
+
for (i, person) in enumerate(people):
|
| 709 |
+
background = write_person_card(background, card, person, H_PADDING + card.size[0] * (i % 3), card.height * (i // 3))
|
| 710 |
+
|
| 711 |
+
guest_compose_poster = Image.new("RGB", (bg_width, background.size[1] + top.size[1]))
|
| 712 |
+
guest_compose_poster.paste(top, (0, 0))
|
| 713 |
+
guest_compose_poster.paste(background, (0, top.size[1]))
|
| 714 |
+
img_stream = io.BytesIO()
|
| 715 |
+
guest_compose_poster.save(img_stream, format="JPEG")
|
| 716 |
+
img_stream.seek(0)
|
| 717 |
+
if os.path.exists("src/assets/output"):
|
| 718 |
+
guest_compose_poster.save("src/assets/output/guest_compose_poster.jpg")
|
| 719 |
+
return img_stream
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
def write_person_card(background, card, person, H_PADDING, start_y):
|
| 723 |
+
bg_width = background.size[0]
|
| 724 |
+
# print(bg_width * photo_size_ratio)
|
| 725 |
+
white_bar = 70
|
| 726 |
+
radius = int(0.67 * card.width) // 2
|
| 727 |
+
|
| 728 |
+
round_photo = write_round_photo(background, person, H_PADDING + card.width // 2, start_y+white_bar+radius, radius)
|
| 729 |
+
background = embed_image(background, card, offset_y=start_y, offset_x=H_PADDING)
|
| 730 |
+
|
| 731 |
+
text = person["name"]
|
| 732 |
+
font_size = 120
|
| 733 |
+
font_path = font_path_zihun59
|
| 734 |
+
pioneer_part_width = H_PADDING
|
| 735 |
+
people_text_y = start_y + 2 * radius + white_bar + 20
|
| 736 |
+
guest_compose_poster_people_name, background, next_height = write_text(
|
| 737 |
+
text, font_path, background,
|
| 738 |
+
pioneer_part_width / bg_width,
|
| 739 |
+
font_size,
|
| 740 |
+
start_y=people_text_y,
|
| 741 |
+
start_x=H_PADDING,
|
| 742 |
+
part_width=card.width,
|
| 743 |
+
max_width=card.width,
|
| 744 |
+
alignment="medium",
|
| 745 |
+
# color=(0,0,0)
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
+
text = person["title"]
|
| 749 |
+
text = "\n".join(text.split("\n")[:5])
|
| 750 |
+
font_size = 40
|
| 751 |
+
font_path = font_path_notosansM
|
| 752 |
+
next_height += 30
|
| 753 |
+
for i, text in enumerate(text.split("\n")):
|
| 754 |
+
pioneer_part_width = H_PADDING
|
| 755 |
+
people_text_y = next_height + 10
|
| 756 |
+
guest_compose_poster_people_title, background, next_height = write_text(
|
| 757 |
+
text, font_path, background,
|
| 758 |
+
pioneer_part_width / bg_width,
|
| 759 |
+
font_size,
|
| 760 |
+
start_y=people_text_y,
|
| 761 |
+
start_x=H_PADDING,
|
| 762 |
+
part_width=card.width,
|
| 763 |
+
max_width=card.width-white_bar,
|
| 764 |
+
alignment="medium",
|
| 765 |
+
# color=(0,0,0)
|
| 766 |
+
)
|
| 767 |
+
|
| 768 |
+
return background
|
| 769 |
|
| 770 |
|
| 771 |
if __name__ == "__main__":
|
| 772 |
+
# image_path = 'src/assets/邀请函封面.jpg' # 替换为你的图片文件路径
|
| 773 |
+
# text_to_write = "你好,世界!"
|
| 774 |
+
# font_path = "src/assets/FangZhengHeiTiJianTi-1.ttf"
|
| 775 |
+
# position = (120, 140)
|
| 776 |
+
from collections import namedtuple
|
| 777 |
+
|
| 778 |
+
Person = namedtuple("Person", ["name", "title", "category", "conference", "respond_person"])
|
| 779 |
+
person = Person(name="一二三四五六", title="先生", category="演讲", conference="8月21日中欧智慧论坛,做主题演讲", respond_person="Eazy")
|
| 780 |
+
output_path = "output/output.pdf"
|
| 781 |
+
# writeInvitationPDF(person, debug=True)
|
| 782 |
+
|
| 783 |
+
text_list = [
|
| 784 |
+
"""Shanghai 协砺科技有限公司专注于青少年科技教育12年,通过STEAM25教育模式培养青少年的科学思维和创新能力。在全国设有数十家学习中心,服务上万名学员,提供科创、机器人和编程课程。公司还设有赛事研究中心,由专业工程师团队提供赛事研究和策略支持。学员在各类科技赛事中屡获佳绩。协砺科技致力于推动中国科技教育的创新与发展,为国家繁荣贡献力量。
|
| 785 |
+
|
| 786 |
+
【主要产品】科创、编程、赛事
|
| 787 |
+
【核心特点】为青少年提供优质的科创学习规划。
|
| 788 |
+
【核心亮点】高效提供更优质的赛事解决方案——快速赋能当地传统教育企业转型智能化教育赛道。""",
|
| 789 |
+
"""天天乐学创办于2015年6月,总部位于北京,创始团队均来自清北等知名高校,旗下产品天天乐学定制app,主打立体化教学和趣味性学习,可深度匹配教学需求,实现个性化教学。目前合作校近三万家,遍布全国31个省,300座城市,是国内知名的教育科技公司。
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
【主要产品】独立教师·机构·绘本馆英语APP定制
|
| 793 |
+
【核心特点】内容深度定制,帮助机构实现线上线下联动教学
|
| 794 |
+
""",
|
| 795 |
+
"""【】星瀚之光是明德传承集团旗下文化传媒品牌
|
| 796 |
+
成立于2023年,立足深圳,布局全国九大中心城市,是国内首家定位于价值观驱动的P产业链开发的MCN机构,拥有知识P孵化事业群和商业P孵化事业群两大主营板块,旗下涵盖了创始人P孵化直播电商、私域消费电商、企业整合营销、艺人经纪等业务,致力于成为中国规模最大的P生态搭建领军企业。
|
| 797 |
+
|
| 798 |
+
【】坚守“自利利他、奋斗为本、价值为纲、成就客户”的企业价值观
|
| 799 |
+
公司成立以来发展迅猛,迅速孵化出各大赛道的多个头部P,霸榜各大短视频平台榜单,包括:有家庭教育赛道:白瑞(家庭教育赛道头部主播);身心灵赛道:尚致胜(新中式心理学首创者);女性成长赛道:黄欢;商业赛道:苏建诚(亚细亚集团联合创始人)、王怀南(宝宝树创始人)、杨晓燕(长江商学院助理院长)、何慕(联众智达董事长)等;国学赛道:沈德斌(了凡四训学者)、钱锦国老师等。""",
|
| 800 |
+
"""学趋坊是一家为学校、机构、智能自习室等提供在线化和智能化解决方案的数字化教育内容供应商。已经为多个知名的在线教育品牌、智能自习室品牌提供0DM和OEM服务,产品用户累计超过1000万。
|
| 801 |
+
|
| 802 |
+
【主要产品】智能校本
|
| 803 |
+
【核心特点】为本地化内容插上科技的翅膀。
|
| 804 |
+
【核心亮点】高质量高效本地化教研—快速赋能当地传统教育企业转型智能化教育赛道。""",
|
| 805 |
+
]
|
| 806 |
+
logo_path_list = [
|
| 807 |
+
"src/assets/logo/学区房logo.png",
|
| 808 |
+
"src/assets/logo/星瀚之光logo.jpg",
|
| 809 |
+
"src/assets/logo/软科起点logo.png",
|
| 810 |
+
"src/assets/logo/天天乐学logo.png",
|
| 811 |
+
]
|
| 812 |
+
slogan_list = ["探索科技,启迪未来。", "价值观驱动的知识P全域孵化领先平台", "定制英语APP学习系统"]
|
| 813 |
+
FOREGROUND_ID = 3 # 0, 1, 2, 3
|
| 814 |
+
SLOGAN_ID = 1 # 0, 1, 2
|
| 815 |
+
TEXT_ID = 2 # 0, 1, 2, 3
|
| 816 |
+
|
| 817 |
+
LOGO_LINK = "https://weboffice-temporary.ks3-cn-beijing.wpscdn.cn/thumbnail/tJUjbgWdD6R5k5rECKka1yK_100.png?Expires=1721322000&KSSAccessKeyId=AKLTmoJhggaFT1CHuozGZqbC&Signature=qZCdG1n42uwCFGJ8%2BAlF6VK8KEM%3D&response-cache-control=public%2Cmax-age%3D86400"
|
| 818 |
+
|
| 819 |
+
name, titles = "王千里", "全国政协文员\n好好先生"
|
| 820 |
+
PHOTO_LINK = "https://weboffice-temporary.ks3-cn-beijing.wpscdn.cn/thumbnail/tpcdnbPEKEJ3ge831dSHEd3_100.png?Expires=1720890000&KSSAccessKeyId=AKLTmoJhggaFT1CHuozGZqbC&Signature=jeKkEy%2BTx3BAMNzqrmjUbg6uBK8%3D&response-cache-control=public%2Cmax-age%3D86400"
|
| 821 |
+
ORIGINAL_PHOTO_LINK = "https://weboffice-temporary.ks3-cn-beijing.wpscdn.cn/thumbnail/tZCeTRiNFQbksEEF4WMR8ZX_100.jpeg?Expires=1721743200&KSSAccessKeyId=AKLTmoJhggaFT1CHuozGZqbC&Signature=CvxnLBCIf%2B0aXEfaJ9XK6Vq%2FXU8%3D&response-cache-control=public%2Cmax-age%3D86400"
|
| 822 |
+
|
| 823 |
+
conference_name = "教育行业规范管理发展论坛"
|
| 824 |
+
conference_name_eng = "2024 World Education Conference"
|
| 825 |
+
conference_name_full = "2024世界教育者大会上海主会"
|
| 826 |
+
conference_info = [
|
| 827 |
+
["指导单位", "上海市民办教育协会、长三角教育发展研究院"],
|
| 828 |
+
["主办单位", "上海市民办教育协会、长三角教育发展研究院"],
|
| 829 |
+
["论坛时间", "2024年8月21日上午9:00-12:30"],
|
| 830 |
+
["论坛地址", "国家会展中心4.2号馆A"],
|
| 831 |
+
]
|
| 832 |
+
|
| 833 |
+
conference_rundown = [
|
| 834 |
+
{
|
| 835 |
+
"type": "主持人",
|
| 836 |
+
"title": "上海市教科规划办副主任",
|
| 837 |
+
"people": [
|
| 838 |
+
{
|
| 839 |
+
"name": "方法名",
|
| 840 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 841 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 842 |
+
},
|
| 843 |
+
{
|
| 844 |
+
"name": "方法名",
|
| 845 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 846 |
+
"photo_link": "",
|
| 847 |
+
},
|
| 848 |
+
],
|
| 849 |
+
"start_time": "9:00:00",
|
| 850 |
+
"end_time": "9:20:00",
|
| 851 |
+
},
|
| 852 |
+
{
|
| 853 |
+
"type": "致辞",
|
| 854 |
+
"title": "上海市教科规划办副主",
|
| 855 |
+
"people": [
|
| 856 |
+
{
|
| 857 |
+
"name": "方法名",
|
| 858 |
+
"title": "上海市教科规划办副主任",
|
| 859 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 860 |
+
}
|
| 861 |
+
],
|
| 862 |
+
"start_time": "9:00:00",
|
| 863 |
+
"end_time": "9:20:00",
|
| 864 |
+
}, {
|
| 865 |
+
"type": "致辞",
|
| 866 |
+
"title": "上海市教科规划办副主任上海市教科规划办副主任上海市教科规",
|
| 867 |
+
"people": [
|
| 868 |
+
{
|
| 869 |
+
"name": "方法名",
|
| 870 |
+
"title": "上海市教科规划办副主任",
|
| 871 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 872 |
+
}
|
| 873 |
+
],
|
| 874 |
+
"start_time": "9:00:00",
|
| 875 |
+
"end_time": "9:20:00",
|
| 876 |
+
},
|
| 877 |
+
]
|
| 878 |
+
people = [
|
| 879 |
+
{
|
| 880 |
+
"name": "方法名",
|
| 881 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 882 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 883 |
+
},
|
| 884 |
+
{
|
| 885 |
+
"name": "方法名",
|
| 886 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 887 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 888 |
+
},
|
| 889 |
+
{
|
| 890 |
+
"name": "方法名",
|
| 891 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 892 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 893 |
+
},
|
| 894 |
+
{
|
| 895 |
+
"name": "方法名",
|
| 896 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 897 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 898 |
+
},
|
| 899 |
+
{
|
| 900 |
+
"name": "方法名",
|
| 901 |
+
"title": "上海市教科规划办副主任\n上海教科院改革发展研究部研究员\n华东师范大学国家宏观教育研究院博士生导师",
|
| 902 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 903 |
+
},
|
| 904 |
+
{
|
| 905 |
+
"name": "方法名",
|
| 906 |
+
"title": "上海市教科规划办副主任",
|
| 907 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 908 |
+
},
|
| 909 |
+
{
|
| 910 |
+
"name": "方法名",
|
| 911 |
+
"title": "上海市教科规划办副主任",
|
| 912 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 913 |
+
},
|
| 914 |
+
{
|
| 915 |
+
"name": "方法名",
|
| 916 |
+
"title": "上海市教科规划办副主任",
|
| 917 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 918 |
+
},
|
| 919 |
+
{
|
| 920 |
+
"name": "方法名",
|
| 921 |
+
"title": "上海市教科规划办副主任",
|
| 922 |
+
"photo_link": ORIGINAL_PHOTO_LINK
|
| 923 |
+
},
|
| 924 |
+
{
|
| 925 |
+
"name": "方法名",
|
| 926 |
+
"title": "上海市教科规划办副主任",
|
| 927 |
+
"photo_link": ORIGINAL_PHOTO_LINK,
|
| 928 |
+
},
|
| 929 |
+
]
|
| 930 |
+
|
| 931 |
+
# res = requests.get(ORIGINAL_PHOTO_LINK)
|
| 932 |
+
# # guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 933 |
+
# original_guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 934 |
+
# guest_photo = rm_img_bg(original_guest_photo)
|
| 935 |
+
|
| 936 |
+
# compose_guest_poster(name, guest_photo, titles)
|
| 937 |
+
|
| 938 |
+
# for file in os.listdir("src/assets/GuestPoster/original_guest"):
|
| 939 |
+
# image = Image.open(os.path.join("src/assets/GuestPoster/original_guest", file))
|
| 940 |
+
# image = rm_img_bg(image)
|
| 941 |
+
# image.save(os.path.join("src/assets/output", file.replace("jpg", "png")))
|
| 942 |
+
|
| 943 |
+
# image = Image.open('src/assets/苏建诚.png')
|
| 944 |
+
# width, height = image.size
|
| 945 |
+
# circle_center = (width // 2, height // 4)
|
| 946 |
+
# circle_radius = min(width, height//2) // 2
|
| 947 |
+
# round_img = crop_round_img(image, circle_center, circle_radius)
|
| 948 |
+
# print(round_img.size)
|
| 949 |
+
|
| 950 |
+
# res = requests.get(ORIGINAL_PHOTO_LINK)
|
| 951 |
+
# photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 952 |
+
|
| 953 |
+
# make_exhibitor_poster(LOGO_LINK, slogan_list[SLOGAN_ID], text_list[TEXT_ID])
|
| 954 |
+
# make_guest_poster(name, "", "", ORIGINAL_PHOTO_LINK, titles)
|
| 955 |
+
|
| 956 |
+
# make_conference_poster(conference_name, conference_name_eng, conference_name_full, conference_info, conference_rundown)
|
| 957 |
+
# 定义圆心和半径
|
| 958 |
+
|
| 959 |
+
# res = requests.get(ORIGINAL_PHOTO_LINK)
|
| 960 |
+
# print(res)
|
| 961 |
+
# with open("src/assets/output/guest_compose_poster.png", "wb") as f:
|
| 962 |
+
# f.write(res.content)
|
| 963 |
+
# photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
|
| 964 |
+
|
| 965 |
+
|
| 966 |
+
getGuestComposePoster("全球教育者公论教育", "EDUCATORS AROUND THE GLOBE TALKING ABOUT EDUCATION", people)
|