1. 程式人生 > 其它 >錯誤RuntimeWarning: Glyph xxxxx missing from current font的產生原因解析,檢測當前字型是否包含某字元

錯誤RuntimeWarning: Glyph xxxxx missing from current font的產生原因解析,檢測當前字型是否包含某字元

技術標籤:MatplotlibPython基礎matplotlib字型GlyphUnicode字元

RuntimeWarning: Glyph xxxx missing from current fontmatplotlib的經典錯誤。
原因大家都清楚:字型不匹配,顯示不了對應的字元。
現象就是:本該顯示的字元,顯示為方框了,一般出現在漢字當中。

那這個錯誤是怎麼產生的呢?

除錯資訊

 d:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:201:
  RuntimeWarning: Glyph 39064
missing from current font. font.set_text(s, 0, flags=flags)

根據traceback資訊可知:

  1. 提示當前字型中找不到Glyph 39064
  2. 錯誤發生在backend_agg.py
  3. 出錯程式碼在font.set_text(s, 0, flags=flags)

問題分析

  1. Glyph是象形文字的意思,可以理解為符號,那39064是什麼呢?通過查詢漢字unicode編碼表可知,39064對應的漢字就是。從字面意思上看,錯誤資訊就是表示在當前設定的字型中找不到對應字元不單漢字會出現這樣的錯誤提示,只要當前字型中不存在對應字元都會報這個錯誤
  2. backend_agg.py
    matplotlib的繪圖後端模組。
  3. font.set_text(s, 0, flags=flags)屬於RendererAgg類中的draw_text方法。相關程式碼為:
        font = self._get_agg_font(prop)
        if font is None:
            return None
        # We pass '0' for angle here, since it will be rotated (in raster
        # space) in the following call to draw_text_image).
font.set_text(s, 0, flags=flags)

再次追蹤 _get_agg_font(prop)。

def _get_agg_font(self, prop):
    """
    Get the font for text instance t, caching for efficiency
    """
    fname = findfont(prop)
    font = get_font(fname)
    font.clear()
    size = prop.get_size_in_points()
    font.set_size(size, self.dpi)
    return font

font = get_font(fname)
from matplotlib.font_manager import findfont, get_font
問題又回到了font_manager模組了!

_get_font = lru_cache(64)(ft2font.FT2Font)
# FT2Font objects cannot be used across fork()s because they reference the same
# FT_Library object.  While invalidating *all* existing FT2Fonts after a fork
# would be too complicated to be worth it, the main way FT2Fonts get reused is
# via the cache of _get_font, which we can empty upon forking (in Py3.7+).
if hasattr(os, "register_at_fork"):
    os.register_at_fork(after_in_child=_get_font.cache_clear)


def get_font(filename, hinting_factor=None):
    # Resolving the path avoids embedding the font twice in pdf/ps output if a
    # single font is selected using two different relative paths.
    filename = os.path.realpath(filename)
    if hinting_factor is None:
        hinting_factor = rcParams['text.hinting_factor']
    return _get_font(os.fspath(filename), hinting_factor,
                     _kerning_factor=rcParams['text.kerning_factor'])

from matplotlib import afm, cbook, ft2font, rcParams

ft2fontmatplotlib的模組,在我的計算機上檔名是ft2font.cp37-win_amd64.pyd,看來是用其他語言編譯的模組。
通過查詢可知,ft2font模組的原始碼即https://github.com/matplotlib/matplotlib/blob/master/src/ft2font.cpp

static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode)
{
    FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
    if (!glyph_index) {
        PyErr_WarnFormat(NULL, 1, "Glyph %lu missing from current font.", charcode);
        // Apparently PyErr_WarnFormat returns 0 even if the exception propagates
        // due to running with -Werror, so check the error flag directly instead.
        if (PyErr_Occurred()) {
            throw py::exception();
        }
    }
    return glyph_index;
}

由此可知,這條錯誤從哪兒來了的!就是字型裡沒有對應的字元

檢測當前字型是否含有該字元

根據fonttools寫了一個簡易檢測當前字型是否含有該字元的小工具。

import sys
from itertools import chain

from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode

font_path=r"c:\windows\fonts\simhei.ttf"
def font_validation(input_string,font_path):
    font =TTFont(font_path)
    chars = chain.from_iterable([y + (Unicode[y[0]],) for y in x.cmap.items()] for x in font["cmap"].tables)
    for i in input_string:
        char=ord(i) 
        # 輸出字元、10進位制Unicode編號、16進位制Unicode編號、Unicode名稱、是否包含在字型內   
        print(i,char,hex(char),Unicode[char],char in (x[0] for x in chars))
    font.close()

font_validation("標題₩",font_path)

從結果看,韓元符號黑體是沒有的。

26631 0x6807 CJK UNIFIED IDEOGRAPH-6807 True39064 0x9898 CJK UNIFIED IDEOGRAPH-9898 True65510 0xffe6 FULLWIDTH WON SIGN False

使用黑體繪圖測試下:
在這裡插入圖片描述

import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

font = FontProperties(fname=r"c:\windows\fonts\simhei.ttf", size=30) 
plt.title("標題₩", fontproperties=font)
plt.show()

使用gulim繪圖測試下:
在這裡插入圖片描述

import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

font_path=r"C:\Users\Administrator\AppData\Local\Microsoft\Windows\Fonts\gulim.ttc"
font = FontProperties(fname=font_path, size=30) 
plt.title("標題₩", fontproperties=font)
plt.show()