Solution to Chinese display problem in matplotlib
An error occurred while matplotlib was running:
UserWarning: findfont: Font family ['SimHei'] not found. Falling back to DejaVu Sa
perhaps
RuntimeWarning: Glyph 20998 missing from current font.
Error RuntimeWarning: Cause resolution of Glyph XXXX missing from current font to detect if the current font contains a character
RuntimeWarning: Glyph xxxx missing from current font is a classic error for matplotlib.
The reason is clear to all: the fonts do not match and the corresponding characters cannot be displayed.
Phenomena are: the characters that should be displayed are shown as boxes, and generally appear in Chinese characters.
So how did this mistake come about?
debug information
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) 123
According to the traceback information:
- Tips that Glyph 39064 cannot be found in the current font.
- The error occurred in backend_agg.py.
- Error code in font.set_text(s, 0, flags=flags).
problem analysis
- Glyph is an ideographic meaning, which can be interpreted as a symbol. What is 39064?Looking for the unicode encoding table of Chinese characters shows that the corresponding Chinese characters of 39064 are the title.Literally, the error message is that no corresponding character can be found in the currently set font.An error message like this will appear for more than one Chinese character. This error will be reported as long as there are no corresponding characters in the current font.
- backend_agg.py is the drawing back-end module of matplotlib.
- font.set_text(s, 0, flags=flags) belongs to draw_in the RendererAgg classText method.The relevant code is:
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) 123456
Trace Again_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 12345678910
font = get_font(fname)
from matplotlib.font_manager import findfont, get_font
The question goes back to font_manager module!
_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']) 1234567891011121314151617 from matplotlib import afm, cbook, ft2font, rcParams
Ft2font is a module of matplotlib and the file name on my computer is ft2font.cp37-win_amd64.pyd, appears to be a module compiled in another language.
Find out that the source code of the ft2font module is 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; } 12345678910111213
So, where did this error come from?There are no corresponding characters in the font!
Detect if the current font contains this character
A simple tool to detect if the current font contains this character was written based on 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) # Output character, 10-digit Unicode number, 16-digit Unicode number, Unicode name, whether included in font print(i,char,hex(char),Unicode[char],char in (x[0] for x in chars)) font.close() font_validation("Title₩",font_path) 1234567891011121314151617
As a result, there is no Korean Won symbol black body.
Label 26631 0 x6807 CJK UNIFIED IDEOGRAPH-6807 True Topic 39064 0 x9898 CJK UNIFIED IDEOGRAPH-9898 True ₩ 65510 0xffe6 FULLWIDTH WON SIGN False 123
Under Test Using Black Body Drawing:
import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties font = FontProperties(fname=r"c:\windows\fonts\simhei.ttf", size=30) plt.title("Title₩", fontproperties=font) plt.show() 123456
Test using gulim drawing:
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("Title₩", fontproperties=font) plt.show()
Solution
Method One
1. Download the corresponding Chinese font link:https://pan.baidu.com/s/1Jb4-qWXPd-iDP4VnaIYhDQ Password: ppse
There are many font styles arranged in this link. You can download them to choose the font style you want to use.
1.1 Add the following two lines of code to the drawing program to display Chinese in the drawing. Change the code to your path name followed by the font style you want to use.
SimHei.ttf below/font-master/unicode under downloaded files can be used in general Chinese
from matplotlib.font_manager import FontProperties font = FontProperties(fname=r"Own path name/SimHei.ttf", size=15) 1234
- This method is cumbersome, however, to find the path name to add each time
- You can try the second method, once and for all
Method 2
1. Download fonts first
2. Find the configuration file for matplitlib on your computer
import matplotlib print(matplotlib.matplotlib_fname()) 123
Run code in IDLE to find the path to your matplitlib configuration file
3. Place the font file to be used (e.g. SimHei.ttf font) under / site-packages/matplotlib/mpl-data/fonts/ttf [External chain picture transfer failed, the source may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-IEt59Uf4-1631034258957)](https://www.pianshen.com/images/806/2a091b0f3818904d395277e849ecc2ae.png)]
4. Chinese cannot be displayed in the diagram after running the program at this time because matplotlib still has a cache, so it will run normally after clearing the cache.
This is the key step. If you try every step, but you don't succeed in the end, the cache may not have been deleted.
- (1) On Linux: root/.cache/matplotlib/(/.cache/matplotlib/fontList.json file does not save newly installed fonts).If the folder does not exist, delete ~/.cache/ipython (?This run was successful)
- (2) Under Windows: Delete the C:\Users\dongfang.matplotlib folder
5. After that, just write the following sentence in the legend that you want to use Chinese to solve the Chinese scrambling problem perfectly.
# For normal display of Chinese labels, SimHei is the name of the font, the font must exist in the system again, the way the font is viewed and installed Part 3 plt.rcParams['font.sans-serif']=['SimHei'] 123
5. After that, you can run the program to display Chinese
The error is that Matplotlib cannot display Chinese because Matplitlib does not support Chinese fonts by default and needs to provide a SimHei.ttf file.
Server Code
1. Download SimHei.ttf file
Download links are provided here:https://www.uslogger.com/details/3
2. Find the path where Matplotlib stores ttf files
Enter the python script to do the following:
>>> import matplotlib >>> print(matplotlib.matplotlib_fname()) /home/book/.local/lib/python3.5/site-packages/matplotlib/mpl-data/matplotlibrc
Find ttf directory:
$ cd ~/.local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf
3. Copy SimHei.ttf file to TTF directory
$ cp /mnt/hgfs/DirShare/SimHei.ttf .
4. Clear Matplotlib Cathe Cache
$ rm /root/.cache/matplotlib/fontlist-v300.json
5. The following Matplotlib statements will be executed without error
plt.rcParams['font.sans-serif']=['SimHei']