matplotlib:我可以使用辅助字体来缺少字形吗?

时间:2018-12-02 15:15:05

标签: python matplotlib graph fonts

我要使用的字体没有我需要的所有符号。如果缺少符号,是否可以让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 Sans Symbols2缺少字母。我正在尝试获取类似于DejaVu Sans的示例,但是要使用Noto Sans的字母和Noto Sans Symbols2的内心。

1 个答案:

答案 0 :(得分:4)

这是我的想法:

使用x-起始x位置,y-y位置,text-要绘制的文本,以及{{ 1}}-字体列表,在CSS中排序为fallbackList

  1. 渲染文本时,使用font-family通过分析字体表,循环遍历并检查给定字符来检查主字体(fontTools.ttLib.TTFont)中是否包含某个字符。在地图内(请参阅this问题)。
  2. 如果步骤1的结果是fallbackList[0](即它不包含在字体包中),请遍历False,重复步骤1,直到找到可以使用的字体包含它。如果找到一个没有字体的字符,请使用第一种字体。我们称此字体为fallbackList
  3. 使用foundFont(matplotlib> 1.0.0)绘制该字符。这将绘制该字符,但还会获取文本的边界框,您可以从该边界框提取宽度(请参见this问题)。执行textpath.TextPath((xPosNow, y) ...stuff... prop=foundFont).getextents(),其中xPosNow += textWidth是从textWidth中提取的。

从本质上来说,这将保持与原点的总距离(通过将所添加文本的每一位的宽度加在一起),然后在需要添加另一种不同字体的文本时,只需设置x值就是这个计数+一点点字距调整,这样,您就可以算出每个字符要去的位置(但每个字符要分开做)。

这是一个代码示例:

getextents()

我们称呼它为:

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

输出为:

image