rougier / freetype-py

Python binding for the freetype library
Other
298 stars 88 forks source link

Bitmap of numbers or letters is incorrect #193

Closed lp20010415 closed 1 month ago

lp20010415 commented 1 month ago

code is

import freetype

font = freetype.Face(r"C:\Windows\Fonts\simsun.ttc")

# 绘制字符
font.set_char_size(16 * 16)
font.set_pixel_sizes(16, 16)
font.load_char('1')
bitmap = font.glyph.bitmap
print(bitmap.rows, bitmap.width)
print(bitmap.buffer)
print(len(bitmap.buffer))
left = []
right = []
for b, j in enumerate(bitmap.buffer):
    b_res = list(format(j, "08b"))
    for r in b_res:
        if r == "1":
            print("\033[1;30;46m   \033[0m", end="")
        else:
            print("\033[1;30;40m   \033[0m", end="")
    if b % 2 != 0 and b > 0:
        right.append(format(j, "02X"))
        print()
    else:
        left.append(format(j, "02X"))
print(' '.join(left))
print(' '.join(right))

num is incorrect(Maybe the ascii is incorrect?): image

Chinese input is correct: image

HinTak commented 1 month ago

Your code has a few bugs:

HinTak commented 1 month ago

Here is the corrected code:

import freetype

font = freetype.Face(r"simsun.ttc")

# 绘制字符
font.set_pixel_sizes(19, 19)
font.load_char('1', freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)
#font.load_char('字', freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)
bitmap = font.glyph.bitmap
print(bitmap.rows, bitmap.width, bitmap.pitch)
assert(font.glyph.format == freetype.FT_GLYPH_FORMAT_BITMAP)
assert(font.glyph.bitmap.pixel_mode == freetype.FT_PIXEL_MODE_MONO)
print(bitmap.buffer)
print(len(bitmap.buffer))
left = []
right = []
for b, j in enumerate(bitmap.buffer):
    b_res = list(format(j, "08b"))
    for r in b_res:
        if r == "1":
            print("\033[1;30;46m   \033[0m", end="")
        else:
            print("\033[1;30;40m   \033[0m", end="")
    if (b + 1) % bitmap.pitch == 0 and b > 0:
        right.append(format(j, "02X"))
        print()
    else:
        left.append(format(j, "02X"))
print(' '.join(left))
print(' '.join(right))

Your code also has the unfortunate problem of using pixel size 16. This particular font has some embedded bitmaps at size 12 to 17. Thus some glyph returns the embedded bitmap without needing FT_LOAD_RENDER, some without embedded bitmp just return garbage. Anyway, it has multiple problems, and it would have save both you and me a lot of time if you start from one of the examples in the example directory... (most of them have FT_LOAD_RENDER, except those which draws outlines and obviously want the outlines returned rather than a rendered bitmap!).

HinTak commented 1 month ago

I have put the code under https://github.com/rougier/freetype-py/blob/master/examples/user-code-from-issue-193.py

HinTak commented 1 month ago

As for your question in #191, you can modify the 'load_chars' line in the example to do this (it just encode from utf-8 to gb18030 then decode it back... obviously you can decode directly if your input is in gb18030):

gb18030code='字'.encode('gb18030')
print(gb18030code) # b'\xd7\xd6' , as below
font.load_char(b'\xd7\xd6'.decode('gb18030'), freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)
lp20010415 commented 1 month ago

As for your question in #191, you can modify the 'load_chars' line in the example to do this (it just encode from utf-8 to gb18030 then decode it back... obviously you can decode directly if your input is in gb18030):

gb18030code='字'.encode('gb18030')
print(gb18030code) # b'\xd7\xd6' , as below
font.load_char(b'\xd7\xd6'.decode('gb18030'), freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)

Oh,Jesus.I can't believe you also linked the issue with 191, thank you so much!

lp20010415 commented 1 month ago

As for your question in #191, you can modify the 'load_chars' line in the example to do this (it just encode from utf-8 to gb18030 then decode it back... obviously you can decode directly if your input is in gb18030):

gb18030code='字'.encode('gb18030')
print(gb18030code) # b'\xd7\xd6' , as below
font.load_char(b'\xd7\xd6'.decode('gb18030'), freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)

In the latest version, "freetype.FT_LOAD_TARGETS['FT_LOAD_TARGET_MONO']" is used.

HinTak commented 1 month ago

