Skip to content

Commit b92e423

Browse files
authored
Add files via upload
1 parent 0d82e35 commit b92e423

File tree

4 files changed

+148
-0
lines changed

4 files changed

+148
-0
lines changed

assets/fonts/Full_Minecraft-R.ttf

24.7 KB
Binary file not shown.

assets/images/background.png

131 Bytes
Loading

assets/images/frame.png

154 Bytes
Loading

main.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import os
2+
from PIL import Image, ImageDraw, ImageFont, ImageOps
3+
4+
# Color code map, Source - https://www.digminecraft.com/lists/color_list_pc.php
5+
color_code_map = {
6+
"§4": "#AA0000",
7+
"§c": "#FF5555",
8+
"§6": "#FFAA00",
9+
"§e": "#FFFF55",
10+
"§2": "#00AA00",
11+
"§a": "#55FF55",
12+
"§b": "#55FFFF",
13+
"§3": "#00AAAA",
14+
"§1": "#0000AA",
15+
"§9": "#5555FF",
16+
"§d": "#FF55FF",
17+
"§5": "#AA00AA",
18+
"§f": "#FFFFFF",
19+
"§7": "#AAAAAA",
20+
"§8": "#555555",
21+
"§0": "#000000",
22+
"§l": "bold",
23+
"§o": "italic",
24+
"§r": "reset"}
25+
26+
font_path = 'assets/fonts/Full_Minecraft-R.ttf'
27+
offset = 60
28+
29+
30+
def draw_description(desc, text_size=100, line_spacing=10, char_spacing=5, draw_text_start_offset_w=0,
31+
draw_text_start_offset_h=0):
32+
"""
33+
A function that generates an image out of a list of text.
34+
35+
:param desc: The full description as a list containing each line as a string
36+
:type desc: list[str,]
37+
:param text_size: The font size of the text
38+
:type text_size: int
39+
:param line_spacing: The spacing of each written line
40+
:type line_spacing: int
41+
:param char_spacing: The spacing of each written char
42+
:type char_spacing: int
43+
:param draw_text_start_offset_w: The offset the text would start writing from the right
44+
:type draw_text_start_offset_w: int
45+
:param draw_text_start_offset_h: The offset the text would start writing from the top
46+
:type draw_text_start_offset_h: int
47+
:return: The description box
48+
:rtype: PIL.Image
49+
"""
50+
51+
# Creates an image that is bigger than the text itself so we can crop it later to the exact size of the text
52+
template_image = Image.new('RGBA',
53+
(ImageFont.truetype(font_path,
54+
text_size).getsize(max(desc, key=len))[
55+
0] * char_spacing // 5 + offset * 2,
56+
len(desc) * line_spacing * 10 + offset * 2), (0, 0, 0, 0))
57+
58+
# Initializes some variables
59+
template_image_draw = ImageDraw.Draw(template_image)
60+
current_color = color_code_map['§7']
61+
current_line_height = 30 + draw_text_start_offset_h
62+
font = ImageFont.truetype(font_path, size=text_size)
63+
bold = 0
64+
65+
# Loops through every line
66+
for line_counter, line in enumerate(desc):
67+
68+
# Checks if we've reached the end, skip the data line and break out of the loop
69+
if line_counter != len(desc) - 1:
70+
if '§7Obtained' in desc[line_counter + 1]:
71+
break
72+
73+
# Resets the current_line_width so it can start writing again from the start
74+
current_line_width = 30 + draw_text_start_offset_w
75+
76+
# Loops through every char in the line
77+
for char_counter, char in enumerate(line):
78+
if char == '§':
79+
80+
# If the char is not at the end
81+
if char_counter != len(line) - 1:
82+
83+
# Set a temp variable to have the current color if that current color is NOT
84+
# bold or reset then continue and set the actual variable to the color so it won't set
85+
# The color to be bold or reset which are not valid colors but if they come out it will
86+
# Make the text stroke width higher or if it's reset it will reset the font, the boldness and the
87+
# color. After that it skips so it wont write the color code chars
88+
tmp_current_color = color_code_map[f'§{line[char_counter + 1]}']
89+
if tmp_current_color not in ['bold', 'reset']:
90+
current_color = tmp_current_color
91+
else:
92+
if tmp_current_color == 'bold':
93+
bold = 1
94+
else:
95+
font = ImageFont.truetype(font_path, size=text_size)
96+
current_color = color_code_map['§7']
97+
bold = 0
98+
continue
99+
100+
# If the char before the current char is § then skip writing this char because it means it is part
101+
# of the color coding
102+
if line[char_counter - 1] == '§':
103+
continue
104+
105+
# Draws the char with the right color, font and stroke width and then ups the value of the variable
106+
# current_line_width With the current char width in pixels and with the
107+
# char_spacing var so the chars wont overlap.
108+
# Im drawing each char at a time so i can color each one.
109+
template_image_draw.text((current_line_width, current_line_height), char, fill=current_color, font=font,
110+
stroke_width=bold)
111+
current_line_width += font.getsize(char)[0] + char_spacing
112+
113+
# After each line it will up the value of the variable current_line_height with the height of the last drawn
114+
# Char in pixels plus the line spacing so the lines wont overlap.
115+
current_line_height += font.getsize(char)[1] + line_spacing
116+
117+
tmp_offset = offset // 2
118+
119+
# After all the text was drawn on the big image it will crop the image to the minimum size possible while
120+
# keeping the text and then making it a bit larger so we would be able to put the frame in
121+
bbox = template_image.getbbox()
122+
template_image = template_image.crop((bbox[0] - tmp_offset, bbox[1] - tmp_offset,
123+
bbox[2] + tmp_offset + tmp_offset // 2,
124+
bbox[3] + tmp_offset + tmp_offset // 2))
125+
126+
# Changes the original image's size and rotation to fit on all sides of the frame and background
127+
frame_image = Image.new('RGBA', template_image.size, (0, 0, 0, 0))
128+
left_frame = Image.open('assets/images/frame.png').resize((tmp_offset, frame_image.size[1] - tmp_offset))
129+
right_frame = ImageOps.mirror(
130+
Image.open('assets/images/frame.png').resize((tmp_offset, frame_image.size[1] - tmp_offset)))
131+
top_frame = Image.open('assets/images/frame.png').rotate(-90, Image.NEAREST, 1).resize(
132+
(frame_image.size[0] - tmp_offset, tmp_offset))
133+
bottom_frame = Image.open('assets/images/frame.png').rotate(90, Image.NEAREST, 1).resize(
134+
(frame_image.size[0] - tmp_offset, tmp_offset))
135+
background = Image.open('assets/images/background.png').resize(
136+
(frame_image.size[0] - offset, frame_image.size[1] - offset))
137+
138+
# Pastes every frame, background and the text to the original image
139+
frame_image.paste(background, (tmp_offset, tmp_offset), background)
140+
frame_image.paste(left_frame, (0, tmp_offset // 2), left_frame)
141+
frame_image.paste(right_frame, (frame_image.size[0] - tmp_offset, tmp_offset // 2), right_frame)
142+
frame_image.paste(top_frame, (tmp_offset // 2, 0), top_frame)
143+
frame_image.paste(bottom_frame, (tmp_offset // 2, frame_image.size[1] - tmp_offset), bottom_frame)
144+
frame_image.paste(template_image, (10 + draw_text_start_offset_w, 10 + draw_text_start_offset_h), template_image)
145+
return frame_image
146+
147+
148+
draw_description(['§6Heroic Flower of Truth §6✪§6✪§6✪', '§7Gear Score: §d726 §8(953)', '§7Damage: §c+180 §e(+20) §8(+248.4)', '§7Strength: §c+360 §e(+20) §9(+40) §8(+496.8)', '§7Bonus Attack Speed: §c+5% §9(+5%) §8(+6.5%)', '§7Intelligence: §a+100 §9(+100) §8(+138)', ' §8[§7❁§8]', '', '§7§lUltimate Wise III§9, §7Cleave V§9, §7Critical V', '§7Cubism V§9, §7Ender Slayer V§9, §7Execute V', '§9Experience III§9, §7First Strike IV§9, §7Giant Killer V', '§7Impaling III§9, §7Lethality V§9, §7Life Steal III', '§9Looting III§9, §7Luck V§9, §9Scavenger III', '§9Sharpness V§9, §9Telekinesis I§9, §7Thunderbolt V', '§7Vampirism V§9, §7Venomous V', '', '§7§cYou do not have a high enough', '§cEnchanting level to use some of', '§cthe enchantments on this item!', '', '§6Ability: Heat-Seeking Rose §e§lRIGHT CLICK', '§7Shoots a rose that ricochets', '§7between enemies, damaging up to', '§7§a3 §7of your foes! Damage', '§7multiplies as more enemies are', '§7hit.', '§8Mana Cost: §37', '§8Cooldown: §a1s', '§7§7The mana cost of this item is', '§7§a10.0% §7of your maximum mana.', '', '§aPerfect 70000 / 70000', '§6§lLEGENDARY DUNGEON SWORD', '', '§7Obtained: §c<local-time timestamp="1627124400000"></local-time>']).save('test.png')

0 commit comments

Comments
 (0)