(PIL / Pillow)尽管坐标正确,但文本位置错误

Posted

技术标签:

【中文标题】(PIL / Pillow)尽管坐标正确,但文本位置错误【英文标题】:(PIL / Pillow) Text in wrong position despite having correct coordinates 【发布时间】:2021-07-07 17:04:31 【问题描述】:

所以这里的计划是让机器人在用户获得角色时在特定频道中发送消息。 该消息包含一个用 PIL 制作的图像——它具有从 Discord 获取的用户名和头像——以及一条用一些基本频道说明欢迎用户的文本。

这是我到目前为止的代码。

    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

现在的问题是,当我运行机器人时,用户名的书面文本不在正确的位置。我用 Photoshop 上的标尺检查了多次,它的坐标是 (485.3, 155.3)。

之后,我将它与python上的坐标进行了比较。据我了解,它不接受整数,但 0.3 px 的差异不应该像返回的图像那样大。它们在数量上确实大致匹配,但由于某种原因,它仍然在错误的位置。

我打开bot返回的图片,错位文字的坐标是(491.5 ,177.5)

This is the current result of the code.

我使用了一个 alt 帐户来查看使用较长用户名的文本的外观。我也在尝试寻找一种方法,如果文本是长用户名时可以自动调整大小,以避免用户名占据整个图像。

任何帮助将不胜感激。

感谢您的阅读。

【问题讨论】:

【参考方案1】:

实际上,我不久前制作了一个类似的系统,用于我的不和谐机器人。

这里有一些代码可以满足您的需求:

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

使用 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)

本例使用重写

【讨论】:

让它(希望)不那么混乱 我添加了一个例子来尝试让它更容易理解 表示你还没有定义resizetofit,或者换句话说:要么执行函数的那段代码在函数定义之前执行,要么根本没有定义函数 哦,您还必须编辑 resizetofit 函数,因为您将 self 参数添加到 GetTextDimensions,所以您必须将函数调用更改为 self.GetTextDimensions 您已经计算出最大水平尺寸(589),x 位置应该是文本的中心:leftmostx + maxhorizontal / 2,在本例中为 455 + 589 / 2,即 749.5(您'必须将其四舍五入到 750 或 749),并且 y 位置应该是你之前使用的 y 位置(或者你可以计算它应该在哪里像 x 位置)

以上是关于(PIL / Pillow)尽管坐标正确,但文本位置错误的主要内容,如果未能解决你的问题,请参考以下文章

RLE8 图像支持/使用 Pillow 解压(PIL fork)

爬虫学习笔记(十七)—— 字符验证码

PIL和Pillow

python3之成像库pillow

python pil 怎么安装

(linux / win)怎样安装Pillow和PIL-Pillow兼容包?