freetype.FT_LOAD_TARGETS['FT_LOAD_TARGET_MONO'] is a bit confusing - that argument is a bit-field, so you set them by doing a | b | c | d . Combo flags for commonly used flags are nice, but it hides the fact that it is a bit field. For example, you can disable usage of embedded bitmaps with FT_LOAD_NO_BITMAP | otherflags to force it to ignore embedded bitmaps. In which case, any character (Chinese and ascii) in your original code would return a garbage bitmap at size 16, rather than just the ascii characters returning garbage.

lp20010415 commented 1 month ago

freetype.FT_LOAD_TARGETS['FT_LOAD_TARGET_MONO']有点令人困惑 - 该参数是一个位字段,因此您可以通过执行来设置它们a | b | c | d 。常用标志的组合标志很好,但它隐藏了它是一个位字段的事实。例如,您可以禁用嵌入位图的使用,以FT_LOAD_NO_BITMAP | otherflags强制它忽略嵌入位图。在这种情况下,原始代码中的任何字符(中文和 ascii)都会返回大小为 16 的垃圾位图,而不仅仅是返回垃圾的 ascii 字符。

I use python 3.9 In my version, the correct way to writefreetype.FT_LOAD_RENDE is freetype.FT_LOAD_FLAGS['FT_LOAD_RENDER'], and the correct way to write freetype.FT_LOAD_TARGET_MONO is freetype.FT_LOAD_TARGETS['FT_LOAD_TARGET_MONO']. However, only IDLE raises an error. In practice, you can still use freetype.FT_LOAD_RENDER and freetype.FT_LOAD_TARGET_MONO despite the error.

image image

lp20010415 commented 1 month ago

As for your question in #191, you can modify the 'load_chars' line in the example to do this (it just encode from utf-8 to gb18030 then decode it back... obviously you can decode directly if your input is in gb18030):

gb18030code='字'.encode('gb18030')
print(gb18030code) # b'\xd7\xd6' , as below
font.load_char(b'\xd7\xd6'.decode('gb18030'), freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO)

I have a question: how should I use Set_Charmap? This piece of code doesn't seem to work:

def control_size(res):
    surplus = 16 - len(res) // 2
    if surplus % 2 == 0 and surplus > 0:
        add_num = surplus // 2
        for j in range(add_num):
            res.insert(0, 0)
            res.insert(0, 0)
            res.append(0)
            res.append(0)
    elif surplus % 2 != 0 and surplus > 0:
        add_num = surplus // 2
        for j in range(add_num):
            res.insert(0, 0)
            res.insert(0, 0)
            res.append(0)
            res.append(0)
        res.append(0)
        res.append(0)

font = freetype.Face(r"C:\Windows\Fonts\simsun.ttc")
font.set_charmap(freetype.FT_ENCODINGS['FT_ENCODING_BIG5'])

# 绘制字符
font.set_pixel_sizes(16, 16)
font.load_char('2', freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGETS['FT_LOAD_TARGET_MONO'])
bitmap = font.glyph.bitmap

left = []
right = []
b_b = bitmap.buffer
control_size(b_b)
for i in range(len(b_b) // bitmap.pitch):
    res = b_b[i * 2] << 8 | b_b[i * 2 + 1]
    # print(b_b[i], b_b[i + 1])
    b_res = list(format(res >> 5, "016b"))
    for r in b_res:
        if r == "1":
            print("\033[1;30;43m   \033[0m", end="")
        else:
            print("\033[1;30;40m   \033[0m", end="")
    print()
print(' '.join(left))
print(' '.join(right))
HinTak commented 1 month ago

Already answered the set_chairmap question in #191 - there are two example scripts in the example directory, one called font-info.py, one called ftdump. Both of them display font file info. Both of them show you what charmaps simsun.ttc was shipped with. It was shipped with two unicode charmaps only (one for apple, one for microsoft). Hence you cannot set charmap to a big5 one, since there isn't one for that encoding. You need to do the same as above: "big5input".decode('big5').

Your IDE is broken/buggy if it doesn't show freetype.FT_LOAD_RENDER... even python's own repr gives you that..

HinTak commented 1 month ago

https://github.com/rougier/freetype-py/blob/master/examples/font-info.py and https://github.com/rougier/freetype-py/blob/master/examples/ftdump.py

lp20010415 commented 1 month ago

Already answered the set_chairmap question in #191 - there are two example scripts in the example directory, one called font-info.py, one called ftdump. Both of them display font file info. Both of them show you what charmaps simsun.ttc was shipped with. It was shipped with two unicode charmaps only (one for apple, one for microsoft). Hence you cannot set charmap to a big5 one, since there isn't one for that encoding. You need to do the same as above: "big5input".decode('big5').

Your IDE is broken/buggy if it doesn't show freetype.FT_LOAD_RENDER... even python's own repr gives you that..

:) No problem, I just noticed it myself. Thank you!