IsidoreSong commited on
Commit
af5c3bb
·
verified ·
1 Parent(s): 3c19c13

Update src/pyscripts/run.py

Browse files
Files changed (1) hide show
  1. src/pyscripts/run.py +90 -374
src/pyscripts/run.py CHANGED
@@ -1,374 +1,90 @@
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
-
10
-
11
- font_path_fangzhenghei = "src/assets/FangZhengHeiTiJianTi-1.ttf"
12
- font_path_zihun59 = "src/assets/字魂59号-创粗黑.ttf"
13
- font_path_notosans = "src/assets/NotoSansHans-Regular.otf"
14
- font_path_feihuasong = "src/assets/FeiHuaSongTi-2.ttf"
15
- font_path_wendingPLjianbaosong = "src/assets/gbsn00lp-2.ttf"
16
- font_path_SourceHanSans = "src/assets/SourceHanSansCN-VF-2.otf"
17
-
18
-
19
-
20
-
21
- @dataclass
22
- class text_info:
23
- text: str
24
- font_size: int
25
- position: list
26
- color: tuple= (0,0,0)
27
- max_height: int=1000
28
- max_width: int=0
29
- line_spacing: int = 10
30
- font_path: str = None
31
- em_font_path: str = None
32
-
33
-
34
- def __post_init__(self):
35
- self.wrap_text()
36
-
37
-
38
- def wrap_text(self):
39
- """
40
- resize to fix max_height and max_width
41
- """
42
- font = ImageFont.truetype(self.font_path, self.font_size)
43
- words = re.findall(r'[a-zA-Z\d]+|[\u4e00-\u9fa5]|\n|.', self.text)
44
- lines = []
45
- current_line = ''
46
- for word in words:
47
- if word == '\n':
48
- lines.append(current_line.strip())
49
- current_line = ''
50
- continue
51
- test_line = current_line + word
52
- test_bbox = font.getbbox(test_line, anchor="la")
53
- test_width = test_bbox[2] - test_bbox[0] # 计算宽度
54
- if test_width <= self.max_width and '\n' not in test_line:
55
- current_line = test_line
56
- else:
57
- lines.append(current_line.strip())
58
- current_line = word
59
- if current_line:
60
- lines.append(current_line.strip())
61
- self.lines = lines
62
- self.height = len(lines) * self.font_size + (len(lines) - 1) * self.line_spacing
63
- key_lines = [line for line in lines if line.startswith("【】")]
64
- if len(key_lines) > 0:
65
- key_line = max(key_lines, key=len)
66
- else:
67
- key_line = False
68
- if self.height > self.max_height or (key_line and not re.search("^"+re.escape(key_line)+"$", self.text, re.M)):
69
- self.font_size -= 2
70
- self.wrap_text()
71
-
72
-
73
- def write_text_on_image(img, text_info):
74
- # 创建一个可以在给定图像上绘图的对象
75
- draw = ImageDraw.Draw(img)
76
- # 加载字体,如果没有提供字体路径,则使用系统默认字体
77
- font = ImageFont.load_default().font_variant(size=text_info.font_size)
78
- em_font = ImageFont.load_default().font_variant(size=text_info.font_size)
79
- if text_info.font_path:
80
- font = ImageFont.truetype(text_info.font_path, text_info.font_size)
81
- if text_info.em_font_path:
82
- em_font = ImageFont.truetype(text_info.em_font_path, text_info.font_size)
83
- # 在图片上写入文字
84
- # draw.text(text_info.position, text_info.text, fill=text_info.color, font=font)
85
- y_text = text_info.position[1]
86
- wrapped_lines = text_info.lines
87
- text_bbox_chinese = font.getbbox("中文行高")
88
- for line in wrapped_lines:
89
- text_bbox = font.getbbox(line)
90
- line_width = text_bbox[2] - text_bbox[0] # 获取宽度
91
- line_height = max(text_bbox[3] - text_bbox[1], text_bbox_chinese[3] - text_bbox_chinese[1])
92
- # print(text_bbox[3] - text_bbox[1], text_bbox_chinese[3] - text_bbox_chinese[1], text_info.font_size)
93
- if re.search(r"^【.*】.*?"+re.escape(line), text_info.text, re.M) or re.match(r"^【.*】.*?$", line):
94
- draw.text((text_info.position[0], y_text), line.replace("【】", ""), fill=text_info.color, font=em_font)
95
- else:
96
- draw.text((text_info.position[0], y_text), line, fill=text_info.color, font=font)
97
- # 更新y坐标以添加行间距并为下一行做准备
98
- y_text += line_height + text_info.line_spacing
99
- return img, y_text
100
-
101
-
102
- def embed_image(background, foreground, vertical_offset=0, standard_fg_size=None):
103
- """
104
- 粘贴图片
105
-
106
- Args:
107
- background (_type_): 背景图片
108
- foreground (_type_): 前景图片
109
- vertical_offset (int, optional): 纵向偏移. Defaults to 1500.
110
- standard_fg_size (tuple, optional): 前景缩放大小. Defaults to (1600, 450).
111
-
112
- Returns:
113
- _type_: _description_
114
- """
115
- # 获取背景图片的尺寸
116
- if standard_fg_size is None:
117
- standard_fg_size = background.size
118
- standard_fg_width, standard_fg_height = standard_fg_size
119
- bg_width, bg_height = background.size
120
- fg_width, fg_height = foreground.size
121
- fg_resize_percent = max(fg_width / standard_fg_width, fg_height / standard_fg_height)
122
- # print((fg_width / standard_fg_width, fg_height / standard_fg_height))
123
- foreground = foreground.resize((int(fg_width // fg_resize_percent), int(fg_height // fg_resize_percent)))
124
- fg_width, fg_height = foreground.size
125
- offset_x = (bg_width - fg_width) // 2 # 横轴偏移使小图居中
126
- offset_y = vertical_offset # 纵轴固定偏移300px
127
-
128
- background.paste(foreground, (offset_x, offset_y), foreground)
129
- return background
130
-
131
- def make_exhibitor_poster(logo_link, slogan, content):
132
- background = Image.open("src/assets/展商海报白板.jpg")
133
- H_PADDING_RATION = 0.08
134
- V_PADDING = 25
135
- V_START = 950
136
- MAX_HEIGHT = 520
137
- res = requests.get(logo_link)
138
- foreground = Image.open(io.BytesIO(res.content))
139
- # foreground = Image.open(logo_path_list[FOREGROUND_ID])
140
- background = embed_image(background, foreground, vertical_offset=750, standard_fg_size=(1600//2, 450//2))
141
- slogan = text_info(text=slogan, font_size=110//2,
142
- position=[background.size[0] * H_PADDING_RATION, V_START],
143
- max_width=background.size[0]*(1-2*H_PADDING_RATION),
144
- max_height=110//2,
145
- font_path=font_path_fangzhenghei)
146
- slogan.position[0] = (background.size[0] - len(slogan.text) * slogan.font_size) // 2
147
- background, next_height = write_text_on_image(background, slogan)
148
-
149
- content = text_info(text=content, font_size=100,
150
- position=[background.size[0] * H_PADDING_RATION, next_height+V_PADDING],
151
- max_width=background.size[0]*(1-2*H_PADDING_RATION),
152
- max_height=MAX_HEIGHT,
153
- font_path=font_path_SourceHanSans, em_font_path=font_path_fangzhenghei)
154
- print(content.height)
155
- background, next_height = write_text_on_image(background, content)
156
- img_stream = io.BytesIO()
157
- background.save(img_stream, format="JPEG")
158
- img_stream.seek(0)
159
- background.save("output/展商海报.jpg")
160
- return img_stream
161
-
162
-
163
- def crop_image(image):
164
- # 获取图片的宽度和高度
165
- width, height = image.size
166
- # 初始化边界框
167
- left = width
168
- top = height
169
- right = 0
170
- bottom = 0
171
- # 遍历图片的每个像素,找到非透明的区域
172
- for x in range(width):
173
- for y in range(height):
174
- pixel = image.getpixel((x, y))
175
- if pixel[3] != 0: # 检查透明度通道
176
- if x < left:
177
- left = x
178
- if x > right:
179
- right = x
180
- if y < top:
181
- top = y
182
- if y > bottom:
183
- bottom = y
184
- cropped_image = image.crop((left, top, right + 1, bottom + 1))
185
- return cropped_image
186
-
187
-
188
- def rm_img_bg(image):
189
- image = rembg.remove(image)
190
- cropped_image = crop_image(image)
191
- return cropped_image
192
-
193
- def get_text_width(font_path, font_size, text):
194
- font = ImageFont.truetype(font_path, font_size)
195
- test_bbox = font.getbbox(text, anchor="la")
196
- test_width = test_bbox[2] - test_bbox[0]
197
- return test_width
198
- def compose_guest_poster(name, guest_photo, titles):
199
-
200
- background = Image.open("src/assets/Guest04.jpg").convert("RGBA")
201
- background = background.resize((1080*2, 2400*2))
202
-
203
- title_list = titles.split("\n")
204
- # guest = Image.open("src/assets/Guest.png").convert("RGBA")
205
- # higher_mask = Image.open("src/assets/Guest01.png").convert("RGBA")
206
- H_PADDING_RATION = 0.08
207
- V_PADDING = 5
208
- V_START = 950
209
- MAX_HEIGHT = 520
210
- # foreground = Image.open(logo_path_list[FOREGROUND_ID])
211
- bg_width, bg_height = background.size
212
- background = embed_image(background, guest_photo, vertical_offset=1300, standard_fg_size=(2000, 2600))
213
- lower_mask = Image.open("src/assets/Guest03.png").convert("RGBA")
214
- background = embed_image(background, lower_mask)
215
- higher_mask = Image.open("src/assets/Guest02.png").convert("RGBA")
216
- background = embed_image(background, higher_mask)
217
- seal = Image.open("src/assets/Guest01.png").convert("RGBA")
218
- background = embed_image(background, seal)
219
- # background = background.convert("RGB")
220
-
221
- font_size=210
222
- name, background, next_height = write_text(name, font_path_zihun59, background, H_PADDING_RATION, 3020, font_size)
223
-
224
- next_height += 50
225
- font_size = 100
226
- pinyin_list = pinyin(name.text, style=Style.NORMAL)
227
- name_pinyin = ' '.join([item[0] for item in pinyin_list]).title()
228
- name_pinyin, background, next_height = write_text(name_pinyin, font_path_notosans, background, H_PADDING_RATION,next_height+V_PADDING, font_size)
229
-
230
-
231
- next_height += 100
232
- font_size = 70
233
- for title in title_list:
234
- title, background, next_height = write_text(title, font_path_notosans, background, H_PADDING_RATION,next_height+V_PADDING, font_size)
235
-
236
- background = background.convert("RGB")
237
- img_stream = io.BytesIO()
238
- background.save(img_stream, format="JPEG")
239
- img_stream.seek(0)
240
- if os.path.exists("src/assets/output"):
241
- background.save("src/assets/output/嘉宾海报.jpg")
242
- return img_stream
243
-
244
- def make_guest_poster(name, photo_link, original_photo_link, titles):
245
- if photo_link != '':
246
- res = requests.get(photo_link)
247
- guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
248
- elif original_photo_link != '':
249
- res = requests.get(original_photo_link)
250
- original_guest_photo = Image.open(io.BytesIO(res.content))
251
- guest_photo = rm_img_bg(original_guest_photo)
252
- else:
253
- return None
254
- img_stream = compose_guest_poster(name, guest_photo, titles)
255
- return img_stream
256
-
257
- def write_text(text, font_path, image, H_PADDING_RATION, start_height, font_size):
258
- bg_width = image.size[0]
259
- text_width = get_text_width(font_path, font_size, text)
260
- text = text_info(text=text, font_size=font_size,
261
- position=[bg_width*(1-H_PADDING_RATION) - text_width, start_height],
262
- color=(255,255,255),
263
- max_width=bg_width*(1-2*H_PADDING_RATION),
264
- font_path=font_path)
265
- image, next_height = write_text_on_image(image, text)
266
- return text,image,next_height
267
- # return img_stream
268
-
269
-
270
-
271
- img_path_dict = {
272
- "old": "src/assets/InvitationTemplateCompressed.pdf",
273
- "common": "src/assets/旧空白邀请函图片.jpg",
274
- "观礼": "src/assets/带名字.jpg",
275
- "演讲": "src/assets/带名字和会.jpg",
276
- "致辞": "src/assets/带名字和会.jpg",
277
- }
278
- font_path = "src/assets/ZhouZiSongTi7000Zi-2.otf"
279
- font_path = "src/assets/FangZhengHeiTiJianTi-1.ttf"
280
-
281
- def writeInvitationPDF(person, debug=False):
282
- "旧空白邀请函图片.jpg"
283
- font_size=40
284
- resized_name_font_size = font_size*5/len(person.name+person.title)
285
- resized_conference_font_size = font_size*9/len(person.conference)
286
- img_path = img_path_dict[person.category]
287
- image = Image.open(img_path)
288
- draw = ImageDraw.Draw(image)
289
- text_color = (255, 255, 255) # 白色 RGB
290
-
291
- draw.text((182, 150+max(0, font_size-resized_name_font_size)), person.name+person.title, fill=text_color, font=ImageFont.truetype(font_path, min(font_size, resized_name_font_size)))
292
- draw.text((102, 532+max(0, font_size-resized_conference_font_size)), person.conference, fill=text_color, font=ImageFont.truetype(font_path, min(font_size, resized_conference_font_size)))
293
- imgStream = io.BytesIO()
294
- # image.save(imgStream)
295
- # doc.save(pdfStream)
296
- # doc.close()
297
- if debug:
298
- image.save("output/image_with_text.jpg")
299
-
300
- return imgStream
301
-
302
-
303
-
304
-
305
- if __name__ == "__main__":
306
- # image_path = 'src/assets/邀请函封面.jpg' # 替换为你的图片文件路径
307
- # text_to_write = "你好,世界!"
308
- # font_path = "src/assets/FangZhengHeiTiJianTi-1.ttf"
309
- # position = (120, 140)
310
- # write_text_on_image(image_path, text_to_write, position=position, font_path=font_path)
311
- from collections import namedtuple
312
- Person = namedtuple('Person', ['name', 'title', 'category', 'conference', 'respond_person'])
313
- person = Person(name="一二三四五六", title="先生", category="演讲", conference="8月21日中欧智慧论坛,做主题演讲", respond_person="Eazy")
314
- output_path = 'output/output.pdf'
315
- # writeInvitationPDF(person, debug=True)
316
-
317
-
318
- text_list = ["""Shanghai 协砺科技有限公司专注于青少年科技教育12年,通过STEAM25教育模式培养青少年的科学思维和创新能力。在全国设有数十家学习中心,服务上万名学员,提供科创、机器人和编程课程。公司还设有赛事研究中心,由专业工程师团队提供赛事研究和策略支持。学员在各类科技赛事中屡获佳绩。协砺科技致力于推动中国科技教育的创新与发展,为国家繁荣贡献力量。
319
-
320
- 【主要产品】科创、编程、赛事
321
- 【核心特点】为青少年提供优质的科创学习规划。
322
- 【核心亮点】高效提供更优质的赛事解决方案——快速赋能当地传统教育企业转型智能化教育赛道。""",
323
- """天天乐学创办于2015年6月,总部位于北京,创始团队均来自清北等知名高校,旗下产品天天乐学定制app,主打立体化教学和趣味性学习,可深度匹配教学需求,实现个性化教学。目前合作校近三万家,遍布全国31个省,300座城市,是国内知名的教育科技公司。
324
-
325
-
326
- 【主要产品】独立教师·机构·绘本馆英语APP定制
327
- 【核心特点】内容深度定制,帮助机构实现线上线下联动教学
328
- """,
329
- """【】星瀚之光是明德传承集团旗下文化传媒品牌
330
- 成立于2023年,立足深圳,布局全国九大中心城市,是国内首家定位于价值观驱动的P产业链开发的MCN机构,拥有知识P孵化事业群和商业P孵化事业群两大主营板块,旗下涵盖了创始人P孵化直播电商、私域消费电商、企业整合营销、艺人经纪等业务,致力于成为中国规模最大的P生态搭建领军企业。
331
-
332
- 【】坚守“自利利他、奋斗为本、价值为纲、成就客户”的企业价值观
333
- 公司成立以来发展迅猛,迅速孵化出各大赛道的多个头部P,霸榜各大短视频平台榜单,包括:有家庭教育赛道:白瑞(家庭教育赛道头部主播);身心灵赛道:尚致胜(新中式心理学首创者);女性成长赛道:黄欢;商业赛道:苏建诚(亚细亚集团联合创始人)、王怀南(宝宝树创始人)、杨晓燕(长江商学院助理院长)、何慕(联众智达董事长)等;国学赛道:沈德斌(���凡四训学者)、钱锦国老师等。""",
334
- """学趋坊是一家为学校、机构、智能自习室等提供在线化和智能化解决方案的数字化教育内容供应商。已经为多个知名的在线教育品牌、智能自习室品牌提供0DM和OEM服务,产品用户累计超过1000万。
335
-
336
- 【主要产品】智能校本
337
- 【核心特点】为本地化内容插上科技的翅膀。
338
- 【核心亮点】高质量高效本地化教研—快速赋能当地传统教育企业转型智能化教育赛道。"""
339
- ]
340
- logo_path_list = [
341
- "src/assets/logo/学区房logo.png",
342
- "src/assets/logo/星瀚之光logo.jpg",
343
- "src/assets/logo/软科起点logo.png",
344
- "src/assets/logo/天天乐学logo.png",
345
- ]
346
- slogan_list = [
347
- "探索科技,启迪未来。",
348
- "价值观驱动的知识P全域孵化领先平台",
349
- "定制英语APP学习系统"
350
- ]
351
- FOREGROUND_ID = 3 # 0, 1, 2, 3
352
- SLOGAN_ID = 1 # 0, 1, 2
353
- TEXT_ID = 2 # 0, 1, 2, 3
354
-
355
- LOGO_LINK = "https://weboffice-temporary.ks3-cn-beijing.wpscdn.cn/thumbnail/tJUjbgWdD6R5k5rECKka1yK_100.png?Expires=1720890000&KSSAccessKeyId=AKLTmoJhggaFT1CHuozGZqbC&Signature=IxmcUo8eX3VzsRltml9F8AQ5%2FDY%3D&response-cache-control=public%2Cmax-age%3D86400"
356
- # make_exhibitor_poster(LOGO_LINK, slogan_list[SLOGAN_ID], text_list[TEXT_ID])
357
- name, titles = "王千里", "全国政协文员\n好好先生"
358
- 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"
359
- ORIGINAL_PHOTO_LINK = "https://weboffice-temporary.ks3-cn-beijing.wpscdn.cn/thumbnail/tpBWRvjDQNAtpVk4Kgx4EhW_100.png?Expires=1720965600&KSSAccessKeyId=AKLTmoJhggaFT1CHuozGZqbC&Signature=B3K%2BNoLRIYPejeceVg3BJiC%2FMzU%3D&response-cache-control=public%2Cmax-age%3D86400"
360
-
361
-
362
- # res = requests.get(ORIGINAL_PHOTO_LINK)
363
- # # guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
364
- # original_guest_photo = Image.open(io.BytesIO(res.content)).convert("RGBA")
365
- # guest_photo = rm_img_bg(original_guest_photo)
366
-
367
- # compose_guest_poster(name, guest_photo, titles)
368
-
369
- make_guest_poster(name, None, ORIGINAL_PHOTO_LINK, titles)
370
- # for file in os.listdir("src/assets/GuestPoster/original_guest"):
371
- # image = Image.open(os.path.join("src/assets/GuestPoster/original_guest", file))
372
- # image = rm_img_bg(image)
373
- # image.save(os.path.join("src/assets/output", file.replace("jpg", "png")))
374
-
 
1
+ from fastapi import FastAPI, Path, Query, Response, BackgroundTasks
2
+ from fastapi.responses import FileResponse, HTMLResponse
3
+ import gradio as gr
4
+ from src.pyscripts import file_process, oss, img_process
5
+ from datetime import datetime
6
+ from collections import namedtuple
7
+ from pydantic import BaseModel
8
+
9
+ Person = namedtuple('Person', ['name', 'title', 'category', 'conference', 'respond_person'])
10
+
11
+ CUSTOM_PATH = "/gradio"
12
+
13
+ app = FastAPI()
14
+
15
+ pdf_path = "src/assets/InvitationTemplate.pdf"
16
+ output_path = 'output/output.pdf'
17
+ person = Person(name="雷军", title="先生", category="观礼", conference="世界教育者大会", respond_person="Eazy")
18
+
19
+ # data = {
20
+ # 'name': '雷军',
21
+ # 'title': '先生',
22
+ # "conference": "世界教育者大会"
23
+ # }
24
+
25
+
26
+ @app.get("/")
27
+ def read_main():
28
+ return {"message": "This is your main app"}
29
+
30
+ # @app.post("/getInvitationPDF/{name}/{title}/{conference}")
31
+ @app.get("/getInvitationPDF")
32
+ def getInvitationPDF(name, title, conference, category, respond_person):
33
+ person = Person(name=name, title=title, category=category, conference=conference, respond_person=respond_person)
34
+ formatted_date = datetime.now().strftime('%Y-%m-%d')
35
+ file_name = f"致{name+title}的WWEC2024邀请函.pdf"
36
+ file_dir = f"invitation/{category}"
37
+ object_name = file_dir + "/" + file_name
38
+ pdfStream = file_process.writeInvitationPDF(person)
39
+ # with open(f"output/{file_name}", 'wb') as f:
40
+ # f.write(pdfStream.getvalue())
41
+ oss.upload_file_stream_to_s3(pdfStream, "host", object_name)
42
+ file_url = f"https://isidoresong.us.kg/{object_name}"
43
+ return {"url": file_url}
44
+
45
+ class GuestPosterInfo(BaseModel):
46
+ exhibitor: str
47
+ logo_link: str
48
+ slogan: str
49
+ content: str
50
+
51
+ @app.post("/getExhibitorPoster")
52
+ def getExhibitorPoster(item: GuestPosterInfo):
53
+ logo_link = item.logo_link
54
+ exhibitor = item.exhibitor
55
+ slogan = item.slogan
56
+ content = item.content
57
+ img_stream = img_process.make_exhibitor_poster(logo_link, slogan, content)
58
+ file_dir = "ExhibitorPoster"
59
+ object_name = file_dir + "/" + exhibitor + ".jpg"
60
+ # with open(f"output/{exhibitor + '.jpg'}", 'wb') as f:
61
+ # f.write(img_stream.getvalue())
62
+ oss.upload_file_stream_to_s3(img_stream, "host", object_name)
63
+ file_url = f"https://isidoresong.us.kg/{object_name}"
64
+ return {"url": file_url}
65
+
66
+
67
+ class GuestPosterInfo(BaseModel):
68
+ name: str
69
+ photo_link: str
70
+ original_photo_link: str
71
+ titles: str
72
+
73
+ @app.post("/getGuestPoster")
74
+ def getGuestPoster(item: GuestPosterInfo):
75
+ photo_link = item.photo_link
76
+ original_photo_link = item.original_photo_link
77
+ name = item.name
78
+ titles = item.titles
79
+ img_stream = img_process.make_guest_poster(name, photo_link, original_photo_link, titles)
80
+ file_dir = "GuestPoster"
81
+ object_name = file_dir + "/" + name + ".jpg"
82
+ # f.write(img_stream.getvalue())
83
+ oss.upload_file_stream_to_s3(img_stream, "host", object_name)
84
+ file_url = f"https://isidoresong.us.kg/{object_name}"
85
+ return {"url": file_url}
86
+
87
+ interface = gr.Interface(lambda x: "Hello, " + x + "!", "textbox", "textbox")
88
+ app = gr.mount_gradio_app(app, interface, path=CUSTOM_PATH)
89
+
90
+ # if __name__ == "__main__":