Вот мои мысли:
Создайте функцию, взяв x
- начальную x
позицию, y
- y
позицию, text
- текст для рисования и fallbackList
- список шрифтов, упорядоченный как font-family
в CSS.
- При отображении текста используйте
fontTools.ttLib.TTFont
, чтобы проверить, содержится ли определенный символ в основном шрифте (fallbackList[0]
), анализируя таблицу шрифтов, просматривая ее и проверяя, находится ли данный символ внутри карты (см. этот вопрос). - Если результат шага 1 равен
False
(т.е. он не содержится в пакете шрифтов), пройдите fallbackList
, повторяя шаг 1, пока не найдете шрифт, который содержит его .Если вы найдете символ без шрифта, содержащего его, просто используйте первый шрифт.Мы назовем этот шрифт, который мы только что нашли, foundFont
. - Нарисуйте этот символ с помощью
textpath.TextPath((xPosNow, y) ...stuff... prop=foundFont).getextents()
(matplotlib> 1.0.0).Это рисует этот символ, но также получает ограничивающую рамку текста, из которой вы можете извлечь ширину (см. этот вопрос).Выполните xPosNow += textWidth
, где textWidth
извлечено из getextents()
.
Это, по существу, позволит подсчитать общее расстояние от начала координат (сложив вместе ширину каждого бита текста, который выдобавить), а затем, когда вам нужно добавить еще один бит текста другим шрифтом, просто установите значение x равным этому числу + немного для кернинга, и таким образом, вы можете просто работать там, где вы хотите, чтобы каждый символидти (но делать каждый символ отдельно).
Вот пример кода:
import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from fontTools.ttLib import TTFont
fig = plt.figure()
plt.axis([0, 8, 0, 6])
t = u'abcde♥'
plt.text(4.5, 4, 'DejaVu Sans:', horizontalalignment='right')
plt.text(5, 4, t, {'family':'DejaVu Sans'})
plt.text(4.5, 3, 'Noto Sans:', horizontalalignment='right')
plt.text(5, 3, t, {'family':'Noto Sans'})
plt.text(4.5, 2, 'Noto Sans Symbols2:', horizontalalignment='right')
plt.text(5, 2, t, {'family':'Noto Sans Symbols2'})
def doesContain(fontPath, unicode_char): # Helper function, the only issue being it takes font paths instead of names
font = TTFont(fontPath) # Use helper library to go through all characters
for cmap in font['cmap'].tables:
if cmap.isUnicode():
if ord(unicode_char) in cmap.cmap: # If the character table contains our character return True
return True
# Otherwise, return False.
return False
def renderText(x, y, text, fontSize, fallback_list, spacingSize):
xPosNow = x
for char in text: # For each character...
fontId = 0
while not doesContain(fallback_list[fontId]['path'], char): # find a font that works
if fontId < len(fallback_list) - 1:
fontId += 1
else: # Or just go with the first font, if nothing seems to match
fontId = 0
break
print(fontId)
t = plt.text(xPosNow, y, char, {'family':fallback_list[fontId]['name']})
r = fig.canvas.get_renderer()
xPosNow += t.get_window_extent(renderer=r).width/100 + spacingSize
Мы называем это с помощью:
renderText(3, 5, t, 9, [
{
'path': 'C:\\Users\\User\\Downloads\\NotoSans-hinted\\NotoSans-Regular.ttf', # Font path
'name': 'Noto Sans' # Font name
},
{
'path': 'C:\\Users\\User\\Downloads\\NotoSansSymbols2-unhinted\\NotoSansSymbols2-Regular.ttf',
'name': 'Noto Sans Symbols2'
}
]
, 0.08) # The distance between the letters
plt.show()
И вывод: