From 349762b8d2fba9638746e6ed1c8d237a925561ec Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 21 Oct 2015 14:31:34 -0400 Subject: [PATCH 1/4] Reduce number of font files opened This should hopefully address the long-reported "Too many open files" error message (Fix #3315). To reproduce: On a Mac or Windows box with starvation for file handles (Linux has a much higher file handle limit by default), build the docs, then immediately build again. This will trigger the caching bug. The font cache in the mathtext renderer was broken. It was caching a font file once for every *combination* of font properties, including things like size. Therefore, in a complex math expression containing many different sizes of the same font, the font file was opened once for each of those sizes. Font files are opened and kept open (rather than opened, read, and closed) so that FreeType only needs to load the actual glyphs that are used, rather than the entire font. In an era of cheap memory and fast disk, it probably doesn't matter for our current fonts, but once #5214 is merged, we will have larger font files with many more glyphs and this loading time will matter more. The solution here is to do all font file loading in one place and to use `lru_cache` (available since Python 3.2) to do the caching, and to use only the file name and hinting parameters as a cache key. For earlier versions of Python, the functools32 backport package is required. (Or we can discuss whether we want to vendor it). --- lib/matplotlib/backends/backend_agg.py | 22 +++++++--------------- lib/matplotlib/backends/backend_pdf.py | 20 ++++++-------------- lib/matplotlib/backends/backend_pgf.py | 3 +-- lib/matplotlib/backends/backend_ps.py | 18 +++++------------- lib/matplotlib/backends/backend_svg.py | 17 +++++------------ lib/matplotlib/font_manager.py | 13 +++++++++++-- lib/matplotlib/mathtext.py | 9 ++++----- lib/matplotlib/tests/__init__.py | 6 ------ lib/matplotlib/textpath.py | 8 ++++---- lib/mpl_toolkits/tests/__init__.py | 6 ------ setup.py | 1 + setupext.py | 23 +++++++++++++++++++++++ 12 files changed, 67 insertions(+), 79 deletions(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index f3b23ca95531..5c4c4d96c6c4 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -32,8 +32,8 @@ FigureManagerBase, FigureCanvasBase from matplotlib.cbook import is_string_like, maxdict, restrict_dict from matplotlib.figure import Figure -from matplotlib.font_manager import findfont -from matplotlib.ft2font import FT2Font, LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, \ +from matplotlib.font_manager import findfont, get_font +from matplotlib.ft2font import LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, \ LOAD_DEFAULT, LOAD_NO_AUTOHINT from matplotlib.mathtext import MathTextParser from matplotlib.path import Path @@ -81,7 +81,6 @@ class RendererAgg(RendererBase): # renderer at a time lock = threading.RLock() - _fontd = maxdict(50) def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') RendererBase.__init__(self) @@ -191,6 +190,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): flags = get_hinting_flag() font = self._get_agg_font(prop) + if font is None: return None if len(s) == 1 and ord(s) > 127: font.load_char(ord(s), flags=flags) @@ -272,18 +272,10 @@ def _get_agg_font(self, prop): if __debug__: verbose.report('RendererAgg._get_agg_font', 'debug-annoying') - key = hash(prop) - font = RendererAgg._fontd.get(key) - - if font is None: - fname = findfont(prop) - font = RendererAgg._fontd.get(fname) - if font is None: - font = FT2Font( - fname, - hinting_factor=rcParams['text.hinting_factor']) - RendererAgg._fontd[fname] = font - RendererAgg._fontd[key] = font + fname = findfont(prop) + font = get_font( + fname, + hinting_factor=rcParams['text.hinting_factor']) font.clear() size = prop.get_size_in_points() diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 0ef7c1c49634..0dad65eec996 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -35,11 +35,11 @@ from matplotlib.cbook import Bunch, is_string_like, \ get_realpath_and_stat, is_writable_file_like, maxdict from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, is_opentype_cff_font +from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font from matplotlib.afm import AFM import matplotlib.type1font as type1font import matplotlib.dviread as dviread -from matplotlib.ft2font import FT2Font, FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, \ +from matplotlib.ft2font import FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, \ LOAD_NO_HINTING, KERNING_UNFITTED from matplotlib.mathtext import MathTextParser from matplotlib.transforms import Affine2D, BboxBase @@ -757,7 +757,7 @@ def createType1Descriptor(self, t1font, fontfile): if 0: flags |= 1 << 18 - ft2font = FT2Font(fontfile) + ft2font = get_font(fontfile) descriptor = { 'Type': Name('FontDescriptor'), @@ -817,7 +817,7 @@ def _get_xobject_symbol_name(self, filename, symbol_name): def embedTTF(self, filename, characters): """Embed the TTF font from the named file into the document.""" - font = FT2Font(filename) + font = get_font(filename) fonttype = rcParams['pdf.fonttype'] def cvt(length, upe=font.units_per_EM, nearest=True): @@ -1526,7 +1526,6 @@ def writeTrailer(self): class RendererPdf(RendererBase): - truetype_font_cache = maxdict(50) afm_font_cache = maxdict(50) def __init__(self, file, image_dpi): @@ -2126,15 +2125,8 @@ def _get_font_afm(self, prop): return font def _get_font_ttf(self, prop): - key = hash(prop) - font = self.truetype_font_cache.get(key) - if font is None: - filename = findfont(prop) - font = self.truetype_font_cache.get(filename) - if font is None: - font = FT2Font(filename) - self.truetype_font_cache[filename] = font - self.truetype_font_cache[key] = font + filename = findfont(prop) + font = get_font(filename) font.clear() font.set_size(prop.get_size_in_points(), 72) return font diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index ce00652e1388..4f00ea0b43ab 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -36,10 +36,9 @@ system_fonts = [] if sys.platform.startswith('win'): from matplotlib import font_manager - from matplotlib.ft2font import FT2Font for f in font_manager.win32InstalledFonts(): try: - system_fonts.append(FT2Font(str(f)).family_name) + system_fonts.append(font_manager.get_font(str(f)).family_name) except: pass # unknown error, skip this font else: diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 3f1145a5b14e..bb6f83afc667 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -28,8 +28,8 @@ def _fn_name(): return sys._getframe(1).f_code.co_name is_writable_file_like, maxdict, file_requires_unicode from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, is_opentype_cff_font -from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING +from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font +from matplotlib.ft2font import KERNING_DEFAULT, LOAD_NO_HINTING from matplotlib.ttconv import convert_ttf_to_ps from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 @@ -199,7 +199,6 @@ class RendererPS(RendererBase): context instance that controls the colors/styles. """ - fontd = maxdict(50) afmfontd = maxdict(50) def __init__(self, width, height, pswriter, imagedpi=72): @@ -393,15 +392,8 @@ def _get_font_afm(self, prop): return font def _get_font_ttf(self, prop): - key = hash(prop) - font = self.fontd.get(key) - if font is None: - fname = findfont(prop) - font = self.fontd.get(fname) - if font is None: - font = FT2Font(fname) - self.fontd[fname] = font - self.fontd[key] = font + fname = findfont(prop) + font = get_font(fname) font.clear() size = prop.get_size_in_points() font.set_size(size, 72.0) @@ -1145,7 +1137,7 @@ def print_figure_impl(): if not rcParams['ps.useafm']: for font_filename, chars in six.itervalues(ps_renderer.used_characters): if len(chars): - font = FT2Font(font_filename) + font = get_font(font_filename) cmap = font.get_charmap() glyph_ids = [] for c in chars: diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 4e137936b96d..9c59c8bbbfee 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -19,8 +19,8 @@ from matplotlib.cbook import is_string_like, is_writable_file_like, maxdict from matplotlib.colors import rgb2hex from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, FontProperties -from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING +from matplotlib.font_manager import findfont, FontProperties, get_font +from matplotlib.ft2font import KERNING_DEFAULT, LOAD_NO_HINTING from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib import _path @@ -326,15 +326,8 @@ def _make_flip_transform(self, transform): .translate(0.0, self.height)) def _get_font(self, prop): - key = hash(prop) - font = self.fontd.get(key) - if font is None: - fname = findfont(prop) - font = self.fontd.get(fname) - if font is None: - font = FT2Font(fname) - self.fontd[fname] = font - self.fontd[key] = font + fname = findfont(prop) + font = get_font(fname) font.clear() size = prop.get_size_in_points() font.set_size(size, 72.0) @@ -495,7 +488,7 @@ def _write_svgfonts(self): writer = self.writer writer.start('defs') for font_fname, chars in six.iteritems(self._fonts): - font = FT2Font(font_fname) + font = get_font(font_fname) font.set_size(72, 72) sfnt = font.get_sfnt() writer.start('font', id=sfnt[(1, 0, 0, 4)]) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 7bba3b8ae3ae..1dfae5e10c97 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -63,6 +63,12 @@ from matplotlib.fontconfig_pattern import \ parse_fontconfig_pattern, generate_fontconfig_pattern +try: + from functools import lru_cache +except ImportError: + from functools32 import lru_cache + + USE_FONTCONFIG = False verbose = matplotlib.verbose @@ -733,7 +739,7 @@ def get_name(self): Return the name of the font that best matches the font properties. """ - return ft2font.FT2Font(findfont(self)).family_name + return get_font(findfont(self)).family_name def get_style(self): """ @@ -1336,7 +1342,6 @@ def findfont(self, prop, fontext='ttf', directory=None, _lookup_cache[fontext].set(prop, result) return result - _is_opentype_cff_font_cache = {} def is_opentype_cff_font(filename): """ @@ -1357,6 +1362,10 @@ def is_opentype_cff_font(filename): fontManager = None _fmcache = None + +get_font = lru_cache(64)(ft2font.FT2Font) + + # The experimental fontconfig-based backend. if USE_FONTCONFIG and sys.platform != 'win32': import re diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 90e45a3c4d75..7971366a0dd0 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -50,8 +50,8 @@ from matplotlib.afm import AFM from matplotlib.cbook import Bunch, get_realpath_and_stat, \ is_string_like, maxdict -from matplotlib.ft2font import FT2Font, FT2Image, KERNING_DEFAULT, LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING -from matplotlib.font_manager import findfont, FontProperties +from matplotlib.ft2font import FT2Image, KERNING_DEFAULT, LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING +from matplotlib.font_manager import findfont, FontProperties, get_font from matplotlib._mathtext_data import latex_to_bakoma, \ latex_to_standard, tex2uni, latex_to_cmex, stix_virtual_fonts from matplotlib import get_data_path, rcParams @@ -563,7 +563,7 @@ def __init__(self, default_font_prop, mathtext_backend): self._fonts = {} filename = findfont(default_font_prop) - default_font = self.CachedFont(FT2Font(filename)) + default_font = self.CachedFont(get_font(filename)) self._fonts['default'] = default_font self._fonts['regular'] = default_font @@ -576,10 +576,9 @@ def _get_font(self, font): basename = self.fontmap[font] else: basename = font - cached_font = self._fonts.get(basename) if cached_font is None and os.path.exists(basename): - font = FT2Font(basename) + font = get_font(basename) cached_font = self.CachedFont(font) self._fonts[basename] = cached_font self._fonts[font.postscript_name] = cached_font diff --git a/lib/matplotlib/tests/__init__.py b/lib/matplotlib/tests/__init__.py index 20a4ae3c6087..9b06bd1cbc91 100644 --- a/lib/matplotlib/tests/__init__.py +++ b/lib/matplotlib/tests/__init__.py @@ -49,12 +49,6 @@ def setup(): rcParams['text.hinting'] = False rcParams['text.hinting_factor'] = 8 - # Clear the font caches. Otherwise, the hinting mode can travel - # from one test to another. - backend_agg.RendererAgg._fontd.clear() - backend_pdf.RendererPdf.truetype_font_cache.clear() - backend_svg.RendererSVG.fontd.clear() - def assert_str_equal(reference_str, test_str, format_str=('String {str1} and {str2} do not ' diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index d46dd27898b1..e0f7fbe8d400 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -13,11 +13,11 @@ from matplotlib.path import Path from matplotlib import rcParams import matplotlib.font_manager as font_manager -from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING +from matplotlib.ft2font import KERNING_DEFAULT, LOAD_NO_HINTING from matplotlib.ft2font import LOAD_TARGET_LIGHT from matplotlib.mathtext import MathTextParser import matplotlib.dviread as dviread -from matplotlib.font_manager import FontProperties +from matplotlib.font_manager import FontProperties, get_font from matplotlib.transforms import Affine2D from matplotlib.externals.six.moves.urllib.parse import quote as urllib_quote @@ -54,7 +54,7 @@ def _get_font(self, prop): find a ttf font. """ fname = font_manager.findfont(prop) - font = FT2Font(fname) + font = get_font(fname) font.set_size(self.FONT_SCALE, self.DPI) return font @@ -334,7 +334,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None, font_bunch = self.tex_font_map[dvifont.texname] if font_and_encoding is None: - font = FT2Font(font_bunch.filename) + font = get_font(font_bunch.filename) for charmap_name, charmap_code in [("ADOBE_CUSTOM", 1094992451), diff --git a/lib/mpl_toolkits/tests/__init__.py b/lib/mpl_toolkits/tests/__init__.py index 20a4ae3c6087..9b06bd1cbc91 100644 --- a/lib/mpl_toolkits/tests/__init__.py +++ b/lib/mpl_toolkits/tests/__init__.py @@ -49,12 +49,6 @@ def setup(): rcParams['text.hinting'] = False rcParams['text.hinting_factor'] = 8 - # Clear the font caches. Otherwise, the hinting mode can travel - # from one test to another. - backend_agg.RendererAgg._fontd.clear() - backend_pdf.RendererPdf.truetype_font_cache.clear() - backend_svg.RendererSVG.fontd.clear() - def assert_str_equal(reference_str, test_str, format_str=('String {str1} and {str2} do not ' diff --git a/setup.py b/setup.py index f3ecd708f54f..b3fb0413699e 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ 'Required dependencies and extensions', setupext.Numpy(), setupext.Dateutil(), + setupext.FuncTools32(), setupext.Pytz(), setupext.Cycler(), setupext.Tornado(), diff --git a/setupext.py b/setupext.py index 0a9660b6f7c8..6ef9480c943d 100755 --- a/setupext.py +++ b/setupext.py @@ -1221,6 +1221,29 @@ def get_install_requires(self): return [dateutil] +class FuncTools32(SetupPackage): + name = "functools32" + + def check(self): + if sys.version_info[:2] < (3, 2): + try: + import functools32 + except ImportError: + return ( + "functools32 was not found. It is required for for" + "python versions prior to 3.2") + + return "using functools32 version %s" % functools32.__version__ + else: + return "Not required" + + def get_install_requires(self): + if sys.version_info[:2] < (3, 2): + return ['functools32'] + else: + return [] + + class Tornado(OptionalPackage): name = "tornado" From 082a3a5890ba37b10f191ba8aba25a8aed723677 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 21 Oct 2015 14:38:56 -0400 Subject: [PATCH 2/4] Add INSTALL note about functools32 --- INSTALL | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL b/INSTALL index a81ae7e1a104..02e5946e89d5 100644 --- a/INSTALL +++ b/INSTALL @@ -214,6 +214,10 @@ libpng 1.2 (or later) ``cycler`` 0.9 or later Composable cycle class used for constructing style-cycles +`functools32` + Required for compatibility if running on versions of Python before + Python 3.2. + Optional GUI framework ^^^^^^^^^^^^^^^^^^^^^^ From 5e93dfc265e041aa6855f2eb63946dfc0df4ffad Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 21 Oct 2015 15:54:07 -0400 Subject: [PATCH 3/4] functools32 has no version --- setupext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setupext.py b/setupext.py index 6ef9480c943d..1c70adde064e 100755 --- a/setupext.py +++ b/setupext.py @@ -1233,7 +1233,7 @@ def check(self): "functools32 was not found. It is required for for" "python versions prior to 3.2") - return "using functools32 version %s" % functools32.__version__ + return "using functools32" else: return "Not required" From 2d56ffeb5731f87f9f7ae8a1282bb352aadf1d92 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 27 Oct 2015 14:11:15 -0400 Subject: [PATCH 4/4] Don't cache the charmap and inverse charmap mathtext creates Python dictionaries for the charmap and inverse charmap for each font. This turns out to be unnecessary: 1) freetype has an API to do a charmap lookup that is faster than a Python dictionary 2) The inverse charmap isn't really necessary if we convert the latex_to_bakoma to use unicode character points rather than glyph indices. This should have a large impact when #5241 is merged with larger fonts. --- lib/matplotlib/_mathtext_data.py | 466 ++++++++++++------------- lib/matplotlib/backends/backend_agg.py | 8 +- lib/matplotlib/backends/backend_pdf.py | 12 +- lib/matplotlib/mathtext.py | 115 +++--- src/ft2font_wrapper.cpp | 22 ++ 5 files changed, 312 insertions(+), 311 deletions(-) diff --git a/lib/matplotlib/_mathtext_data.py b/lib/matplotlib/_mathtext_data.py index 438686005f16..a8379e30c9b3 100644 --- a/lib/matplotlib/_mathtext_data.py +++ b/lib/matplotlib/_mathtext_data.py @@ -8,250 +8,238 @@ from matplotlib.externals import six -""" -from matplotlib.ft2font import FT2Font -font = FT2Font('/usr/local/share/matplotlib/cmr10.ttf') -items = font.get_charmap().items() -items.sort() - -for charcode, glyphind in items: - print charcode, glyphind -""" - latex_to_bakoma = { - r'\oint' : ('cmex10', 45), - r'\bigodot' : ('cmex10', 50), - r'\bigoplus' : ('cmex10', 55), - r'\bigotimes' : ('cmex10', 59), - r'\sum' : ('cmex10', 51), - r'\prod' : ('cmex10', 24), - r'\int' : ('cmex10', 56), - r'\bigcup' : ('cmex10', 28), - r'\bigcap' : ('cmex10', 60), - r'\biguplus' : ('cmex10', 32), - r'\bigwedge' : ('cmex10', 4), - r'\bigvee' : ('cmex10', 37), - r'\coprod' : ('cmex10', 42), - r'\__sqrt__' : ('cmex10', 48), - r'\leftbrace' : ('cmex10', 92), - r'{' : ('cmex10', 92), - r'\{' : ('cmex10', 92), - r'\rightbrace' : ('cmex10', 130), - r'}' : ('cmex10', 130), - r'\}' : ('cmex10', 130), - r'\leftangle' : ('cmex10', 97), - r'\rightangle' : ('cmex10', 64), - r'\langle' : ('cmex10', 97), - r'\rangle' : ('cmex10', 64), - r'\widehat' : ('cmex10', 15), - r'\widetilde' : ('cmex10', 52), - r'\widebar' : ('cmr10', 131), + '\\__sqrt__' : ('cmex10', 0x70), + '\\bigcap' : ('cmex10', 0x5c), + '\\bigcup' : ('cmex10', 0x5b), + '\\bigodot' : ('cmex10', 0x4b), + '\\bigoplus' : ('cmex10', 0x4d), + '\\bigotimes' : ('cmex10', 0x4f), + '\\biguplus' : ('cmex10', 0x5d), + '\\bigvee' : ('cmex10', 0x5f), + '\\bigwedge' : ('cmex10', 0x5e), + '\\coprod' : ('cmex10', 0x61), + '\\int' : ('cmex10', 0x5a), + '\\langle' : ('cmex10', 0xad), + '\\leftangle' : ('cmex10', 0xad), + '\\leftbrace' : ('cmex10', 0xa9), + '\\oint' : ('cmex10', 0x49), + '\\prod' : ('cmex10', 0x59), + '\\rangle' : ('cmex10', 0xae), + '\\rightangle' : ('cmex10', 0xae), + '\\rightbrace' : ('cmex10', 0xaa), + '\\sum' : ('cmex10', 0x58), + '\\widehat' : ('cmex10', 0x62), + '\\widetilde' : ('cmex10', 0x65), + '\\{' : ('cmex10', 0xa9), + '\\}' : ('cmex10', 0xaa), + '{' : ('cmex10', 0xa9), + '}' : ('cmex10', 0xaa), - r'\omega' : ('cmmi10', 29), - r'\varepsilon' : ('cmmi10', 20), - r'\vartheta' : ('cmmi10', 22), - r'\varrho' : ('cmmi10', 61), - r'\varsigma' : ('cmmi10', 41), - r'\varphi' : ('cmmi10', 6), - r'\leftharpoonup' : ('cmmi10', 108), - r'\leftharpoondown' : ('cmmi10', 68), - r'\rightharpoonup' : ('cmmi10', 117), - r'\rightharpoondown' : ('cmmi10', 77), - r'\triangleright' : ('cmmi10', 130), - r'\triangleleft' : ('cmmi10', 89), - r'.' : ('cmmi10', 51), - r',' : ('cmmi10', 44), - r'<' : ('cmmi10', 99), - r'/' : ('cmmi10', 98), - r'>' : ('cmmi10', 107), - r'\flat' : ('cmmi10', 131), - r'\natural' : ('cmmi10', 90), - r'\sharp' : ('cmmi10', 50), - r'\smile' : ('cmmi10', 97), - r'\frown' : ('cmmi10', 58), - r'\ell' : ('cmmi10', 102), - r'\imath' : ('cmmi10', 8), - r'\jmath' : ('cmmi10', 65), - r'\wp' : ('cmmi10', 14), - r'\alpha' : ('cmmi10', 13), - r'\beta' : ('cmmi10', 35), - r'\gamma' : ('cmmi10', 24), - r'\delta' : ('cmmi10', 38), - r'\epsilon' : ('cmmi10', 54), - r'\zeta' : ('cmmi10', 10), - r'\eta' : ('cmmi10', 5), - r'\theta' : ('cmmi10', 18), - r'\iota' : ('cmmi10', 28), - r'\lambda' : ('cmmi10', 9), - r'\mu' : ('cmmi10', 32), - r'\nu' : ('cmmi10', 34), - r'\xi' : ('cmmi10', 7), - r'\pi' : ('cmmi10', 36), - r'\kappa' : ('cmmi10', 30), - r'\rho' : ('cmmi10', 39), - r'\sigma' : ('cmmi10', 21), - r'\tau' : ('cmmi10', 43), - '\\upsilon' : ('cmmi10', 25), - r'\phi' : ('cmmi10', 42), - r'\chi' : ('cmmi10', 17), - r'\psi' : ('cmmi10', 31), - r'|' : ('cmsy10', 47), - r'\|' : ('cmsy10', 44), - r'(' : ('cmr10', 119), - r'\leftparen' : ('cmr10', 119), - r'\rightparen' : ('cmr10', 68), - r')' : ('cmr10', 68), - r'+' : ('cmr10', 76), - r'0' : ('cmr10', 40), - r'1' : ('cmr10', 100), - r'2' : ('cmr10', 49), - r'3' : ('cmr10', 110), - r'4' : ('cmr10', 59), - r'5' : ('cmr10', 120), - r'6' : ('cmr10', 69), - r'7' : ('cmr10', 127), - r'8' : ('cmr10', 77), - r'9' : ('cmr10', 22), - r':' : ('cmr10', 85), - r';' : ('cmr10', 31), - r'=' : ('cmr10', 41), - r'\leftbracket' : ('cmr10', 62), - r'[' : ('cmr10', 62), - r'\rightbracket' : ('cmr10', 72), - r']' : ('cmr10', 72), - r'\%' : ('cmr10', 48), - r'%' : ('cmr10', 48), - r'\$' : ('cmr10', 99), - r'@' : ('cmr10', 111), - r'\#' : ('cmr10', 39), - r'\_' : ('cmtt10', 79), - r'\Gamma' : ('cmr10', 19), - r'\Delta' : ('cmr10', 6), - r'\Theta' : ('cmr10', 7), - r'\Lambda' : ('cmr10', 14), - r'\Xi' : ('cmr10', 3), - r'\Pi' : ('cmr10', 17), - r'\Sigma' : ('cmr10', 10), - '\\Upsilon' : ('cmr10', 11), - r'\Phi' : ('cmr10', 9), - r'\Psi' : ('cmr10', 15), - r'\Omega' : ('cmr10', 12), + ',' : ('cmmi10', 0x3b), + '.' : ('cmmi10', 0x3a), + '/' : ('cmmi10', 0x3d), + '<' : ('cmmi10', 0x3c), + '>' : ('cmmi10', 0x3e), + '\\alpha' : ('cmmi10', 0xae), + '\\beta' : ('cmmi10', 0xaf), + '\\chi' : ('cmmi10', 0xc2), + '\\combiningrightarrowabove' : ('cmmi10', 0x7e), + '\\delta' : ('cmmi10', 0xb1), + '\\ell' : ('cmmi10', 0x60), + '\\epsilon' : ('cmmi10', 0xb2), + '\\eta' : ('cmmi10', 0xb4), + '\\flat' : ('cmmi10', 0x5b), + '\\frown' : ('cmmi10', 0x5f), + '\\gamma' : ('cmmi10', 0xb0), + '\\imath' : ('cmmi10', 0x7b), + '\\iota' : ('cmmi10', 0xb6), + '\\jmath' : ('cmmi10', 0x7c), + '\\kappa' : ('cmmi10', 0x2219), + '\\lambda' : ('cmmi10', 0xb8), + '\\leftharpoondown' : ('cmmi10', 0x29), + '\\leftharpoonup' : ('cmmi10', 0x28), + '\\mu' : ('cmmi10', 0xb9), + '\\natural' : ('cmmi10', 0x5c), + '\\nu' : ('cmmi10', 0xba), + '\\omega' : ('cmmi10', 0x21), + '\\phi' : ('cmmi10', 0xc1), + '\\pi' : ('cmmi10', 0xbc), + '\\psi' : ('cmmi10', 0xc3), + '\\rho' : ('cmmi10', 0xbd), + '\\rightharpoondown' : ('cmmi10', 0x2b), + '\\rightharpoonup' : ('cmmi10', 0x2a), + '\\sharp' : ('cmmi10', 0x5d), + '\\sigma' : ('cmmi10', 0xbe), + '\\smile' : ('cmmi10', 0x5e), + '\\tau' : ('cmmi10', 0xbf), + '\\theta' : ('cmmi10', 0xb5), + '\\triangleleft' : ('cmmi10', 0x2f), + '\\triangleright' : ('cmmi10', 0x2e), + '\\upsilon' : ('cmmi10', 0xc0), + '\\varepsilon' : ('cmmi10', 0x22), + '\\varphi' : ('cmmi10', 0x27), + '\\varrho' : ('cmmi10', 0x25), + '\\varsigma' : ('cmmi10', 0x26), + '\\vartheta' : ('cmmi10', 0x23), + '\\wp' : ('cmmi10', 0x7d), + '\\xi' : ('cmmi10', 0xbb), + '\\zeta' : ('cmmi10', 0xb3), - r'\prime' : ('cmsy10', 73), + '!' : ('cmr10', 0x21), + '%' : ('cmr10', 0x25), + '&' : ('cmr10', 0x26), + '(' : ('cmr10', 0x28), + ')' : ('cmr10', 0x29), + '+' : ('cmr10', 0x2b), + '0' : ('cmr10', 0x30), + '1' : ('cmr10', 0x31), + '2' : ('cmr10', 0x32), + '3' : ('cmr10', 0x33), + '4' : ('cmr10', 0x34), + '5' : ('cmr10', 0x35), + '6' : ('cmr10', 0x36), + '7' : ('cmr10', 0x37), + '8' : ('cmr10', 0x38), + '9' : ('cmr10', 0x39), + ':' : ('cmr10', 0x3a), + ';' : ('cmr10', 0x3b), + '=' : ('cmr10', 0x3d), + '?' : ('cmr10', 0x3f), + '@' : ('cmr10', 0x40), + '[' : ('cmr10', 0x5b), + '\\#' : ('cmr10', 0x23), + '\\$' : ('cmr10', 0x24), + '\\%' : ('cmr10', 0x25), + '\\Delta' : ('cmr10', 0xa2), + '\\Gamma' : ('cmr10', 0xa1), + '\\Lambda' : ('cmr10', 0xa4), + '\\Omega' : ('cmr10', 0xad), + '\\Phi' : ('cmr10', 0xa9), + '\\Pi' : ('cmr10', 0xa6), + '\\Psi' : ('cmr10', 0xaa), + '\\Sigma' : ('cmr10', 0xa7), + '\\Theta' : ('cmr10', 0xa3), + '\\Upsilon' : ('cmr10', 0xa8), + '\\Xi' : ('cmr10', 0xa5), + '\\circumflexaccent' : ('cmr10', 0x5e), + '\\combiningacuteaccent' : ('cmr10', 0xb6), + '\\combiningbreve' : ('cmr10', 0xb8), + '\\combiningdiaeresis' : ('cmr10', 0xc4), + '\\combiningdotabove' : ('cmr10', 0x5f), + '\\combininggraveaccent' : ('cmr10', 0xb5), + '\\combiningoverline' : ('cmr10', 0xb9), + '\\combiningtilde' : ('cmr10', 0x7e), + '\\leftbracket' : ('cmr10', 0x5b), + '\\leftparen' : ('cmr10', 0x28), + '\\rightbracket' : ('cmr10', 0x5d), + '\\rightparen' : ('cmr10', 0x29), + '\\widebar' : ('cmr10', 0xb9), + ']' : ('cmr10', 0x5d), - # these are mathml names, I think. I'm just using them for the - # tex methods noted - r'\circumflexaccent' : ('cmr10', 124), # for \hat - r'\combiningbreve' : ('cmr10', 81), # for \breve - r'\combiningoverline' : ('cmr10', 131), # for \bar - r'\combininggraveaccent' : ('cmr10', 114), # for \grave - r'\combiningacuteaccent' : ('cmr10', 63), # for \accute - r'\combiningdiaeresis' : ('cmr10', 91), # for \ddot - r'\combiningtilde' : ('cmr10', 75), # for \tilde - r'\combiningrightarrowabove' : ('cmmi10', 110), # for \vec - r'\combiningdotabove' : ('cmr10', 26), # for \dot + '*' : ('cmsy10', 0xa4), + '-' : ('cmsy10', 0xa1), + '\\Downarrow' : ('cmsy10', 0x2b), + '\\Im' : ('cmsy10', 0x3d), + '\\Leftarrow' : ('cmsy10', 0x28), + '\\Leftrightarrow' : ('cmsy10', 0x2c), + '\\P' : ('cmsy10', 0x7b), + '\\Re' : ('cmsy10', 0x3c), + '\\Rightarrow' : ('cmsy10', 0x29), + '\\S' : ('cmsy10', 0x78), + '\\Uparrow' : ('cmsy10', 0x2a), + '\\Updownarrow' : ('cmsy10', 0x6d), + '\\Vert' : ('cmsy10', 0x6b), + '\\aleph' : ('cmsy10', 0x40), + '\\approx' : ('cmsy10', 0xbc), + '\\ast' : ('cmsy10', 0xa4), + '\\asymp' : ('cmsy10', 0xb3), + '\\backslash' : ('cmsy10', 0x6e), + '\\bigcirc' : ('cmsy10', 0xb0), + '\\bigtriangledown' : ('cmsy10', 0x35), + '\\bigtriangleup' : ('cmsy10', 0x34), + '\\bot' : ('cmsy10', 0x3f), + '\\bullet' : ('cmsy10', 0xb2), + '\\cap' : ('cmsy10', 0x5c), + '\\cdot' : ('cmsy10', 0xa2), + '\\circ' : ('cmsy10', 0xb1), + '\\clubsuit' : ('cmsy10', 0x7c), + '\\cup' : ('cmsy10', 0x5b), + '\\dag' : ('cmsy10', 0x79), + '\\dashv' : ('cmsy10', 0x61), + '\\ddag' : ('cmsy10', 0x7a), + '\\diamond' : ('cmsy10', 0xa6), + '\\diamondsuit' : ('cmsy10', 0x7d), + '\\div' : ('cmsy10', 0xa5), + '\\downarrow' : ('cmsy10', 0x23), + '\\emptyset' : ('cmsy10', 0x3b), + '\\equiv' : ('cmsy10', 0xb4), + '\\exists' : ('cmsy10', 0x39), + '\\forall' : ('cmsy10', 0x38), + '\\geq' : ('cmsy10', 0xb8), + '\\gg' : ('cmsy10', 0xc0), + '\\heartsuit' : ('cmsy10', 0x7e), + '\\in' : ('cmsy10', 0x32), + '\\infty' : ('cmsy10', 0x31), + '\\lbrace' : ('cmsy10', 0x66), + '\\lceil' : ('cmsy10', 0x64), + '\\leftarrow' : ('cmsy10', 0xc3), + '\\leftrightarrow' : ('cmsy10', 0x24), + '\\leq' : ('cmsy10', 0x2219), + '\\lfloor' : ('cmsy10', 0x62), + '\\ll' : ('cmsy10', 0xbf), + '\\mid' : ('cmsy10', 0x6a), + '\\mp' : ('cmsy10', 0xa8), + '\\nabla' : ('cmsy10', 0x72), + '\\nearrow' : ('cmsy10', 0x25), + '\\neg' : ('cmsy10', 0x3a), + '\\ni' : ('cmsy10', 0x33), + '\\nwarrow' : ('cmsy10', 0x2d), + '\\odot' : ('cmsy10', 0xaf), + '\\ominus' : ('cmsy10', 0xaa), + '\\oplus' : ('cmsy10', 0xa9), + '\\oslash' : ('cmsy10', 0xae), + '\\otimes' : ('cmsy10', 0xad), + '\\pm' : ('cmsy10', 0xa7), + '\\prec' : ('cmsy10', 0xc1), + '\\preceq' : ('cmsy10', 0xb9), + '\\prime' : ('cmsy10', 0x30), + '\\propto' : ('cmsy10', 0x2f), + '\\rbrace' : ('cmsy10', 0x67), + '\\rceil' : ('cmsy10', 0x65), + '\\rfloor' : ('cmsy10', 0x63), + '\\rightarrow' : ('cmsy10', 0x21), + '\\searrow' : ('cmsy10', 0x26), + '\\sim' : ('cmsy10', 0xbb), + '\\simeq' : ('cmsy10', 0x27), + '\\slash' : ('cmsy10', 0x36), + '\\spadesuit' : ('cmsy10', 0xc4), + '\\sqcap' : ('cmsy10', 0x75), + '\\sqcup' : ('cmsy10', 0x74), + '\\sqsubseteq' : ('cmsy10', 0x76), + '\\sqsupseteq' : ('cmsy10', 0x77), + '\\subset' : ('cmsy10', 0xbd), + '\\subseteq' : ('cmsy10', 0xb5), + '\\succ' : ('cmsy10', 0xc2), + '\\succeq' : ('cmsy10', 0xba), + '\\supset' : ('cmsy10', 0xbe), + '\\supseteq' : ('cmsy10', 0xb6), + '\\swarrow' : ('cmsy10', 0x2e), + '\\times' : ('cmsy10', 0xa3), + '\\to' : ('cmsy10', 0x21), + '\\top' : ('cmsy10', 0x3e), + '\\uparrow' : ('cmsy10', 0x22), + '\\updownarrow' : ('cmsy10', 0x6c), + '\\uplus' : ('cmsy10', 0x5d), + '\\vdash' : ('cmsy10', 0x60), + '\\vee' : ('cmsy10', 0x5f), + '\\vert' : ('cmsy10', 0x6a), + '\\wedge' : ('cmsy10', 0x5e), + '\\wr' : ('cmsy10', 0x6f), + '\\|' : ('cmsy10', 0x6b), + '|' : ('cmsy10', 0x6a), - r'\leftarrow' : ('cmsy10', 10), - '\\uparrow' : ('cmsy10', 25), - r'\downarrow' : ('cmsy10', 28), - r'\leftrightarrow' : ('cmsy10', 24), - r'\nearrow' : ('cmsy10', 99), - r'\searrow' : ('cmsy10', 57), - r'\simeq' : ('cmsy10', 108), - r'\Leftarrow' : ('cmsy10', 104), - r'\Rightarrow' : ('cmsy10', 112), - '\\Uparrow' : ('cmsy10', 60), - r'\Downarrow' : ('cmsy10', 68), - r'\Leftrightarrow' : ('cmsy10', 51), - r'\nwarrow' : ('cmsy10', 65), - r'\swarrow' : ('cmsy10', 116), - r'\propto' : ('cmsy10', 15), - r'\infty' : ('cmsy10', 32), - r'\in' : ('cmsy10', 59), - r'\ni' : ('cmsy10', 122), - r'\bigtriangleup' : ('cmsy10', 80), - r'\bigtriangledown' : ('cmsy10', 132), - r'\slash' : ('cmsy10', 87), - r'\forall' : ('cmsy10', 21), - r'\exists' : ('cmsy10', 5), - r'\neg' : ('cmsy10', 20), - r'\emptyset' : ('cmsy10', 33), - r'\Re' : ('cmsy10', 95), - r'\Im' : ('cmsy10', 52), - r'\top' : ('cmsy10', 100), - r'\bot' : ('cmsy10', 11), - r'\aleph' : ('cmsy10', 26), - r'\cup' : ('cmsy10', 6), - r'\cap' : ('cmsy10', 19), - '\\uplus' : ('cmsy10', 58), - r'\wedge' : ('cmsy10', 43), - r'\vee' : ('cmsy10', 96), - r'\vdash' : ('cmsy10', 109), - r'\dashv' : ('cmsy10', 66), - r'\lfloor' : ('cmsy10', 117), - r'\rfloor' : ('cmsy10', 74), - r'\lceil' : ('cmsy10', 123), - r'\rceil' : ('cmsy10', 81), - r'\lbrace' : ('cmsy10', 92), - r'\rbrace' : ('cmsy10', 105), - r'\mid' : ('cmsy10', 47), - r'\vert' : ('cmsy10', 47), - r'\Vert' : ('cmsy10', 44), - '\\updownarrow' : ('cmsy10', 94), - '\\Updownarrow' : ('cmsy10', 53), - r'\backslash' : ('cmsy10', 126), - r'\wr' : ('cmsy10', 101), - r'\nabla' : ('cmsy10', 110), - r'\sqcup' : ('cmsy10', 67), - r'\sqcap' : ('cmsy10', 118), - r'\sqsubseteq' : ('cmsy10', 75), - r'\sqsupseteq' : ('cmsy10', 124), - r'\S' : ('cmsy10', 129), - r'\dag' : ('cmsy10', 71), - r'\ddag' : ('cmsy10', 127), - r'\P' : ('cmsy10', 130), - r'\clubsuit' : ('cmsy10', 18), - r'\diamondsuit' : ('cmsy10', 34), - r'\heartsuit' : ('cmsy10', 22), - r'-' : ('cmsy10', 17), - r'\cdot' : ('cmsy10', 78), - r'\times' : ('cmsy10', 13), - r'*' : ('cmsy10', 9), - r'\ast' : ('cmsy10', 9), - r'\div' : ('cmsy10', 31), - r'\diamond' : ('cmsy10', 48), - r'\pm' : ('cmsy10', 8), - r'\mp' : ('cmsy10', 98), - r'\oplus' : ('cmsy10', 16), - r'\ominus' : ('cmsy10', 56), - r'\otimes' : ('cmsy10', 30), - r'\oslash' : ('cmsy10', 107), - r'\odot' : ('cmsy10', 64), - r'\bigcirc' : ('cmsy10', 115), - r'\circ' : ('cmsy10', 72), - r'\bullet' : ('cmsy10', 84), - r'\asymp' : ('cmsy10', 121), - r'\equiv' : ('cmsy10', 35), - r'\subseteq' : ('cmsy10', 103), - r'\supseteq' : ('cmsy10', 42), - r'\leq' : ('cmsy10', 14), - r'\geq' : ('cmsy10', 29), - r'\preceq' : ('cmsy10', 79), - r'\succeq' : ('cmsy10', 131), - r'\sim' : ('cmsy10', 27), - r'\approx' : ('cmsy10', 23), - r'\subset' : ('cmsy10', 50), - r'\supset' : ('cmsy10', 86), - r'\ll' : ('cmsy10', 85), - r'\gg' : ('cmsy10', 40), - r'\prec' : ('cmsy10', 93), - r'\succ' : ('cmsy10', 49), - r'\rightarrow' : ('cmsy10', 12), - r'\to' : ('cmsy10', 12), - r'\spadesuit' : ('cmsy10', 7), - r'?' : ('cmr10', 50), - r'!' : ('cmr10', 29), - r'&' : ('cmr10', 109) + '\\_' : ('cmtt10', 0x5f) } latex_to_cmex = { diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 5c4c4d96c6c4..bde41f1f2a9a 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -28,13 +28,13 @@ import numpy as np from matplotlib import verbose, rcParams -from matplotlib.backend_bases import RendererBase,\ - FigureManagerBase, FigureCanvasBase +from matplotlib.backend_bases import (RendererBase, FigureManagerBase, + FigureCanvasBase) from matplotlib.cbook import is_string_like, maxdict, restrict_dict from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font -from matplotlib.ft2font import LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, \ - LOAD_DEFAULT, LOAD_NO_AUTOHINT +from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, + LOAD_DEFAULT, LOAD_NO_AUTOHINT) from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Bbox, BboxBase diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 0dad65eec996..acba9795ae2a 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -29,18 +29,18 @@ import matplotlib from matplotlib import __version__, rcParams from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ - FigureManagerBase, FigureCanvasBase +from matplotlib.backend_bases import (RendererBase, GraphicsContextBase, + FigureManagerBase, FigureCanvasBase) from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.cbook import Bunch, is_string_like, \ - get_realpath_and_stat, is_writable_file_like, maxdict +from matplotlib.cbook import (Bunch, is_string_like, get_realpath_and_stat, + is_writable_file_like, maxdict) from matplotlib.figure import Figure from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font from matplotlib.afm import AFM import matplotlib.type1font as type1font import matplotlib.dviread as dviread -from matplotlib.ft2font import FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, \ - LOAD_NO_HINTING, KERNING_UNFITTED +from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, + LOAD_NO_HINTING, KERNING_UNFITTED) from matplotlib.mathtext import MathTextParser from matplotlib.transforms import Affine2D, BboxBase from matplotlib.path import Path diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 7971366a0dd0..b9f15cf6a621 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -33,10 +33,10 @@ import numpy as np import pyparsing -from pyparsing import Combine, Group, Optional, Forward, \ - Literal, OneOrMore, ZeroOrMore, ParseException, Empty, \ - ParseResults, Suppress, oneOf, StringEnd, ParseFatalException, \ - FollowedBy, Regex, ParserElement, QuotedString, ParseBaseException +from pyparsing import (Combine, Group, Optional, Forward, + Literal, OneOrMore, ZeroOrMore, ParseException, Empty, + ParseResults, Suppress, oneOf, StringEnd, ParseFatalException, + FollowedBy, Regex, ParserElement, QuotedString, ParseBaseException) # Enable packrat parsing if (six.PY3 and @@ -48,12 +48,14 @@ ParserElement.enablePackrat() from matplotlib.afm import AFM -from matplotlib.cbook import Bunch, get_realpath_and_stat, \ - is_string_like, maxdict -from matplotlib.ft2font import FT2Image, KERNING_DEFAULT, LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING +from matplotlib.cbook import (Bunch, get_realpath_and_stat, is_string_like, + maxdict) +from matplotlib.ft2font import (FT2Image, KERNING_DEFAULT, LOAD_FORCE_AUTOHINT, + LOAD_NO_HINTING) from matplotlib.font_manager import findfont, FontProperties, get_font -from matplotlib._mathtext_data import latex_to_bakoma, \ - latex_to_standard, tex2uni, latex_to_cmex, stix_virtual_fonts +from matplotlib._mathtext_data import (latex_to_bakoma, latex_to_standard, + tex2uni, latex_to_cmex, + stix_virtual_fonts) from matplotlib import get_data_path, rcParams import matplotlib.colors as mcolors @@ -547,23 +549,13 @@ class TruetypeFonts(Fonts): A generic base class for all font setups that use Truetype fonts (through FT2Font). """ - class CachedFont: - def __init__(self, font): - self.font = font - self.charmap = font.get_charmap() - self.glyphmap = dict( - [(glyphind, ccode) for ccode, glyphind in six.iteritems(self.charmap)]) - - def __repr__(self): - return repr(self.font) - def __init__(self, default_font_prop, mathtext_backend): Fonts.__init__(self, default_font_prop, mathtext_backend) self.glyphd = {} self._fonts = {} filename = findfont(default_font_prop) - default_font = self.CachedFont(get_font(filename)) + default_font = get_font(filename) self._fonts['default'] = default_font self._fonts['regular'] = default_font @@ -578,15 +570,14 @@ def _get_font(self, font): basename = font cached_font = self._fonts.get(basename) if cached_font is None and os.path.exists(basename): - font = get_font(basename) - cached_font = self.CachedFont(font) + cached_font = get_font(basename) self._fonts[basename] = cached_font - self._fonts[font.postscript_name] = cached_font - self._fonts[font.postscript_name.lower()] = cached_font + self._fonts[cached_font.postscript_name] = cached_font + self._fonts[cached_font.postscript_name.lower()] = cached_font return cached_font - def _get_offset(self, cached_font, glyph, fontsize, dpi): - if cached_font.font.postscript_name == 'Cmex10': + def _get_offset(self, font, glyph, fontsize, dpi): + if font.postscript_name == 'Cmex10': return ((glyph.height/64.0/2.0) + (fontsize/3.0 * dpi/72.0)) return 0. @@ -596,17 +587,16 @@ def _get_info(self, fontname, font_class, sym, fontsize, dpi): if bunch is not None: return bunch - cached_font, num, symbol_name, fontsize, slanted = \ + font, num, symbol_name, fontsize, slanted = \ self._get_glyph(fontname, font_class, sym, fontsize) - font = cached_font.font font.set_size(fontsize, dpi) glyph = font.load_char( num, flags=self.mathtext_backend.get_hinting_type()) xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox] - offset = self._get_offset(cached_font, glyph, fontsize, dpi) + offset = self._get_offset(font, glyph, fontsize, dpi) metrics = Bunch( advance = glyph.linearHoriAdvance/65536.0, height = glyph.height/64.0, @@ -632,13 +622,13 @@ def _get_info(self, fontname, font_class, sym, fontsize, dpi): ) return result - def get_xheight(self, font, fontsize, dpi): - cached_font = self._get_font(font) - cached_font.font.set_size(fontsize, dpi) - pclt = cached_font.font.get_sfnt_table('pclt') + def get_xheight(self, fontname, fontsize, dpi): + font = self._get_font(fontname) + font.set_size(fontsize, dpi) + pclt = font.get_sfnt_table('pclt') if pclt is None: # Some fonts don't store the xHeight, so we do a poor man's xHeight - metrics = self.get_metrics(font, rcParams['mathtext.default'], 'x', fontsize, dpi) + metrics = self.get_metrics(fontname, rcParams['mathtext.default'], 'x', fontsize, dpi) return metrics.iceberg xHeight = (pclt['xHeight'] / 64.0) * (fontsize / 12.0) * (dpi / 100.0) return xHeight @@ -690,28 +680,27 @@ def __init__(self, *args, **kwargs): def _get_glyph(self, fontname, font_class, sym, fontsize): symbol_name = None + font = None if fontname in self.fontmap and sym in latex_to_bakoma: basename, num = latex_to_bakoma[sym] slanted = (basename == "cmmi10") or sym in self._slanted_symbols - cached_font = self._get_font(basename) - if cached_font is not None: - symbol_name = cached_font.font.get_glyph_name(num) - num = cached_font.glyphmap[num] + font = self._get_font(basename) elif len(sym) == 1: slanted = (fontname == "it") - cached_font = self._get_font(fontname) - if cached_font is not None: + font = self._get_font(fontname) + if font is not None: num = ord(sym) - gid = cached_font.charmap.get(num) - if gid is not None: - symbol_name = cached_font.font.get_glyph_name( - cached_font.charmap[num]) + + if font is not None: + gid = font.get_char_index(num) + if gid != 0: + symbol_name = font.get_glyph_name(gid) if symbol_name is None: return self._stix_fallback._get_glyph( fontname, font_class, sym, fontsize) - return cached_font, num, symbol_name, fontsize, slanted + return font, num, symbol_name, fontsize, slanted # The Bakoma fonts contain many pre-sized alternatives for the # delimiters. The AutoSizedChar class will use these alternatives @@ -842,10 +831,10 @@ def _get_glyph(self, fontname, font_class, sym, fontsize): slanted = (new_fontname == 'it') or sym in self._slanted_symbols found_symbol = False - cached_font = self._get_font(new_fontname) - if cached_font is not None: + font = self._get_font(new_fontname) + if font is not None: try: - glyphindex = cached_font.charmap[uniindex] + glyphindex = font.get_char_index(uniindex) found_symbol = True except KeyError: pass @@ -859,19 +848,21 @@ def _get_glyph(self, fontname, font_class, sym, fontsize): else: if fontname in ('it', 'regular') and isinstance(self, StixFonts): return self._get_glyph('rm', font_class, sym, fontsize) - warn("Font '%s' does not have a glyph for '%s' [U%x]" % - (new_fontname, sym.encode('ascii', 'backslashreplace'), uniindex), + warn("Font '%s' does not have a glyph for '%s' [U+%x]" % + (new_fontname, + sym.encode('ascii', 'backslashreplace').decode('ascii'), + uniindex), MathTextWarning) warn("Substituting with a dummy symbol.", MathTextWarning) fontname = 'rm' new_fontname = fontname - cached_font = self._get_font(fontname) + font = self._get_font(fontname) uniindex = 0xA4 # currency character, for lack of anything better - glyphindex = cached_font.charmap[uniindex] + glyphindex = font.get_char_index(uniindex) slanted = False - symbol_name = cached_font.font.get_glyph_name(glyphindex) - return cached_font, uniindex, symbol_name, fontsize, slanted + symbol_name = font.get_glyph_name(glyphindex) + return font, uniindex, symbol_name, fontsize, slanted def get_sized_alternatives_for_symbol(self, fontname, sym): if self.cm_fallback: @@ -982,9 +973,9 @@ def get_sized_alternatives_for_symbol(self, fontname, sym): uniindex = fix_ups.get(uniindex, uniindex) for i in range(6): - cached_font = self._get_font(i) - glyphindex = cached_font.charmap.get(uniindex) - if glyphindex is not None: + font = self._get_font(i) + glyphindex = font.get_char_index(uniindex) + if glyphindex != 0: alternatives.append((i, unichr_safe(uniindex))) # The largest size of the radical symbol in STIX has incorrect @@ -1144,12 +1135,13 @@ def get_kern(self, font1, fontclass1, sym1, fontsize1, font2, fontclass2, sym2, fontsize2, dpi) def get_xheight(self, font, fontsize, dpi): - cached_font = self._get_font(font) - return cached_font.get_xheight() * 0.001 * fontsize + font = self._get_font(font) + return font.get_xheight() * 0.001 * fontsize def get_underline_thickness(self, font, fontsize, dpi): - cached_font = self._get_font(font) - return cached_font.get_underline_thickness() * 0.001 * fontsize + font = self._get_font(font) + return font.get_underline_thickness() * 0.001 * fontsize + ############################################################################## # TeX-LIKE BOX MODEL @@ -1318,7 +1310,6 @@ def __init__(self, c, state): Node.__init__(self) self.c = c self.font_output = state.font_output - assert isinstance(state.font, (six.string_types, int)) self.font = state.font self.font_class = state.font_class self.fontsize = state.fontsize diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index c2fd95798959..92e21bb593bf 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -954,6 +954,27 @@ static PyObject *PyFT2Font_get_charmap(PyFT2Font *self, PyObject *args, PyObject return charmap; } + +const char *PyFT2Font_get_char_index__doc__ = + "get_char_index()\n" + "\n" + "Given a character code, returns a glyph index.\n"; + +static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args, PyObject *kwds) +{ + FT_UInt index; + FT_ULong ccode; + + if (!PyArg_ParseTuple(args, "I:get_char_index", &ccode)) { + return NULL; + } + + index = FT_Get_Char_Index(self->x->get_face(), ccode); + + return PyLong_FromLong(index); +} + + const char *PyFT2Font_get_sfnt__doc__ = "get_sfnt(name)\n" "\n" @@ -1602,6 +1623,7 @@ static PyTypeObject *PyFT2Font_init_type(PyObject *m, PyTypeObject *type) {"draw_glyph_to_bitmap", (PyCFunction)PyFT2Font_draw_glyph_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyph_to_bitmap__doc__}, {"get_glyph_name", (PyCFunction)PyFT2Font_get_glyph_name, METH_VARARGS, PyFT2Font_get_glyph_name__doc__}, {"get_charmap", (PyCFunction)PyFT2Font_get_charmap, METH_NOARGS, PyFT2Font_get_charmap__doc__}, + {"get_char_index", (PyCFunction)PyFT2Font_get_char_index, METH_VARARGS, PyFT2Font_get_char_index__doc__}, {"get_sfnt", (PyCFunction)PyFT2Font_get_sfnt, METH_NOARGS, PyFT2Font_get_sfnt__doc__}, {"get_name_index", (PyCFunction)PyFT2Font_get_name_index, METH_VARARGS, PyFT2Font_get_name_index__doc__}, {"get_ps_font_info", (PyCFunction)PyFT2Font_get_ps_font_info, METH_NOARGS, PyFT2Font_get_ps_font_info__doc__},