matplotlib:我可以使用辅助字体来丢失字形吗?

Posted

技术标签:

【中文标题】matplotlib:我可以使用辅助字体来丢失字形吗?【英文标题】:matplotlib: Can I use a secondary font for missing glyphs? 【发布时间】:2019-05-04 00:41:52 【问题描述】:

我想使用的字体没有我需要的所有符号。如果缺少符号,是否可以让 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()

还有输出:

Noto Sans 缺少心形符号,而 Noto Sans Symbols2 缺少字母。我正在尝试获得类似 DejaVu Sans 示例的内容,但带有来自 Noto Sans 的字母和来自 Noto Sans Symbols2 的心。

【问题讨论】:

由于任何 matplotlib 文本只包含一种字体,因此没有这种回退机制。 你无法检查字体是否正确显示,只有当你没有字体时,它才会恢复为默认字体。所以我可以尝试的一件事是篡改matplotlib.font_manager.py,您可以根据字符返回 0 或 1 等值。您可能想尝试一下! 您是否尝试过将两个部分字体合并为新字体,例如使用 fonttools 的 pyftmerge? 有人想提供指向this question 的链接作为答案。自从它被删除后就放在这里。 IMO 无论如何它都不会回答这个问题,但在某些情况下可能仍然有用。 【参考方案1】:

这是我的想法:

创建一个函数,采用x - 起始x 位置,y - y 位置,text - 要绘制的文本,fallbackList - 字体列表,按@ 排序CSS 中的 987654331@。

    渲染文本时,使用fontTools.ttLib.TTFont检查某个字符是否包含在主要字体(fallbackList[0])中,通过解析字体表,循环遍历它,并检查给定字符是否在地图内(请参阅this 问题)。 如果第 1 步的结果是 False(即它不包含在字体包中),请遍历 fallbackList,重复第 1 步,直到找到确实包含它的字体.如果您发现一个没有包含它的字体的字符,只需使用第一种字体。我们将把我们刚刚找到的这个字体命名为foundFont。 使用textpath.TextPath((xPosNow, y) ...stuff... prop=foundFont).getextents() (matplotlib > 1.0.0) 绘制该字​​符。这会绘制该字符,但也会获取文本的边界框,您可以从中提取宽度(请参阅this 问题)。做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()

输出是:

【讨论】:

以上是关于matplotlib:我可以使用辅助字体来丢失字形吗?的主要内容,如果未能解决你的问题,请参考以下文章

彩色字体字形

如何将字体字形垂直居中?

字体字形 / UIWebView 中的内存泄漏

Pango 颜色字形

Apple 的文本渲染是如何绘制字体没有的字形的?

如何在 OpenType PostScript OTF 字体文件中用一个字形替换另一个字形?