How to use unicode symbols in matplotlib?

Question:

import matplotlib.pyplot as pyplot

pyplot.figure()
pyplot.xlabel(u"u2736")
pyplot.show()

Here is the simplest code I can create to show my problem. The axis label symbol is meant to be a six-pointed star but it shows as a box. How do I change it so the star is shown? I’ve tried adding the comment:

#-*- coding: utf-8 -*-

like previous answers suggested but it didn’t work, as well as using matplotlib.rc or matplotlib.rcParams which also didn’t work. Help would be appreciated.

Asked By: Chris H

||

Answers:

You’ll need a font that has the given unicode character, STIX fonts should contain the star symbol. You’ll need to locate or download the STIX fonts, ofcourse any other ttf file with the given symbol should be fine.

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

if __name__ == "__main__":
    pyplot.figure() 
    prop = FontProperties()
    prop.set_file('STIXGeneral.ttf')
    pyplot.xlabel(u"u2736", fontproperties=prop)
    pyplot.show()
Answered By: arjenve

To complement @arjenve’s answer. To plot a Unicode character, firstly, find which font contains this character, secondly, use that font to plot characters with Matplotlib

Find a font which contains the character

According to this post, we can use fontTools package to find which font contains the character we want to plot.

from fontTools.ttLib import TTFont
import matplotlib.font_manager as mfm

def char_in_font(unicode_char, font):
    for cmap in font['cmap'].tables:
        if cmap.isUnicode():
            if ord(unicode_char) in cmap.cmap:
                return True
    return False

uni_char =  u"✹"
# or uni_char = u"u2739"

font_info = [(f.fname, f.name) for f in mfm.fontManager.ttflist]

for i, font in enumerate(font_info):
    if char_in_font(uni_char, TTFont(font[0])):
        print(font[0], font[1])

This script will print a list of font paths and font names (all these fonts support that Unicode Character). Sample output shown below

enter image description here

Then, we can use the following script to plot this character (see image below)

import matplotlib.pyplot as plt
import matplotlib.font_manager as mfm

font_path = '/usr/share/fonts/gnu-free/FreeSerif.ttf'
prop = mfm.FontProperties(fname=font_path)
plt.text(0.5, 0.5, s=uni_char, fontproperties=prop, fontsize=20)

plt.show()

enter image description here

Answered By: jdhao

I was trying to implement @jdhao code but it was constantly breaking because I also had type 0 fonts in my library.
So here’s a tweaked version of his code that does not break with type 0 fonts and also enables finding several uni chars at once in the same font by providing the function a list of uni chars instead of just 1 uni char.

For more details read the comments throughout the code.

#!/usr/bin/env python
from fontTools.ttLib import TTFont
import matplotlib.font_manager as mfm

def print_if_char_in_font(list_unichars, print_true_only=False):

  def char_in_font(unicode_char, font):
    if font.endswith('.ttc'): fontNo = 0 #default is for type -1 so type 0 fonts are going to break the code without this line
    else: fontNo = -1
    font=TTFont(font, fontNumber=fontNo)
    for cmap in font['cmap'].tables: 
      if cmap.isUnicode():
        for i in unicode_char: #tries to match all uni chars with font cmap
          if ord(i) not in cmap.cmap:
            return False
    return True #returns true if all uni chars in given list were found successfully

  font_info = [(f.fname, f.name) for f in mfm.fontManager.ttflist] #list of all font dirs & font names in system

  for i, font in enumerate(font_info):
    try:
      if char_in_font(list_unichars, font[0]) == True: #calls for the defined searching func and checks if True
        print('Yess!! loc:{0}; font name:{1}'.format(font[0], font[1])) #print statement if find is successful
      elif print_true_only==False: print('No :( -- {0}'.format(font[1])) #print statement is find fails
    except:
      print('Error!! Unable to load: {0}, font name: {1}'.format(font[0], font[1])) #if unable to load(type 1 fonts?)
  return

def main(): #this is newbie friendly code, only need to touch 'main' for it to work with your data!
  uni_char = [u'1',u'2', u'*', u"✹"]
  # uni_char = [u"✹"] #or [u"u2739"] #put inside list even if finding just one char!
  print_if_char_in_font(uni_char, print_true_only=True) #print_true_only is just going to output fonts that satisfy the search

###e.g
# Yess!! loc:C:UsersxxxAppDataLocalProgramsPythonPython310libsite-packagesmatplotlibmpl-datafontsttfDejaVuSansMono-Bold.ttf; font name:DejaVu Sans Mono
# Yess!! loc:C:UsersxxxAppDataLocalProgramsPythonPython310libsite-packagesmatplotlibmpl-datafontsttfDejaVuSans.ttf; font name:DejaVu Sans

### print_true_only=False is going to output for all fonts including those which do not satisfy:
# No :( -- Times New Roman
# Yess!! loc:C:UsersxxxAppDataLocalProgramsPythonPython310libsite-packagesmatplotlibmpl-datafontsttfDejaVuSans-Bold.ttf; font name:DejaVu Sans
# No :( -- Sitka Small

if __name__ == '__main__':
  main()
Answered By: luzclasil
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.