我正在构建一个基于Kivy的应用程序,并需要包含一个字体选择器。由于我找不到适合Kivy的字体选择器,所以我正在自己构建。但我无法找到一种方法来判断一个字体是否适用于我(即,我是否可以使用它制作一个句子而不是一串符号)。我正在使用Pillow ImageFont。在Python中是否有区分符号和文本字体的方法?
我正在构建一个基于Kivy的应用程序,并需要包含一个字体选择器。由于我找不到适合Kivy的字体选择器,所以我正在自己构建。但我无法找到一种方法来判断一个字体是否适用于我(即,我是否可以使用它制作一个句子而不是一串符号)。我正在使用Pillow ImageFont。在Python中是否有区分符号和文本字体的方法?
我一直在努力,现在我的工作有所改善。虽然还不完美,但以下代码在我的Ubuntu上只给了我一个错误的字体支持文本:
def isValidFont(f, fontNum=0, debug=False):
"""
Determine if a font is a valid font for displaying text
This code makes a best guess as to whether the font supports text.
It does this by writing a 'W' using the font, and inspecting the result.
:param f: full path to the font file
:param fontNum: index into the font file (for a file containing a collection of fonts)
:param debug: If True, files will be written for each font with information useful for debugging
:return: True if font appears to support text, False otherwise
"""
# size of test image
width = 40
height = 40
font = ImageFont.truetype(f, index=fontNum, size=height-6)
fontName = font.getname()
tmpImg = Image.new('1', (width,height)) #default fill is 0 (black)
# draw a single 'W' into the test image (sized and positioned to fit inside test image)
# this codes depends on the character drawn being a 'W'
dr = ImageDraw.Draw(tmpImg)
dr.text((3, 3), 'W', font=font, fill=(1))
if debug:
# save test image for this font
fname = str(fontName) + '.bmp'
tmpImg.save(fname)
# get the data from the image as a list of 1's and 0's (one value per pixel)
img_data = list(tmpImg.getdata())
if debug:
# write the image data to a file
fname = str(fontName) + '.txt'
fd = open(fname, mode='w')
for row in range(height):
fd.write(str(img_data[row*width : (row+1)*width]) + '\n')
fd.close()
# if the image is all black (0's), this is not a valid text font
if sum(img_data) == 0:
return False
# build a simplified version of the image data
compressedList = []
for i in range(height):
prev_elem = None
thisRow = []
for j in range(width):
index = i*width + j
elem = img_data[index] # this is the element at (i,j)
if prev_elem is None:
# first element in this row, just append to "thisRow"
thisRow.append(elem)
prev_elem = elem
elif elem == prev_elem:
# if this element is same as previous (and it's a one), just increment the value in "thisRow"
if elem == 1:
thisRow[len(thisRow)-1] += 1
else:
# just append the element to "thisRow"
thisRow.append(elem)
prev_elem = elem
# finished row #i, append it to "compressedList"
compressedList.append(thisRow)
# a bit more compressing
for row in compressedList:
# eliminate leading zeros from each row
while len(row) > 0 and row[0] == 0:
del row[0]
# eliminate trailing zeros from each row
while len(row) > 0:
index = len(row)-1
if row[index] == 0:
del row[index]
else:
break
# eliminate leading empty rows
while len(compressedList[0]) == 0:
del compressedList[0]
# eliminate trailing empty rows
index = len(compressedList)-1
while len(compressedList[index]) == 0:
del compressedList[index]
index = len(compressedList)-1
if debug:
# save the compressed format
fname = str(fontName) + '_c.txt'
fd = open(fname, mode='w')
for row in compressedList:
fd.write(str(row) + '\n')
fd.close()
# this is where the decision is actually made
for row in compressedList:
if len(row) > 3: # characteristic of a 'W', a simple box will have maximum rowLen of 3
return True
return False
我不是这方面的专家,但是我会给你我的看法。
当你尝试使用某种字体写信时,渲染器将使用该字母的代码点从字体文件中获取绘制指令。有三种情况可能发生:
0x0041
,字体包含绘制某个表情符号的指令,而不是绘制拉丁大写字母"A"的指令。在第一种情况下,你没有选择:只有人类(或非常高级的脚本)才能说“这一堆像素包含表情符号而不是字母”。但这种情况应该很少见:这不是(正常)人们创建符号字体的方式。
第二种情况很简单:这种字体没问题,你可以使用它。
第三种情况比较有趣。有时人们会保留 Unicode 图像的代码点(例如),但删除字母的指令以减小字体文件的大小。 ImageFont 似乎没有特定的方法可以按代码点提取信息,但它 允许我们 获取文本掩模。可以由字体绘制的文本的掩模将具有 (0, 0)
大小:
from PIL import ImageFont
font = ImageFont.truetype("font_to_test.ttf")
mask = font.getmask('abcdefg') # use some letters from lang you need
if mask.size == (0, 0):
print('This is image font.')
else:
print('This is text font.')
不是很理想,但对我测试的几种字体来说还算可以胜任。