为何我的11号TrueType字体在渲染上与Windows不同?

6 投票
6 回答
4465 浏览
提问于 2025-04-16 16:12

简单来说:打开记事本,选择字体为“Arial,大小11”,然后仔细输入“this is just a test”这句话,最后截了个屏:

original

接着输入并运行以下Python代码:

import ImageFont, ImageDraw, Image
im = Image.open("c:/textimg.png") #the above image

pilfont = ImageFont.truetype("arial.ttf", 11)

compimg = Image.new("RGB", im.size, (255, 255, 255))
draw = ImageDraw.Draw(compimg)

draw.text((0,0), "this is just a test", (0,0,0), font=pilfont)

compimg.save("c:/compimg.png")

然而,结果却让人失望地不同:

sad

不仅大小不对,而且字体还有点阴影,而记事本里的显示是清晰的,没有任何像素溢出。

我该怎么才能让它像记事本那样显示呢?我在使用pygame时也遇到过同样的问题,所以我觉得我对TTF的基本理解可能有些欠缺。

更新:我又用pygame试了一次,结果也是一样。它确实有关闭抗锯齿的选项,但看起来只是根据某个阈值去掉了本该抗锯齿处理的像素。我得到的最接近的效果是使用15号字体。代码是:

pygfont = pygame.font.Font(r"c:\windows\fonts\arial.ttf", 15)
surf = pygfont.render("this is just a test", False, (0,0,0), (255,255,255))
pygame.image.save(surf, r"c:\pygameimg.png")

结果(上面是记事本原始效果以供比较):

KILL ME http://tinypic.com/images/404.gif

为什么我不能马上提供悬赏呢?

更新:这是比较所有方法的结果:

AIFEOIFEF

PIL 15,然后是记事本 11,再然后是pygame 15 关闭抗锯齿,最后是pygame 15 开启抗锯齿。

PIL 15实际上有正确的比例,只是进行了抗锯齿处理。那么:为什么15和11会有差别?怎么才能让它和Windows的显示方式一样?(还有pygame到底在干嘛?)

6 个回答

2

我觉得记事本里的“大小”是指字体的点数,而ImageFont.truetype()里的“大小”是指像素。

2

不同的大小是因为它们的定义方式不一样。要把点数转换成像素,可以用这个公式:像素 = 点数 * 96 / 72,这里的96是Windows设置的DPI(每英寸点数),而不是显示器的实际DPI。在你的例子中,11*96/72 = 14.6666,四舍五入后就是15。

至于想让文字在像素上完全一致,这在现有的工具下是不可能的——Ned说得对。如果这真的很重要,你需要使用Windows的API来渲染这个文字,然后把它复制到图像里。这不是一个简单的过程。

6

字体渲染是一个复杂而微妙的过程,而且已经被实现了很多次。在你的情况中,PIL和Windows看起来不同,是因为它们使用了完全不同的字体渲染引擎。Windows使用的是它自带的渲染方式,而PIL则使用它编译时所用的Freetype。

我不太清楚每个环境是如何理解它的“大小”参数的,但即使你让它们的理解方式一致,渲染出来的效果也会有所不同。想要得到和记事本一样的像素效果,最简单的方法就是打开记事本,然后截取屏幕。

也许如果你能多解释一下为什么想要和记事本一样的渲染效果,我们可以找到一些更有创意的解决方案来帮你解决问题。

撰写回答