main.py (view raw)
1from PIL import Image, ImageDraw, ImageFont
2import textwrap
3
4class Meme:
5
6 basewidth = 1200 #Width to make the meme
7 fontBase = 100
8 letSpacing = 9 #Space between letters
9 fill = (255, 255, 255) #TextColor
10 stroke_fill = (0,0,0) #outlineColor
11 lineSpacing = 10 #Space between lines
12 stroke_width=9 #How thick the outline of the text is
13 fontfile = './impact.ttf'
14
15 def __init__(self, caption, image):
16 self.img = self.createImage(image)
17 self.d = ImageDraw.Draw(self.img)
18
19 self.splitCaption = textwrap.wrap(caption.upper(), width=20) # The text can be wider than the img. If thats the case split the text into multiple lines
20 self.splitCaption.reverse() # Draw the lines of text from the bottom up
21
22 fontSize = self.fontBase+10 if len(self.splitCaption) <= 1 else self.fontBase #If there is only one line, make the text a bit larger
23 self.font = ImageFont.truetype(font=self.fontfile, size=fontSize)
24 # self.shadowFont = ImageFont.truetype(font='./impact.ttf', size=fontSize+10)
25
26 def draw(self):
27 '''
28 Draws text onto this objects img object
29 :return: A pillow image object with text drawn onto the image
30 '''
31 (iw, ih) = self.img.size
32 (_, th) = self.d.textsize(self.splitCaption[0], font=self.font) #Height of the text
33 y = (ih - (ih / 10)) - (th / 2) #The starting y position to draw the last line of text. Text in drawn from the bottom line up
34
35 for cap in self.splitCaption: #For each line of text
36 (tw, _) = self.d.textsize(cap, font=self.font) # Getting the position of the text
37 x = ((iw - tw) - (len(cap) * self.letSpacing))/2 # Center the text and account for the spacing between letters
38
39 self.drawLine(x=x, y=y, caption=cap)
40 y = y - th - self.lineSpacing # Next block of text is higher up
41
42 wpercent = ((self.basewidth/2) / float(self.img.size[0]))
43 hsize = int((float(self.img.size[1]) * float(wpercent)))
44 return self.img.resize((int(self.basewidth/2), hsize))
45
46 def createImage(self, image):
47 '''
48 Resizes the image to a resonable standard size
49 :param image: Path to an image file
50 :return: A pil image object
51 '''
52 img = Image.open(image)
53 wpercent = (self.basewidth / float(img.size[0]))
54 hsize = int((float(img.size[1]) * float(wpercent)))
55 return img.resize((self.basewidth, hsize))
56
57 def drawLine(self, x, y, caption):
58 '''
59 The text gets split into multiple lines if it is wider than the image. This function draws a single line
60 :param x: The starting x coordinate of the text
61 :param y: The starting y coordinate of the text
62 :param caption: The text to write on the image
63 :return: None
64 '''
65 for idx in range(0, len(caption)): #For each letter in the line of text
66 char = caption[idx]
67 w, h = self.font.getsize(char) #width and height of the letter
68 self.d.text(
69 (x, y),
70 char,
71 fill=self.fill,
72 stroke_width=self.stroke_width,
73 font=self.font,
74 stroke_fill=self.stroke_fill
75 ) # Drawing the text character by character. This way spacing can be added between letters
76 x += w + self.letSpacing #The next character must be drawn at an x position more to the right
77
78
79
80caption = '''Quando io e poi lei e poi io e poi lei e poi
81
82Bottom text'''
83image = './image.jpg'
84outputImage = './output_image.jpg'
85
86meme = Meme(caption, image)
87img = meme.draw()
88if img.mode in ("RGBA", "P"): #Without this the code can break sometimes
89 img = img.convert("RGB")
90img.save(outputImage, optimize=True, quality=80) #Save with some image optimization