Effects.py (view raw)
1from PIL import Image, ImageDraw, ImageFont, ImageEnhance
2import textwrap, os, random, time
3
4random.seed(time.time())
5
6BASE_WIDTH = 1200
7IMPACT_FONT_FILE = os.path.join("fonts", "impact.ttf")
8ARIAL_FONT_FILE = os.path.join("fonts", "opensans.ttf")
9
10def _darken_image(image: Image, amount=0.5):
11 return ImageEnhance.Brightness(image).enhance(amount)
12
13def _draw_line(d: ImageDraw, x: int, y: int, line: str, font: ImageFont, letter_spacing: int = 9, fill = (255, 255, 255), stroke_width: int = 9, stroke_fill = (0, 0, 0)):
14
15 for i in range(len(line)):
16 d.text((x, y), line[i], fill=fill, stroke_width=stroke_width, font=font, stroke_fill=stroke_fill)
17 x += font.getlength(line[i]) + letter_spacing
18
19def tt_bt_effect(text: str, img: Image):
20 LETTER_SPACING = 9
21 LINE_SPACING = 10
22 FILL = (255, 255, 255)
23 STROKE_WIDTH = 9
24 STROKE_FILL = (0, 0, 0)
25 FONT_BASE = 100
26 MARGIN = 10
27
28 def _draw_tt_bt(text, img, bottom=False):
29 split_caption = textwrap.wrap(text.upper(), width=20)
30 if split_caption == []:
31 return
32 font_size = FONT_BASE + 10 if len(split_caption) <= 1 else FONT_BASE
33 font = ImageFont.truetype(font=IMPACT_FONT_FILE, size=font_size)
34 img_width, img_height = img.size
35
36 d = ImageDraw.Draw(img)
37 txt_height = d.textbbox((0, 0), split_caption[0], font=font)[3]
38
39 if bottom:
40 factor = -1
41 split_caption.reverse()
42 y = (img_height - (img_height / MARGIN)) - (txt_height / 2)
43 else:
44 factor = 1
45 y = (img_height / MARGIN) - (txt_height / 1.5)
46
47 for line in split_caption:
48 txt_width = d.textbbox((0, 0), line, font=font)[2]
49
50 x = (img_width - txt_width - (len(line) * LETTER_SPACING))/2
51
52 _draw_line(d, x, y, line, font, LETTER_SPACING, FILL, STROKE_WIDTH, STROKE_FILL)
53
54 y += (txt_height + LINE_SPACING) * factor
55
56 lines = [x for x in text.split("\n") if x]
57
58 tt = lines[0] if len(lines) > 0 else None
59 bt = lines[1] if len(lines) > 1 else None
60
61 img = img.resize((BASE_WIDTH, int(img.size[1] * float(BASE_WIDTH / img.size[0]))))
62
63 if tt is None and bt is None:
64 return img
65
66 if (tt is not None):
67 _draw_tt_bt(tt, img)
68 if (bt is not None):
69 _draw_tt_bt(bt, img, bottom=True)
70
71 img = img.resize((int(BASE_WIDTH/2), int(float(img.size[1]) * (BASE_WIDTH/2) / img.size[0])))
72
73 if img.mode in ("RGBA", "P"):
74 img = img.convert("RGB")
75
76 return img
77
78def splash_effect(text: str, img: Image):
79 LETTER_SPACING = 1
80 LINE_SPACING = 3
81 FILL = (255, 255, 255)
82 STROKE_WIDTH = 1
83 STROKE_FILL = (0, 0, 0)
84 FONT_FIRST = 50
85 FONT_BASE = 75
86 MARGIN = 10
87 LINE_WIDTH = 20
88
89 lines = [x for x in text.split("\n") if x]
90 first_line = lines.pop(0)
91 text = "\n".join(lines)
92
93 img = img.resize((BASE_WIDTH, int(img.size[1] * float(BASE_WIDTH / img.size[0]))))
94
95 img = _darken_image(img)
96
97 text = textwrap.wrap(text.upper(), width=LINE_WIDTH)
98 if text == []:
99 return
100 text.insert(0, first_line)
101
102 font_first = ImageFont.truetype(font=ARIAL_FONT_FILE, size=FONT_FIRST)
103 font_base = ImageFont.truetype(font=ARIAL_FONT_FILE, size=FONT_BASE)
104
105 img_width, img_height = img.size
106 d = ImageDraw.Draw(img)
107
108 _, _, first_txt_width, first_txt_height = d.textbbox((0, 0), text[0], font=font_first)
109 _, _, max_txt_width, txt_height = d.textbbox((0, 0), text[1], font=font_base)
110
111 total_height = (txt_height + LINE_SPACING) * (len(text) - 1) + LINE_SPACING + first_txt_height
112 y = (img_height - total_height) / 2
113
114 for i in range(1, len(text)):
115 temp = int(font_base.getlength(text[i]))
116 if temp > max_txt_width:
117 max_txt_width = temp
118
119 max_txt_width = max_txt_width if max_txt_width > first_txt_width else first_txt_width
120 x_start = (img_width - max_txt_width) / 2
121
122 for i in range(len(text)):
123 '''
124 if align == "center":
125 txt_width = d.textbbox((0, 0), line, font=font)[2]
126 x = (img_width - txt_width - (len(line) * LETTER_SPACING))/2
127 '''
128 font = font_base if i > 0 else font_first
129 _draw_line(d=d, x=x_start, y=y, line=text[i], font=font, letter_spacing=LETTER_SPACING, fill=FILL, stroke_width=STROKE_WIDTH, stroke_fill=STROKE_FILL)
130
131 y += (txt_height if i > 0 else first_txt_height) + LINE_SPACING
132
133 img = img.resize((int(BASE_WIDTH/2), int(float(img.size[1]) * (BASE_WIDTH/2) / img.size[0])))
134
135 if img.mode in ("RGBA", "P"):
136 img = img.convert("RGB")
137
138 return img
139
140def wot_effect(text: str, img: Image):
141 LETTER_SPACING = 1
142 LINE_SPACING = 3
143 FILL = (255, 255, 255)
144 STROKE_WIDTH = 1
145 STROKE_FILL = (0, 0, 0)
146 FONT_BASE = 50
147 MARGIN = 10
148 LINE_WIDTH = 44
149
150 img = img.resize((BASE_WIDTH, int(img.size[1] * float(BASE_WIDTH / img.size[0]))))
151 img = _darken_image(img)
152
153 text = textwrap.wrap(text, width=LINE_WIDTH)
154 if text == []:
155 return
156
157 font = ImageFont.truetype(font=ARIAL_FONT_FILE, size=FONT_BASE)
158
159 img_width, img_height = img.size
160 d = ImageDraw.Draw(img)
161
162 _, _, txt_width, txt_height = d.textbbox((0, 0), text[0], font=font)
163
164 total_height = (txt_height + LINE_SPACING) * len(text)
165 y = (img_height - total_height) / 2
166
167 for i in range(len(text)):
168 txt_width = d.textbbox((0, 0), text[i], font=font)[2]
169 x = (img_width - txt_width - (len(text[i]) * LETTER_SPACING))/2
170
171 _draw_line(d=d, x=x, y=y, line=text[i], font=font, letter_spacing=LETTER_SPACING, fill=FILL, stroke_width=STROKE_WIDTH, stroke_fill=STROKE_FILL)
172
173 y += txt_height + LINE_SPACING
174
175 img = img.resize((int(BASE_WIDTH/2), int(float(img.size[1]) * (BASE_WIDTH/2) / img.size[0])))
176
177 if img.mode in ("RGBA", "P"):
178 img = img.convert("RGB")
179
180 return img
181
182def text_effect(text: str, img: Image):
183 LETTER_SPACING = 1
184 LINE_SPACING = 3
185 STROKE_WIDTH = 1
186 STROKE_FILL = (0, 0, 0)
187 FONT_BASE = 75
188 MARGIN = 10
189 LINE_WIDTH = 20
190
191 img = img.resize((BASE_WIDTH, int(img.size[1] * float(BASE_WIDTH / img.size[0]))))
192
193 text = textwrap.wrap(text, width=LINE_WIDTH)
194 if text == []:
195 return
196
197 font = ImageFont.truetype(font=ARIAL_FONT_FILE, size=FONT_BASE)
198
199 img_width, img_height = img.size
200 d = ImageDraw.Draw(img)
201
202 _, _, max_txt_width, txt_height = d.textbbox((0, 0), text[0], font=font)
203
204 for line in text:
205 temp = int(font.getlength(line))
206 if temp > max_txt_width:
207 max_txt_width = temp
208
209
210 total_height = (txt_height + LINE_SPACING) * len(text)
211
212 y_inf = 0
213 y_sup = img_height - total_height
214 x_inf = 0
215 x_sup = img_width - max_txt_width - 5
216
217 fill = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
218 x = random.randint(x_inf, x_sup)
219 y = random.randint(y_inf, y_sup)
220 for i in range(len(text)):
221 _draw_line(d=d, x=x, y=y, line=text[i], font=font, letter_spacing=LETTER_SPACING, fill=fill, stroke_width=STROKE_WIDTH, stroke_fill=STROKE_FILL)
222
223 y += txt_height + LINE_SPACING
224
225 img = img.resize((int(BASE_WIDTH/2), int(float(img.size[1]) * (BASE_WIDTH/2) / img.size[0])))
226
227 if img.mode in ("RGBA", "P"):
228 img = img.convert("RGB")
229
230 return img
231
232def test_multiple(text, effect, modifier=""):
233 imgs = os.listdir("test")
234 for i in range(len(imgs)):
235 image = effect(text, Image.open(os.path.join("test", imgs[i])))
236 image.save(os.path.join("test_output", f'output{modifier}{i}.jpg'), optimize=True, quality=80)
237
238 print("Image test successful")
239
240def test(text, effect, modifier=""):
241 image = effect(text, Image.open("image.jpg"))
242 image.save('output.jpg', optimize=True, quality=80)
243
244 print("Image test successful")
245
246def main():
247 test("Autore più lungo del solito per vedere se va a capo\ntesto un po' più lungo ma non troppo eh\nquesto verrà scartato\npure questo lorem ipsum prova ", text_effect)
248 #test_multiple("top text\nbottom text", splash_effect)
249 #test_multiple("top text top text top text top text top text\nbottom text bottom text bottom text bottom text bottom text", splash_effect, "_long")
250
251if __name__ == "__main__":
252 main()