IsidoreSong commited on
Commit
658176d
·
verified ·
1 Parent(s): 6d907df

Update src/pyscripts/img_process.py

Browse files
Files changed (1) hide show
  1. src/pyscripts/img_process.py +955 -168
src/pyscripts/img_process.py CHANGED
@@ -1,179 +1,966 @@
1
- from fastapi import FastAPI, Path, Query, Response, BackgroundTasks
2
- from fastapi.responses import FileResponse, HTMLResponse
3
- import gradio as gr
4
- import sys
5
  import os
6
- project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
7
- sys.path.append(project_root)
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
- # from easydict import EasyDict
14
-
15
- Person = namedtuple('Person', ['name', 'title', 'category', 'conference', 'respond_person'])
16
-
17
- CUSTOM_PATH = "/gradio"
18
-
19
- app = FastAPI()
20
-
21
- pdf_path = "src/assets/InvitationTemplate.pdf"
22
- output_path = 'output/output.pdf'
23
- person = Person(name="雷军", title="先生", category="观礼", conference="世界教育者大会", respond_person="Eazy")
24
-
25
- # data = {
26
- # 'name': '雷军',
27
- # 'title': '先生',
28
- # "conference": "世界教育者大会"
29
- # }
30
-
31
-
32
- @app.get("/")
33
- def read_main():
34
- return {"message": "This is your main app"}
35
-
36
- # @app.post("/getInvitationPDF/{name}/{title}/{conference}")
37
- @app.get("/getInvitationPDF")
38
- def getInvitationPDF(name, title, conference, category, respond_person):
39
- person = Person(name=name, title=title, category=category, conference=conference, respond_person=respond_person)
40
- formatted_date = datetime.now().strftime('%Y-%m-%d')
41
- file_name = f"致{name+title}的WWEC2024邀请函.pdf"
42
- file_dir = f"invitation/{category}"
43
- object_name = file_dir + "/" + file_name
44
- pdfStream = file_process.writeInvitationPDF(person)
45
- # with open(f"output/{file_name}", 'wb') as f:
46
- # f.write(pdfStream.getvalue())
47
- oss.upload_file_stream_to_s3(pdfStream, "host", object_name)
48
- file_url = f"https://isidoresong.us.kg/{object_name}"
49
- return {"url": file_url}
50
-
51
- class GuestPosterInfo(BaseModel):
52
- exhibitor: str
53
- logo_link: str
54
- slogan: str
55
- content: str
56
-
57
- @app.post("/getExhibitorPoster")
58
- def getExhibitorPoster(item: GuestPosterInfo):
59
- logo_link = item.logo_link
60
- exhibitor = item.exhibitor
61
- slogan = item.slogan
62
- content = item.content
63
- img_stream = img_process.make_exhibitor_poster(logo_link, slogan, content)
64
- file_dir = "ExhibitorPoster"
65
- object_name = file_dir + "/" + exhibitor + ".jpg"
66
- # with open(f"output/{exhibitor + '.jpg'}", 'wb') as f:
67
- # f.write(img_stream.getvalue())
68
- oss.upload_file_stream_to_s3(img_stream, "host", object_name)
69
- file_url = f"https://isidoresong.us.kg/{object_name}"
70
- return {"url": file_url}
71
-
72
-
73
- class GuestPosterInfo(BaseModel):
74
- name: str
75
- name_pinyin: str
76
- photo_link: str
77
- original_photo_link: str
78
- titles: str
79
-
80
- @app.post("/getGuestPoster")
81
- def getGuestPoster(item: GuestPosterInfo):
82
- photo_link = item.photo_link
83
- original_photo_link = item.original_photo_link
84
- name = item.name
85
- name_pinyin = item.name_pinyin
86
- titles = item.titles
87
- img_stream = img_process.make_guest_poster(name, name_pinyin, photo_link, original_photo_link, titles)
88
- file_dir = "GuestPoster"
89
- object_name = file_dir + "/" + name + ".jpg"
90
- # f.write(img_stream.getvalue())
91
- oss.upload_file_stream_to_s3(img_stream, "host", object_name)
92
- file_url = f"https://pub-429c75a96a8f4597984dd7ebc525d652.r2.dev/{object_name}"
93
- return {"url": file_url}
94
-
95
- class ConferenceInfo(BaseModel):
96
- name: str
97
- eng_name: str
98
- full_name: str
99
- start_time: str
100
- end_time: str
101
- member: list
102
- guest: list = None
103
-
104
- class ConferencePosterInfo(BaseModel):
105
- conference_info: ConferenceInfo
106
- conference_rundown:list
107
-
108
-
109
- def format_time(start_time, end_time):
110
- start_datetime = datetime.strptime(start_time, "%Y/%m/%d %H:%M:%S")
111
- end_datetime = datetime.strptime(end_time, "%Y/%m/%d %H:%M:%S")
112
-
113
- start_period = "上午" if start_datetime.hour < 12 else "下午"
114
- end_period = "上午" if end_datetime.hour < 12 else "下午"
115
-
116
- formatted_start_time = start_datetime.strftime(f"%Y年%m月%d日{start_period}%H:%M")
117
- formatted_end_time = end_datetime.strftime(f"{end_period}%H:%M")
118
-
119
- return f"{formatted_start_time}-{formatted_end_time}"
120
-
121
-
122
- @app.post("/getConferencePoster")
123
- def getConferencePoster(item: ConferencePosterInfo):
124
- # item.conference_rundown = [list(i) for i in item.conference_rundown]
125
- sorted_conference_rundown = sorted(item.conference_rundown, key=lambda x: x['end_time'])
126
- img_stream = img_process.make_conference_poster(
127
- item.conference_info["name"],
128
- item.conference_info["eng_name"],
129
- item.conference_info["full_name"],
130
- [
131
- *item.conference_info["member"],
132
- ["论坛时间", format_time(item.conference_info["start_time"],
133
- item.conference_info["end_time"])],
134
- ["论坛地点", "国家会展中心4.2号馆 " + item.conference_info["address"]]
135
- ],
136
- sorted_conference_rundown)
137
- file_dir = "ConferencePoster"
138
- object_name = file_dir + "/" + item.conference_info["name"] + ".jpg"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  if os.path.exists("src/assets/output"):
140
- with open(f"src/assets/output/{item.conference_info['name'] + '.jpg'}", 'wb') as f:
141
- f.write(img_stream.getvalue())
 
 
 
 
 
 
 
 
 
 
 
 
142
  else:
