why is my truetype font of size 11 rendering diffe

2019-02-14 18:47发布

问题:

To wit: Notepad is opened, the font selected as "Arial, Size 11", the words "this is just a test" carefully entered, a screenshot taken:

original http://i53.tinypic.com/200skuv.png

The following Python code is entered and run:

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")

Yet the result is disappointingly different:

sad http://i56.tinypic.com/9h7x55.png

Not only is it the wrong size, but it's also slightly shaded, whereas the notepad rendering is crisp and crosses no pixel boundaries.

How can I get it to render like notepad does? I've had this exact issue with pygame as well, so I think I'm missing some fundamental understanding about TTF here.

UPDATE: I tried it with pygame again. It does the same thing. It does have an option to turn off anti-aliasing but it looks like it just lops off whatever pixels it would have antialiased based on some threshold. The closest approximation I got was using size 15. The code was:

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")

and the result (notepad original on top for comparison):

KILL ME http://i56.tinypic.com/2r26mbs.png

Gah why can't I offer a bounty right away?

UPDATE: here's comparing all the methods:

AIFEOIFEF http://i56.tinypic.com/51ybtg.png

PIL 15, then notepad 11, then pygame 15 anti-alias-off, then pygame 15 anti-alias-on.

PIL 15 actually has the right proportions, it's just anti-aliased. so: why 15 vs. 11? how to make it do it the same way windows does? (and wtf is pygame doing?)

回答1:

Font rendering is a complex and subtle process, and one that has been implemented a number of times. In your case, PIL and Windows look different because they are using completely different font rendering engines. Windows uses its built-in rendering, and PIL is using the Freetype it was compiled with.

I don't know how each environment interprets its "size" parameter, but even if you get them interpreted the same, the rendering will simply be different. The way to get the same pixels as Notepad is to launch Notepad and grab the screen.

Perhaps if you explain more about why you want the same rendering as Notepad, we'll have creative solutions to your problem.



回答2:

I think Notepad's "size" is pointsize and ImageFont.truetype()'s "size" is pixels.



回答3:

The sizes are coming out different because they're specified differently. To convert points to pixels, use the formula: pixels = points * 96 / 72 where 96 is the DPI configured into Windows (not the actual DPI of the monitor). In your case, 11*96/72 = 14.6666, which rounds to 15.

As for making the text pixel-for-pixel identical, that will be impossible with the tools provided - Ned is correct. If this is absolutely vital, you'll need to use the Windows API to render this text for you and copy it into the image. Not a simple process.



回答4:

Success - look at the red line:

praise the lord http://i54.tinypic.com/2r60dc3.png

Using the method I created here.

Interestingly, I still had to supply the font size 15. I'm not sure if this is point vs. pixel related, though, as the docs say:

> 0: The font mapper transforms this value into device units and matches it against the cell height of the available fonts.

< 0: The font mapper transforms this value into device units and matches its absolute value against the character height of the available fonts.

However, supplying -11 didn't get the desired result... it just made it smaller.. so I have no clue. It's probably point vs. pixel.



回答5:

As Ned already pointed out, it is not very correct to try to get same result from some rendering library. If you want exactly the same native look, you try to get to Windows' internal functionality, and that is what you already done as I see from your other post, and it is very interesting.

If we speak of realistic text rendering for screen in general, there are some improtant nuances. First, all vector font formats are not initially developed for screen, but merely for typography, and its two big differences. For screen, all you need is a bitmap, namely 8-bit alpha channel information which you then blend to the screen background together with pixel values. To get things working you need literally to rip the TTF file into a set of bitmaps.

And here are two ways to go, roughly speaking:

  • Extract glyphs at high resolution in BW (1-bit). E.g. ~600px bitmaps would be enough for most cases. Then you can construct and resize strings, depending on needs.

  • Extract them directly to the target resolution as 8-bit arrays. In this case you cannot resize them, but only construct strings simply putting this 8-bit arrays in alpha channel and placing along the line.

In both cases you need also exact distance list and kerning pairs list, which is obviously different for each target resolution and extracting this information can vary for different font formats.

It is better to stick to first step, because the second approach you can achieve from high-res bitmaps as well, no need to rasterize it each time. For small sizes for better distance accuracy you can also generate "shifted" glyphs, that means same glyphs, but shifted in 1/2 or 1/3 pixel in initial high-res image. This again would require slightly different string construct procedure.

Important note about the size: there is no such precise thing as size of the font. In typography this is the size of Em square and does not have direct connection with x-height of the font. Moreover, if I do bitmap rendering or resampling, I only operate with pixel-size of the Em-square (boundaries of whole bitmap) and downsampling factor, if I use it to get to small bitmaps. Real x-height size can be approximatly calculated only for reference.

For your case of non-antialiased bitmaps, these small bitmaps must be already included in font file, at least for native fonts like Arial and Times. IIRC Those are stored inside TTF for particular small sizes 7 - 16 pt usually, and as 1-bit masks. The only question is how to extract or get to them them from an application. I don't know really, I see those merely as legacy stuff. I am more in realistic rendering problematics.