garrettj403 / SciencePlots

Matplotlib styles for scientific plotting

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CJK Noto Fonts - Chinese and Korean don't work - Font 'default' does not have a glyph

downeykking opened this issue · comments

os: ubuntu 22.04
python: 3.9
scienceplots: 2.0.1

After following the FAQ Installing CJK fonts, I have already installed fonts-noto-cjk.
But when i tried the examples like:

with plt.style.context(['science', 'no-latex', 'cjk-tc-font']):
    fig, ax = plt.subplots()
    for p in [5, 7, 10, 15, 20, 30, 38, 50, 100]:
        ax.plot(x, model(x, p), label=p)
    ax.legend(title='Order', fontsize=7)
    ax.set(xlabel=r'電壓 (mV)')
    ax.set(ylabel=r'電流 ($\mu$A)')
    ax.autoscale(tight=True)
    fig.savefig('figures/fig14a.jpg', dpi=300)

with plt.style.context(['science', 'no-latex', 'cjk-sc-font']):
    fig, ax = plt.subplots()
    for p in [5, 7, 10, 15, 20, 30, 38, 50, 100]:
        ax.plot(x, model(x, p), label=p)
    ax.legend(title='Order', fontsize=7)
    ax.set(xlabel=r'电压 (mV)')
    ax.set(ylabel=r'电流 ($\mu$A)')
    ax.autoscale(tight=True)
    fig.savefig('figures/fig14b.jpg', dpi=300)

with plt.style.context(['science', 'no-latex', 'cjk-jp-font']):
    fig, ax = plt.subplots()
    for p in [5, 7, 10, 15, 20, 30, 38, 50, 100]:
        ax.plot(x, model(x, p), label=p)
    ax.legend(title='Order', fontsize=7)
    ax.set(xlabel=r'電圧 (mV)')
    ax.set(ylabel=r'電気 ($\mu$A)')
    ax.autoscale(tight=True)
    fig.savefig('figures/fig14c.jpg', dpi=300)

Only cjk-jp-font made sense. Noto Serif CJK SC and Noto Serif CJK TC both failed.

findfont: Generic family 'serif' not found because none of the following families were found: Noto Serif CJK SC
findfont: Generic family 'serif' not found because none of the following families were found: Noto Serif CJK TC

How can I solve this problem? Thanks in advance.

commented

I have same problem in using simplified chinese。

RuntimeWarning: Glyph 21387 missing from current font.
  font.set_text(s, 0, flags=flags)
Font 'default' does not have a glyph for '\u7535' [U+7535], substituting with a dummy symbol.
Font 'default' does not have a glyph for '\u6d41' [U+6d41], substituting with a dummy symbol.
Font 'default' does not have a glyph for '\u7535' [U+7535], substituting with a dummy symbol.
Font 'default' does not have a glyph for '\u6d41' [U+6d41], substituting with a dummy symbol.
Font 'default' does not have a glyph for '\u7535' [U+7535], substituting with a dummy symbol.
Font 'default' does not have a glyph for '\u6d41' [U+6d41], substituting with a dummy symbol.

Hi @machel7 and @downeykking , I won't be able to troubleshoot it for some weeks due to personal constraints.
Can you two find which Latex distribution are you using (e.g. MikTex, TexLive...?
machel7, could you also please provide OS, Python version and SciencePlots version?

Till I return, you can have a look at this wiki entry, it may hint you in solving this issue. Have a look at the issue mentioned in the wiki, in some examples, it uses a "pgf" backend, that might solve it.

Whatever you find useful, please let us know.

Well, I had a little time to look into it, enough I believe.
I come with bad news @downeykking and @machel7 . TLDR at the bottom.

Issue confirmed, SciencePlots docs are outdated

I've been doing the wiki step by step (on Ubuntu 22.04, python 3.9), and found I can't run this snippet:

>>> import matplotlib.font_manager as fm
>>> fm._rebuild()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'matplotlib.font_manager' has no attribute '_rebuild'

Went to matplotlib's repo and found this issue, which is addressed in this PR, still draft, has been some time since last update. They've got some problems with the font_manager module, can't really tell what happened to ._rebuild() as they claim it is internal API, so it is not even in the changelogs.

Some troubleshooting

I've been trying matplotlib versions prior to the ._rebuild() deprecation in v3.4.0 (found by trial and error), but with no luck rebuilding the fonts in any case, the fonts-noto-cjk of course installed and in some cases for older versions of matplotlib, even the JP font wasn't found in the font_manager cache (it was for v3.3.0):

{
	"fname": "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
	"name": "Noto Serif CJK JP",
	"style": "normal",
	"variant": "normal",
	"weight": 400,
	"stretch": "normal",
	"size": "scalable",
	"__class__": "FontEntry"
},
{
	"fname": "/usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc",
	"name": "Noto Serif CJK JP",
	"style": "normal",
	"variant": "normal",
	"weight": 700,
	"stretch": "normal",
	"size": "scalable",
	"__class__": "FontEntry"
}

TLDR

The problem is that matplotlib is not reloading the fonts and it can't find the noto CJK (well, only the Japanese in v3.6.3).
I haven't been able to find a workaround to this issue. I encourage you to look for other sources like this stackoverflow post, although it looks a bit out-of-date. Also, here is a repo that claims to wrap matplotlib and allow Chinese fonts, hope it helps.

Whatever solution you find, please, let us know so the next person to have that problem has the reference.

I encountered the same problem when using Simplified Chinese.

Finally, I used mplfonts as a temporary solution.

mplfonts is a fonts manager for matplotlib.

This is a python package and command line tool to manage your matplotlib fonts. You can easily resolve the "tofu" problem when plotting with CJK(Chinese, Japanese, Korean) languages.

Hope it helps!

Hi @FunClip!

Thank you very much for letting us know! I think I will deprecate our CJK styles, as I don't have both time & motivation to try to fix that. I will update the wiki in shortly.

Again, thank you.

Finally it worked with CJK font! Be careful to set "pgf.preamble". I use the code from matplotlib documentation.

import matplotlib as mpl
import matplotlib.pyplot as plt
import scienceplots

plt.style.use(["science"])
mpl.use("pgf")

params = {
    "font.family": "serif",
    "text.usetex": True,
    "pgf.rcfonts": False,
    "pgf.texsystem": "xelatex",
    "pgf.preamble": "\n".join(
        [
            r"\usepackage{fontspec, xeCJK}",
            # r"\setmainfont{Times New Roman}",
            r"\setCJKmainfont{SimSong}",
            r"\setCJKsansfont{Hei}",
        ]
    ),
}
mpl.rcParams.update(params)

x = [1, 2, 3, 4, 5, 6]
y = [1, 4, 9, 16, 25, 36]
plt.figure()
plt.plot(x, y)
plt.title("中文标题 xyz 123")
plt.savefig("test.pdf")
image with Chinese font

It seems that the pgf backend can only save the figure to pdf format. I'm wondering how to save the figure as png, svg, or other formats.