简体   繁体   中英

(PIL / Pillow) Text in wrong position despite having correct coordinates

So the plan here is to have a bot send a message in a specific channel when a user gets a role. That message contains an image made in PIL — It has username and avatar fetched from Discord — and a text welcoming the user with some basic channel instructions.

This is the code I have thus far.

    async def on_member_join(self, member):
        guild = self.client.get_guild(808176065519812638)
        general_channel = guild.get_channel(808176065519812641)

        url = requests.get(member.avatar_url)
        avatar = Image.open(BytesIO(url.content)).convert("RGB")
        avatar = avatar.resize((240, 240))
        bigsize = (avatar.size[0] * 3, avatar.size[1] * 3)
        mask = Image.new("L", bigsize, 0)
        draw = ImageDraw.Draw(mask)
        draw.ellipse((0, 0) + bigsize, fill=240)
        mask = mask.resize(avatar.size, Image.ANTIALIAS)
        avatar.putalpha(mask)

        output = ImageOps.fit(avatar, mask.size, centering=(1500, 396))
        output.putalpha(mask)
        output.save("avatar.png")

        img = Image.open("welcomebeta.png")
        draw = ImageDraw.Draw(img)
        text = f"{member.name}"
        textcaps = text.upper()
        font = ImageFont.truetype("roboto-boldcondensed.ttf", 118)
        draw.text((485, 152), textcaps, (255, 255, 255), font=font)
        img.paste(avatar, (152, 82), avatar)
        img.save("welcome_user.png")

        file = discord.File("welcome_user.png")
        channel = self.client.get_channel(808176065519812641)
        await channel.send(file=file)
        guild = self.client.get_guild(808176065519812638)
        channel = guild.get_channel(808176065519812641)

        await channel.send(
            f"<:lfcwave:808558627371614258> Welcome to This Server {member.mention} <:lfcwave:808558627371614258> \nIf you have any general inquiries about the server, players, or discord settings, please do not hesitate to ask! Go to <#808559611207483392> to unlock more channels. Read <#808559377648451595> if u wanna talk transfers. If it's matchday <#808451284914929694> will be open for you to get the match roles. If it's another team playing use <#808559425379237898> and feel free to join in our score prediction challenge here <#808559459664265226>"
        )
        await self.client.send(channel, "welcome_user.png")

Here is what I aim to have

Now the problem here is that when I run the bot the written text for the username is not in the correct place. I have checked multiple times using the ruler on Photoshop and its coordinates are (485.3, 155.3).

After that, I compared it with the coordinates on python. From what I understand, it does not accept integers, but 0.3 px difference shouldn't be as huge as it is in the returned image. They do approximately match in numbers, but for some reason, it still is in the wrong place.

I opened the returned image from the bot, and the coordinates for the misplaced text is (491.5,177.5)

This is the current result of the code.

I used an alt account to see how the text would look with longer usernames. I also am trying to find a way if it is possible for the text to automatically be resized when it is a long username in order to avoid the username taking the whole image.

Any help would be highly appreciated.

Thank you for reading.

I've actually made a similar system a while ago, for one my discord bots.

Here's some code that does what you're looking for:

def GetTextDimensions(text, points, font):
    class SIZE(ctypes.Structure):
        _fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]

    hdc = ctypes.windll.user32.GetDC(0)
    hfont = ctypes.windll.gdi32.CreateFontA(-points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
    hfont_old = ctypes.windll.gdi32.SelectObject(hdc, hfont)
    size = SIZE(0, 0)
    ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text.encode('cp1252'), len(text), ctypes.byref(size))
    ctypes.windll.gdi32.SelectObject(hdc, hfont_old)
    ctypes.windll.gdi32.DeleteObject(hfont)
    return (size.cx, size.cy)


def resizetofit(text,sz,fontname,max_horizontal):
    while True:
        width, height = GetTextDimensions(text, sz, fontname) #Get size of text
        if width < max_horizontal: #Check if text is small enough
            break
        else: #And if not, make it one point smaller
            sz -= 1
    return width,height,sz


width, height, size = resizetofit("TEXT", DEFAULT_SIZE, "FONT NAME", MAX_HORIZONTAL) #Find the correct size for text
font = ImageFont.truetype("PATH TO FONT.ttf", size=size) #Get the font for PIL
x_pos = DEFAULT_X_POSITION
x = x_pos - (width / 2) #Center the position, using the width of the resized text
y = Y_POSITION
draw.text((x, y), name, (255,255,255), font=font) #Draw the text

Little Example using discord.py:

def GetTextDimensions(text, points, font):
    class SIZE(ctypes.Structure):
        _fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]

    hdc = ctypes.windll.user32.GetDC(0)
    hfont = ctypes.windll.gdi32.CreateFontA(-points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
    hfont_old = ctypes.windll.gdi32.SelectObject(hdc, hfont)
    size = SIZE(0, 0)
    ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text.encode('cp1252'), len(text), ctypes.byref(size))
    ctypes.windll.gdi32.SelectObject(hdc, hfont_old)
    ctypes.windll.gdi32.DeleteObject(hfont)
    return (size.cx, size.cy)


def resizetofit(text, sz, fontname, max_horizontal):
    while True:
        width, height = GetTextDimensions(text, sz, fontname)  # Get size of text
        if width < max_horizontal:
            break
        else:
            sz -= 1
    return width, height, sz

@client.event
async def on_message(message):
    if message.content.startswith("Shrekify"):
        name = message.author.name
        image = Image.open("shrek1.png")
        draw = ImageDraw.Draw(image)
        color = 'rgb(0, 0, 0)'
        width, height, size = resizetofit(name, 100, "Roboto-Bold", 948) #In this example all the values have been filled out, for example, in this case, the max horizontal value is the horizontal size of the image
        font = ImageFont.truetype("Roboto-Bold.ttf", size=size)
        x_pos = 474 #And here i want the text centered, so i use the middle of the image
        x = x_pos - (width / 2)
        y = 40
        draw.text((x, y), name, fill=color, font=font)
        image.save("shrek1text.png")
        shrekimg = discord.File("shrek1text.png")
        await message.channel.send(file=shrekimg)

This example uses rewrite

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM