Help remove the white square Python Pil
In general, the quote is sent but with a white square in the corner. I don't understand where it comes from please help me.
from handler.base_plugin import CommandPlugin
from utils import upload_photo
from utils import traverse, timestamp_to_date
from PIL import Image, ImageDraw, ImageFont, ImageOps
import aiohttp, io
class QuoteDoerPlugin(CommandPlugin):
__slots__ = ("q", "qf", "f", "fs", "fss")
def __init__(self, *commands, prefixes=None, strict=False):
"""Answers with image containing stylish quote."""
if not commands:
commands = ("цитата",)
super().__init__(*commands, prefixes=prefixes, strict=strict)
self.q = Image.open(self.get_path("q.png")).resize((40, 40), Image.LANCZOS)
self.qf = self.q.copy().transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.FLIP_TOP_BOTTOM)
self.f = ImageFont.truetype(self.get_path("font.ttf"), 24)
self.fs = ImageFont.truetype(self.get_path("font.ttf"), 16)
self.fss = ImageFont.truetype(self.get_path("font.ttf"), 15)
example = self.command_example()
self.description = [f" {example} [титул] - перешлите сообщение и укажите титул (по желанию) и "
"получите цитату!"]
def make_image(self, img, text, name, last_name, timestamp, otext):
rsize = (700, 400)
res = Image.open(self.get_path("/default.jpg")).convert('RGBA')
res.paste(img, (25, 100))
tex = Image.new("RGBA", (rsize), (0, 0, 0, 0))
draw = ImageDraw.Draw(tex)
if len(text) > 70:
font = self.fss
else:
font = self.f
sidth = int(draw.textsize(" ", font=font)[0])
width, height = 0, 0
new_text = ""
for line in text.splitlines():
for word in line.split():
word_width = len(word) * sidth
if width + word_width >= rsize[0] - 340:
width = 0
new_text += "\n"
width += sidth + word_width
new_text += word + " "
width = 0
new_text += "\n"
new_text = new_text[:-1]
width, height = draw.multiline_textsize(new_text, font=font)
draw.multiline_text((0, 0), new_text, font=font)
y = rsize[1] // 2 - height // 2
x = 300 + (rsize[0] - 370 - width) // 2
res.paste(tex, (x, y))
if y <= 10:
return "Не получилось... простите."
if height < 210:
height = 210
y = rsize[1] // 2 - height // 2
res.paste(self.q, (240, y))
res.paste(self.qf, (rsize[0] - 65, y + height - 40))
draw = ImageDraw.Draw(res)
draw.multiline_text((25, 310), f"© {name} {last_name}{' - ' + otext if otext else ''}"
f"\n@ {timestamp_to_date(timestamp)}", font=self.fs)
buff = io.BytesIO()
res.save(buff, format='png')
return buff.getvalue()
async def process_message(self, msg):
command, otext = self.parse_message(msg)
i, url, name, last_name, timestamp = None, None, None, None, None
for m in traverse(await msg.get_full_forwarded()):
if m.full_text:
if i == m.true_user_id:
text += "\n" + m.full_text
continue
elif i is not None:
break
i = m.true_user_id
timestamp = m.timestamp
u = await self.api.users.get(user_ids=i, fields="photo_max")
if not u:
continue
u = u[0]
url = u["photo_max"]
name = u["first_name"]
last_name = u["last_name"]
text = m.full_text
else:
if i is None:
return await msg.answer("Нечего цитировать!")
async with aiohttp.ClientSession() as sess:
async with sess.get(url) as response:
img = Image.open(io.BytesIO(await response.read())).convert('RGBA')
img = img.resize((200, 200))
ll_size = (1000, 100,)
mask = Image.new('L', ll_size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + ll_size, fill=255)
mask = ImageOps.fit(mask, img.size, method=Image.BITCUBIC, centering=(0.5, 0.5))
img.putalpha(mask)
result = await self.run_in_executor(self.make_image, img, text, name, last_name, timestamp, otext)
if isinstance(result, str):
return await msg.answer(result)
attachment = await upload_photo(self.api, result, msg.user_id)
return await msg.answer(attachment=str(attachment))
1 answers
I explain what is happening.
tex = Image.new("RGBA", (rsize), (0, 0, 0, 0))
-- the place where the image with the text was created (the one with a solid background)
res.paste(tex, (x, y))
-- inserting a picture into a shared picture. Everything between them is used only for drawing the transmitted text. And that cycle is necessary so that the text does not go beyond the image of the text.
I thought that it is possible to draw that text directly on the main image, along the way I found a way to do without the loop -- module textwrap
.
Code:
from PIL import Image, ImageDraw
import datetime as DT
import textwrap
def timestamp_to_date(timestamp):
dt = DT.datetime.fromtimestamp(timestamp)
return dt.strftime("%Y-%m-%d %H:%M:%S")
def make_image(img_avatar, text, name, last_name, timestamp, otext):
res = Image.open("default.jpg").convert('RGBA')
res.paste(img_avatar, (25, 100))
draw = ImageDraw.Draw(res)
text = textwrap.fill(text, width=35)
footer_text = f"© {name} {last_name}{' - ' + otext if otext else ''}\n@ {timestamp_to_date(timestamp)}"
draw.multiline_text((250, 200), text, fill="Black")
draw.multiline_text((25, 310), footer_text, fill="Black")
res.save('result.png')
if __name__ == '__main__':
img_avatar = Image.open("avatar.png").convert('RGBA')
text = """Fill a single paragraph of text, returning a new string.
Reformat the single paragraph in 'text' to fit in lines of no more
than 'width' columns, and return a new string containing the entire
wrapped paragraph. As with wrap(), tabs are expanded and other
whitespace characters converted to space. See TextWrapper class for
available keyword args to customize wrapping behaviour.
"""
name = 'Foo'
last_name = 'Bar'
timestamp = 1529682155
otext = 'Bla-bla-bla'
make_image(img_avatar, text, name, last_name, timestamp, otext)
Default.jpg
Avatar.png
Result:
PS. The author will need to calculate the size of the text in order to place the quote on the right edge. Because textwrap looks at the number of characters when splitting the text. You'll need to play around a bit. (width, height = draw.multiline_textsize(new_text, font=font)
will help)
If the author didn't like the textwrap option, you can remove it, just immediately draw on the original image.
Those quotes are inserted in
res.paste(self.q, (240, y))
res.paste(self.qf, (rsize[0] - 65, y + height - 40))