143
- oss.upload_file_stream_to_s3(img_stream, "host", object_name)
144
- file_url = f"https://pub-429c75a96a8f4597984dd7ebc525d652.r2.dev/{object_name}"
145
- return {"url": file_url}
146
-
147
- @app.post("/getGuestComposePoster")
148
- def getGuestComposePoster(item:ConferenceInfo):
149
- # item.conference_rundown = [list(i) for i in item.conference_rundown]
150
- sorted_conference_guest = sorted(item.guest, key=lambda x: x['rank'])
151
- img_stream = img_process.getGuestComposePoster(
152
- item.name,
153
- item.eng_name,
154
- sorted_conference_guest)
155
- file_dir = "ConferenceGuestPoster"
156
- object_name = file_dir + "/" + item.name + ".jpg"
 
 
157
  if os.path.exists("src/assets/output"):
158
- with open(f"src/assets/output/{item.name + '.jpg'}", 'wb') as f:
159
- f.write(img_stream.getvalue())
160
- else:
161
- oss.upload_file_stream_to_s3(img_stream, "host", object_name)
162
- file_url = f"https://pub-429c75a96a8f4597984dd7ebc525d652.r2.dev/{object_name}"
163
- return {"url": file_url}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
 
167
- interface = gr.Interface(lambda x: "Hello, " + x + "!", "textbox", "textbox")
168
- app = gr.mount_gradio_app(app, interface, path=CUSTOM_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
 
171
  if __name__ == "__main__":
172
- MOCK_URL = "https://mock.jsont.run/iKAdks2DIVBtx-R7Fbw3y"
173
- res = requests.get(MOCK_URL)
174
- mock_data = res.json()
175
- # mock_data = ConferencePosterInfo(**mock_data)
176
- # getConferencePoster(mock_data)
177
- print(mock_data)
178
- mock_data = ConferenceInfo(**mock_data["conference_info"])
179
- getGuestComposePoster(mock_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)