1
1
from PIL import Image , ImageDraw , ImageFont
2
-
3
- def create_text_image (text : str , height : int , width : int , file_name : str , color : str = "#000000" ):
2
+ import os
3
+
4
+
5
+ def get_text_width (font , text ):
6
+ if hasattr (font , 'getlength' ):
7
+ # For newer versions of Pillow
8
+ return font .getlength (text )
9
+ elif hasattr (font , 'getsize' ):
10
+ # For older versions of Pillow
11
+ return font .getsize (text )[0 ]
12
+ elif hasattr (font , 'getbbox' ):
13
+ # Alternative method
14
+ bbox = font .getbbox (text )
15
+ return bbox [2 ] - bbox [0 ]
16
+ else :
17
+ raise AttributeError (
18
+ "Font object has no method to calculate text width." )
19
+
20
+
21
+ def create_text_image (text : str , height : int , width : int , file_name : str , color : str = "#000000" ):
4
22
"""
5
23
Creates an image with the specified text centered within the given dimensions and saves it as a PNG file.
6
24
Args:
@@ -27,14 +45,48 @@ def create_text_image(text: str, height: int, width: int, file_name: str, color:
27
45
best_wrapped_text = None
28
46
best_total_height = None
29
47
30
- # Load the font once to avoid repetitive loading
31
- font_path = "arial.ttf" # Ensure this font is available on your system
48
+ # Function to find a font that exists on the system
49
+ def find_font ():
50
+ possible_fonts = [
51
+ # Common fonts on Mac
52
+ "/Library/Fonts/Arial.ttf" ,
53
+ "/System/Library/Fonts/Supplemental/Arial.ttf" ,
54
+ "/Library/Fonts/Helvetica.ttf" ,
55
+ "/System/Library/Fonts/Supplemental/Helvetica.ttf" ,
56
+ "/Library/Fonts/Times New Roman.ttf" ,
57
+ # Common fonts on Windows
58
+ "C:\\ Windows\\ Fonts\\ Arial.ttf" ,
59
+ "C:\\ Windows\\ Fonts\\ times.ttf" ,
60
+ "C:\\ Windows\\ Fonts\\ verdana.ttf" ,
61
+ # Common fonts on Linux
62
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" ,
63
+ # PIL default font
64
+ "arial.ttf" ,
65
+ "DejaVuSans.ttf" , # Comes with matplotlib
66
+ ]
67
+ for font_path in possible_fonts :
68
+ if os .path .exists (font_path ):
69
+ return font_path
70
+ # If none of the above fonts exist, use PIL's default font
71
+ return None
72
+
73
+ font_path = find_font ()
74
+ if font_path is None :
75
+ # Use PIL's default font
76
+ font = ImageFont .load_default ()
77
+ else :
78
+ font = None # We'll set the font in the loop
32
79
33
80
while min_font_size <= max_font_size :
34
81
font_size = (min_font_size + max_font_size ) // 2
35
- font = ImageFont .truetype (font_path , size = font_size )
82
+ if font_path is None :
83
+ # Use default font
84
+ font = ImageFont .load_default ()
85
+ else :
86
+ font = ImageFont .truetype (font_path , size = font_size )
36
87
37
- fits , wrapped_text , total_height = does_text_fit (draw , text , font , width , height )
88
+ fits , wrapped_text , total_height = does_text_fit (
89
+ draw , text , font , width , height )
38
90
if fits :
39
91
# This font size fits, try a bigger one
40
92
best_font_size = font_size
@@ -47,10 +99,15 @@ def create_text_image(text: str, height: int, width: int, file_name: str, color:
47
99
48
100
if best_wrapped_text is None :
49
101
# Text does not fit even at the minimum font size
50
- raise ValueError ("Text cannot fit into the image at the minimum font size." )
102
+ raise ValueError (
103
+ "Text cannot fit into the image at the minimum font size." )
51
104
52
105
# Use the best font size
53
- font = ImageFont .truetype (font_path , size = best_font_size )
106
+ if font_path is None :
107
+ # Use default font
108
+ font = ImageFont .load_default ()
109
+ else :
110
+ font = ImageFont .truetype (font_path , size = best_font_size )
54
111
ascent , descent = font .getmetrics ()
55
112
line_spacing = int (best_font_size * 0.2 ) # 20% of font size
56
113
@@ -62,7 +119,8 @@ def create_text_image(text: str, height: int, width: int, file_name: str, color:
62
119
63
120
# Draw the text on the image
64
121
for line in best_wrapped_text :
65
- text_width = font .getlength (line )
122
+ # Get text width
123
+ text_width = get_text_width (font , line )
66
124
x = (width - text_width ) / 2 # Center horizontally
67
125
draw .text ((x , y_offset ), line , fill = color , font = font )
68
126
y_offset += ascent + descent + line_spacing # Move down for the next line
@@ -71,6 +129,7 @@ def create_text_image(text: str, height: int, width: int, file_name: str, color:
71
129
image .save (file_name , "PNG" )
72
130
return file_name
73
131
132
+
74
133
def does_text_fit (draw , text , font , width , height ):
75
134
ascent , descent = font .getmetrics ()
76
135
line_spacing = int (font .size * 0.2 ) # 20% of font size
@@ -83,7 +142,8 @@ def does_text_fit(draw, text, font, width, height):
83
142
current_line = ""
84
143
for word in words :
85
144
test_line = f"{ current_line } { word } " .strip ()
86
- text_width = font .getlength (test_line )
145
+ # Get text width
146
+ text_width = get_text_width (font , test_line )
87
147
if text_width <= width :
88
148
current_line = test_line
89
149
else :
@@ -97,31 +157,40 @@ def does_text_fit(draw, text, font, width, height):
97
157
98
158
# Calculate total height for the wrapped text
99
159
num_lines = len (wrapped_text )
100
- total_height = (ascent + descent ) * num_lines + line_spacing * (num_lines - 1 )
160
+ total_height = (ascent + descent ) * num_lines + \
161
+ line_spacing * (num_lines - 1 )
101
162
102
163
# Check if any line exceeds width
103
- any_line_too_wide = any (font .getlength (line ) > width for line in wrapped_text )
164
+ any_line_too_wide = any (get_text_width (font , line )
165
+ > width for line in wrapped_text )
104
166
105
167
# Return whether text fits, the wrapped_text, and total_height
106
168
fits = (total_height <= height ) and not any_line_too_wide
107
169
return fits , wrapped_text , total_height
108
170
109
171
110
172
if __name__ == "__main__" :
173
+ # Create output directory if it doesn't exist
174
+ output_dir = "../img"
175
+ if not os .path .exists (output_dir ):
176
+ os .makedirs (output_dir )
177
+
111
178
# Example usage:
112
179
create_text_image (
113
180
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." ,
114
181
800 ,
115
182
1200 ,
116
- "img/ test_1.png" ,
183
+ os . path . join ( output_dir , " test_1.png") ,
117
184
"#000000"
118
185
)
119
186
create_text_image (
120
187
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." ,
121
188
800 ,
122
189
400 ,
123
- "img/ test_2.png" ,
190
+ os . path . join ( output_dir , " test_2.png") ,
124
191
"#FF5733"
125
192
)
126
- create_text_image ("Lorem ipsum dolor sit amet" , 800 , 1200 , "img/test_3.png" )
127
- create_text_image ("Lorem ipsum dolor sit amet" , 800 , 400 , "img/test_4.png" , "#FFFFFF" )
193
+ create_text_image ("Lorem ipsum dolor sit amet" , 800 , 1200 ,
194
+ os .path .join (output_dir , "test_3.png" ))
195
+ create_text_image ("Lorem ipsum dolor sit amet" , 800 , 400 ,
196
+ os .path .join (output_dir , "test_4.png" ), "#FFFFFF" )
0 commit comments