matplotlib: Могу ли я использовать дополнительный шрифт для отсутствующих глифов? - PullRequest
0 голосов
/ 02 декабря 2018

Шрифт, который я хочу использовать, не содержит всех необходимых мне символов.Можно ли заставить matplotlib использовать другой шрифт, если символ отсутствует?

Вот минимальный пример:

import matplotlib.pyplot as plt

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'})

plt.show()

И вывод:

And the output.

В Noto Sans отсутствует символ сердца, а в символах Noto Sans2 - буквы.Я пытаюсь получить что-то вроде примера DejaVu Sans, но с письмами от Noto Sans и сердцем от Noto Sans Symbols2.

1 Ответ

0 голосов
/ 10 августа 2019

Вот мои мысли:

Создайте функцию, взяв x - начальную x позицию, y - y позицию, text - текст для рисования и fallbackList - список шрифтов, упорядоченный как font-family в CSS.

  1. При отображении текста используйте fontTools.ttLib.TTFont, чтобы проверить, содержится ли определенный символ в основном шрифте (fallbackList[0]), анализируя таблицу шрифтов, просматривая ее и проверяя, находится ли данный символ внутри карты (см. этот вопрос).
  2. Если результат шага 1 равен False (т.е. он не содержится в пакете шрифтов), пройдите fallbackList, повторяя шаг 1, пока не найдете шрифт, который содержит его .Если вы найдете символ без шрифта, содержащего его, просто используйте первый шрифт.Мы назовем этот шрифт, который мы только что нашли, foundFont.
  3. Нарисуйте этот символ с помощью 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()

И вывод:

image

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...