Skip to content

Latest commit

 

History

History
3500 lines (2962 loc) · 118 KB

mathtext.py

File metadata and controls

3500 lines (2962 loc) · 118 KB
 
Sep 24, 2004
Sep 24, 2004
1
r"""
Oct 24, 2008
Oct 24, 2008
2
:mod:`~matplotlib.mathtext` is a module for parsing a subset of the
3
TeX math syntax and drawing them to a matplotlib backend.
Sep 24, 2004
Sep 24, 2004
4
May 29, 2018
May 29, 2018
5
For a tutorial of its usage see :doc:`/tutorials/text/mathtext`. This
Oct 24, 2008
Oct 24, 2008
6
document is primarily concerned with implementation details.
Sep 24, 2004
Sep 24, 2004
7
Oct 24, 2008
Oct 24, 2008
8
The module uses pyparsing_ to parse the TeX expression.
Sep 24, 2004
Sep 24, 2004
9
Oct 24, 2008
Oct 24, 2008
10
.. _pyparsing: http://pyparsing.wikispaces.com/
Jun 7, 2007
Jun 7, 2007
11
Oct 24, 2008
Oct 24, 2008
12
The Bakoma distribution of the TeX Computer Modern fonts, and STIX
13
fonts are supported. There is experimental support for using
14
arbitrary fonts, but results may vary without proper tweaking and
15
metrics for those fonts.
Sep 24, 2004
Sep 24, 2004
16
"""
Dec 26, 2017
Dec 26, 2017
17
Jan 5, 2019
Jan 5, 2019
18
from collections import namedtuple
Feb 18, 2018
Feb 18, 2018
19
import functools
20
from io import StringIO
Oct 29, 2018
Oct 29, 2018
21
import logging
Dec 26, 2017
Dec 26, 2017
22
import os
Feb 17, 2018
Feb 17, 2018
23
import types
Aug 6, 2007
Aug 6, 2007
24
import unicodedata
Nov 10, 2017
Nov 10, 2017
25
Jun 12, 2008
Jun 12, 2008
26
import numpy as np
Dec 26, 2017
Dec 26, 2017
27
from pyparsing import (
28
Combine, Empty, FollowedBy, Forward, Group, Literal, oneOf, OneOrMore,
29
Optional, ParseBaseException, ParseFatalException, ParserElement,
30
QuotedString, Regex, StringEnd, Suppress, ZeroOrMore)
Feb 23, 2011
Feb 23, 2011
31
May 22, 2019
May 22, 2019
32
from matplotlib import cbook, colors as mcolors, rcParams
Sep 24, 2004
Sep 24, 2004
33
from matplotlib.afm import AFM
Feb 17, 2018
Feb 17, 2018
34
from matplotlib.cbook import get_realpath_and_stat
Dec 26, 2017
Dec 26, 2017
35
from matplotlib.ft2font import FT2Image, KERNING_DEFAULT, LOAD_NO_HINTING
Oct 28, 2015
Oct 28, 2015
36
from matplotlib.font_manager import findfont, FontProperties, get_font
Oct 28, 2015
Oct 28, 2015
37
from matplotlib._mathtext_data import (latex_to_bakoma, latex_to_standard,
38
tex2uni, latex_to_cmex,
39
stix_virtual_fonts)
Dec 10, 2015
Dec 10, 2015
40
Jun 11, 2019
Jun 11, 2019
41
ParserElement.enablePackrat()
Oct 29, 2018
Oct 29, 2018
42
_log = logging.getLogger(__name__)
43
Sep 24, 2004
Sep 24, 2004
44
Jul 26, 2007
Jul 26, 2007
45
##############################################################################
46
# FONTS
Jun 30, 2006
Jun 30, 2006
47
Mar 1, 2016
Mar 1, 2016
48
def get_unicode_index(symbol, math=True):
Sep 6, 2018
Sep 6, 2018
49
r"""
50
Return the integer index (from the Unicode table) of *symbol*.
51
52
Parameters
53
----------
54
symbol : str
55
A single unicode character, a TeX command (e.g. r'\pi') or a Type1
56
symbol name (e.g. 'phi').
57
math : bool, default is True
58
If False, always treat as a single unicode character.
59
"""
Feb 29, 2016
Feb 29, 2016
60
# for a non-math symbol, simply return its unicode index
Mar 1, 2016
Mar 1, 2016
61
if not math:
Feb 29, 2016
Feb 29, 2016
62
return ord(symbol)
Aug 20, 2007
Aug 20, 2007
63
# From UTF #25: U+2212 minus sign is the preferred
Jul 30, 2007
Jul 30, 2007
64
# representation of the unary and binary minus sign rather than
Aug 20, 2007
Aug 20, 2007
65
# the ASCII-derived U+002D hyphen-minus, because minus sign is
Jul 30, 2007
Jul 30, 2007
66
# unambiguous and because it is rendered with a more desirable
67
# length, usually longer than a hyphen.
Feb 29, 2016
Feb 29, 2016
68
if symbol == '-':
Jul 30, 2007
Jul 30, 2007
69
return 0x2212
May 10, 2018
May 10, 2018
70
try: # This will succeed if symbol is a single unicode char
Jun 30, 2006
Jun 30, 2006
71
return ord(symbol)
72
except TypeError:
73
pass
May 10, 2018
May 10, 2018
74
try: # Is symbol a TeX symbol (i.e. \alpha)
Aug 21, 2006
Aug 21, 2006
75
return tex2uni[symbol.strip("\\")]
Jun 30, 2006
Jun 30, 2006
76
except KeyError:
May 10, 2018
May 10, 2018
77
raise ValueError(
78
"'{}' is not a valid Unicode character or TeX/Type1 symbol"
79
.format(symbol))
Jun 30, 2006
Jun 30, 2006
80
Feb 18, 2018
Feb 18, 2018
81
May 25, 2019
May 25, 2019
82
class MathtextBackend:
Oct 24, 2008
Oct 24, 2008
83
"""
84
The base class for the mathtext backend-specific code. The
85
purpose of :class:`MathtextBackend` subclasses is to interface
86
between mathtext and a specific matplotlib graphics backend.
87
88
Subclasses need to override the following:
89
90
- :meth:`render_glyph`
Nov 26, 2014
Nov 26, 2014
91
- :meth:`render_rect_filled`
Oct 24, 2008
Oct 24, 2008
92
- :meth:`get_results`
93
Nov 5, 2015
Nov 5, 2015
94
And optionally, if you need to use a FreeType hinting style:
Oct 24, 2008
Oct 24, 2008
95
96
- :meth:`get_hinting_type`
97
"""
Jul 30, 2007
Jul 30, 2007
98
def __init__(self):
Mar 8, 2011
Mar 8, 2011
99
self.width = 0
100
self.height = 0
101
self.depth = 0
Jun 30, 2006
Jun 30, 2006
102
Aug 30, 2007
Aug 30, 2007
103
def set_canvas_size(self, w, h, d):
Oct 24, 2008
Oct 24, 2008
104
'Dimension the drawing canvas'
Jul 30, 2007
Jul 30, 2007
105
self.width = w
106
self.height = h
Aug 30, 2007
Aug 30, 2007
107
self.depth = d
Jun 7, 2007
Jun 7, 2007
108
Jul 30, 2007
Jul 30, 2007
109
def render_glyph(self, ox, oy, info):
Oct 24, 2008
Oct 24, 2008
110
"""
111
Draw a glyph described by *info* to the reference point (*ox*,
112
*oy*).
113
"""
Jul 30, 2007
Jul 30, 2007
114
raise NotImplementedError()
Jun 7, 2007
Jun 7, 2007
115
Nov 26, 2014
Nov 26, 2014
116
def render_rect_filled(self, x1, y1, x2, y2):
Oct 24, 2008
Oct 24, 2008
117
"""
118
Draw a filled black rectangle from (*x1*, *y1*) to (*x2*, *y2*).
119
"""
Jul 30, 2007
Jul 30, 2007
120
raise NotImplementedError()
Sep 24, 2004
Sep 24, 2004
121
Aug 28, 2007
Aug 28, 2007
122
def get_results(self, box):
Oct 24, 2008
Oct 24, 2008
123
"""
124
Return a backend-specific tuple to return to the backend after
125
all processing is done.
126
"""
Jul 30, 2007
Jul 30, 2007
127
raise NotImplementedError()
Aug 7, 2007
Aug 7, 2007
128
129
def get_hinting_type(self):
Oct 24, 2008
Oct 24, 2008
130
"""
Nov 5, 2015
Nov 5, 2015
131
Get the FreeType hinting type to use with this particular
Oct 24, 2008
Oct 24, 2008
132
backend.
133
"""
Aug 7, 2007
Aug 7, 2007
134
return LOAD_NO_HINTING
Aug 14, 2007
Aug 14, 2007
135
Aug 16, 2018
Aug 16, 2018
136
Mar 8, 2011
Mar 8, 2011
137
class MathtextBackendAgg(MathtextBackend):
Oct 24, 2008
Oct 24, 2008
138
"""
Mar 8, 2011
Mar 8, 2011
139
Render glyphs and rectangles to an FTImage buffer, which is later
140
transferred to the Agg image by the Agg backend.
Oct 24, 2008
Oct 24, 2008
141
"""
Mar 8, 2011
Mar 8, 2011
142
def __init__(self):
143
self.ox = 0
144
self.oy = 0
145
self.image = None
146
self.mode = 'bbox'
Aug 28, 2007
Aug 28, 2007
147
self.bbox = [0, 0, 0, 0]
Mar 8, 2011
Mar 8, 2011
148
MathtextBackend.__init__(self)
Aug 28, 2007
Aug 28, 2007
149
150
def _update_bbox(self, x1, y1, x2, y2):
151
self.bbox = [min(self.bbox[0], x1),
152
min(self.bbox[1], y1),
153
max(self.bbox[2], x2),
154
max(self.bbox[3], y2)]
Nov 26, 2007
Nov 26, 2007
155
Mar 8, 2011
Mar 8, 2011
156
def set_canvas_size(self, w, h, d):
157
MathtextBackend.set_canvas_size(self, w, h, d)
158
if self.mode != 'bbox':
Feb 18, 2018
Feb 18, 2018
159
self.image = FT2Image(np.ceil(w), np.ceil(h + max(d, 0)))
Mar 8, 2011
Mar 8, 2011
160
Aug 28, 2007
Aug 28, 2007
161
def render_glyph(self, ox, oy, info):
Mar 8, 2011
Mar 8, 2011
162
if self.mode == 'bbox':
163
self._update_bbox(ox + info.metrics.xmin,
164
oy - info.metrics.ymax,
165
ox + info.metrics.xmax,
166
oy - info.metrics.ymin)
167
else:
168
info.font.draw_glyph_to_bitmap(
Mar 23, 2012
Mar 23, 2012
169
self.image, ox, oy - info.metrics.iceberg, info.glyph,
170
antialiased=rcParams['text.antialiased'])
Aug 28, 2007
Aug 28, 2007
171
172
def render_rect_filled(self, x1, y1, x2, y2):
Mar 8, 2011
Mar 8, 2011
173
if self.mode == 'bbox':
174
self._update_bbox(x1, y1, x2, y2)
175
else:
176
height = max(int(y2 - y1) - 1, 0)
177
if height == 0:
178
center = (y2 + y1) / 2.0
179
y = int(center - (height + 1) / 2.0)
180
else:
181
y = int(y1)
Feb 18, 2018
Feb 18, 2018
182
self.image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height)
Mar 8, 2011
Mar 8, 2011
183
184
def get_results(self, box, used_characters):
185
self.mode = 'bbox'
Aug 30, 2007
Aug 30, 2007
186
orig_height = box.height
187
orig_depth = box.depth
Aug 28, 2007
Aug 28, 2007
188
ship(0, 0, box)
189
bbox = self.bbox
Aug 30, 2007
Aug 30, 2007
190
bbox = [bbox[0] - 1, bbox[1] - 1, bbox[2] + 1, bbox[3] + 1]
Mar 8, 2011
Mar 8, 2011
191
self.mode = 'render'
192
self.set_canvas_size(
Aug 30, 2007
Aug 30, 2007
193
bbox[2] - bbox[0],
194
(bbox[3] - bbox[1]) - orig_depth,
195
(bbox[3] - bbox[1]) - orig_height)
Aug 28, 2007
Aug 28, 2007
196
ship(-bbox[0], -bbox[1], box)
Mar 10, 2011
Mar 10, 2011
197
result = (self.ox,
198
self.oy,
199
self.width,
200
self.height + self.depth,
201
self.depth,
202
self.image,
203
used_characters)
204
self.image = None
205
return result
Aug 7, 2007
Aug 7, 2007
206
207
def get_hinting_type(self):
Feb 28, 2012
Feb 28, 2012
208
from matplotlib.backends import backend_agg
209
return backend_agg.get_hinting_flag()
Aug 14, 2007
Aug 14, 2007
210
Aug 16, 2018
Aug 16, 2018
211
Mar 8, 2011
Mar 8, 2011
212
class MathtextBackendBitmap(MathtextBackendAgg):
213
def get_results(self, box, used_characters):
214
ox, oy, width, height, depth, image, characters = \
Mar 14, 2011
Mar 14, 2011
215
MathtextBackendAgg.get_results(self, box, used_characters)
Mar 8, 2011
Mar 8, 2011
216
return image, depth
Aug 31, 2007
Aug 31, 2007
217
Aug 16, 2018
Aug 16, 2018
218
Jul 30, 2007
Jul 30, 2007
219
class MathtextBackendPs(MathtextBackend):
Oct 24, 2008
Oct 24, 2008
220
"""
Jan 5, 2019
Jan 5, 2019
221
Store information to write a mathtext rendering to the PostScript backend.
Oct 24, 2008
Oct 24, 2008
222
"""
Jan 5, 2019
Jan 5, 2019
223
224
_PSResult = namedtuple(
225
"_PSResult", "width height depth pswriter used_characters")
226
Jul 30, 2007
Jul 30, 2007
227
def __init__(self):
Feb 18, 2018
Feb 18, 2018
228
self.pswriter = StringIO()
Aug 30, 2007
Aug 30, 2007
229
self.lastfont = None
Aug 14, 2007
Aug 14, 2007
230
Jul 30, 2007
Jul 30, 2007
231
def render_glyph(self, ox, oy, info):
232
oy = self.height - oy + info.offset
233
postscript_name = info.postscript_name
234
fontsize = info.fontsize
235
symbol_name = info.symbol_name
Aug 14, 2007
Aug 14, 2007
236
Aug 30, 2007
Aug 30, 2007
237
if (postscript_name, fontsize) != self.lastfont:
238
self.lastfont = postscript_name, fontsize
Jun 7, 2019
Jun 7, 2019
239
self.pswriter.write(
240
f"/{postscript_name} findfont\n"
241
f"{fontsize} scalefont\n"
242
f"setfont\n")
243
244
self.pswriter.write(
245
f"{ox:f} {oy:f} moveto\n"
246
f"/{symbol_name} glyphshow\n")
Jun 30, 2006
Jun 30, 2006
247
Jul 30, 2007
Jul 30, 2007
248
def render_rect_filled(self, x1, y1, x2, y2):
Aug 16, 2018
Aug 16, 2018
249
ps = "%f %f %f %f rectfill\n" % (
250
x1, self.height - y2, x2 - x1, y2 - y1)
Jul 30, 2007
Jul 30, 2007
251
self.pswriter.write(ps)
Mar 31, 2005
Mar 31, 2005
252
Mar 8, 2011
Mar 8, 2011
253
def get_results(self, box, used_characters):
Apr 19, 2013
Apr 19, 2013
254
ship(0, 0, box)
Jan 5, 2019
Jan 5, 2019
255
return self._PSResult(self.width,
256
self.height + self.depth,
257
self.depth,
258
self.pswriter,
259
used_characters)
Aug 14, 2007
Aug 14, 2007
260
Aug 16, 2018
Aug 16, 2018
261
Jul 30, 2007
Jul 30, 2007
262
class MathtextBackendPdf(MathtextBackend):
Jan 5, 2019
Jan 5, 2019
263
"""Store information to write a mathtext rendering to the PDF backend."""
264
265
_PDFResult = namedtuple(
266
"_PDFResult", "width height depth glyphs rects used_characters")
267
Jul 30, 2007
Jul 30, 2007
268
def __init__(self):
Aug 10, 2007
Aug 10, 2007
269
self.glyphs = []
270
self.rects = []
Aug 14, 2007
Aug 14, 2007
271
Jul 30, 2007
Jul 30, 2007
272
def render_glyph(self, ox, oy, info):
273
filename = info.font.fname
274
oy = self.height - oy + info.offset
Aug 10, 2007
Aug 10, 2007
275
self.glyphs.append(
276
(ox, oy, filename, info.fontsize,
Aug 9, 2007
Aug 9, 2007
277
info.num, info.symbol_name))
Mar 31, 2005
Mar 31, 2005
278
Jul 30, 2007
Jul 30, 2007
279
def render_rect_filled(self, x1, y1, x2, y2):
Aug 10, 2007
Aug 10, 2007
280
self.rects.append((x1, self.height - y2, x2 - x1, y2 - y1))
Mar 31, 2005
Mar 31, 2005
281
Mar 8, 2011
Mar 8, 2011
282
def get_results(self, box, used_characters):
Apr 19, 2013
Apr 19, 2013
283
ship(0, 0, box)
Jan 5, 2019
Jan 5, 2019
284
return self._PDFResult(self.width,
285
self.height + self.depth,
286
self.depth,
287
self.glyphs,
288
self.rects,
289
used_characters)
Mar 31, 2005
Mar 31, 2005
290
Aug 16, 2018
Aug 16, 2018
291
Jul 30, 2007
Jul 30, 2007
292
class MathtextBackendSvg(MathtextBackend):
Oct 24, 2008
Oct 24, 2008
293
"""
294
Store information to write a mathtext rendering to the SVG
295
backend.
296
"""
Jul 30, 2007
Jul 30, 2007
297
def __init__(self):
298
self.svg_glyphs = []
299
self.svg_rects = []
Aug 14, 2007
Aug 14, 2007
300
Jul 30, 2007
Jul 30, 2007
301
def render_glyph(self, ox, oy, info):
302
oy = self.height - oy + info.offset
Jun 10, 2010
Jun 10, 2010
303
Jul 30, 2007
Jul 30, 2007
304
self.svg_glyphs.append(
Feb 17, 2011
Feb 17, 2011
305
(info.font, info.fontsize, info.num, ox, oy, info.metrics))
Mar 31, 2005
Mar 31, 2005
306
Jul 30, 2007
Jul 30, 2007
307
def render_rect_filled(self, x1, y1, x2, y2):
308
self.svg_rects.append(
Aug 13, 2007
Aug 13, 2007
309
(x1, self.height - y1 + 1, x2 - x1, y2 - y1))
Jul 30, 2007
Jul 30, 2007
310
Mar 8, 2011
Mar 8, 2011
311
def get_results(self, box, used_characters):
Apr 19, 2013
Apr 19, 2013
312
ship(0, 0, box)
Feb 17, 2018
Feb 17, 2018
313
svg_elements = types.SimpleNamespace(svg_glyphs=self.svg_glyphs,
314
svg_rects=self.svg_rects)
Jul 30, 2007
Jul 30, 2007
315
return (self.width,
Aug 30, 2007
Aug 30, 2007
316
self.height + self.depth,
317
self.depth,
Jul 30, 2007
Jul 30, 2007
318
svg_elements,
Mar 8, 2011
Mar 8, 2011
319
used_characters)
Aug 10, 2007
Aug 10, 2007
320
Aug 16, 2018
Aug 16, 2018
321
Sep 4, 2009
Sep 4, 2009
322
class MathtextBackendPath(MathtextBackend):
323
"""
May 9, 2011
May 9, 2011
324
Store information to write a mathtext rendering to the text path
325
machinery.
Sep 4, 2009
Sep 4, 2009
326
"""
327
328
def __init__(self):
329
self.glyphs = []
330
self.rects = []
331
332
def render_glyph(self, ox, oy, info):
333
oy = self.height - oy + info.offset
May 9, 2011
May 9, 2011
334
thetext = info.num
Sep 4, 2009
Sep 4, 2009
335
self.glyphs.append(
336
(info.font, info.fontsize, thetext, ox, oy))
337
338
def render_rect_filled(self, x1, y1, x2, y2):
Aug 16, 2018
Aug 16, 2018
339
self.rects.append((x1, self.height - y2, x2 - x1, y2 - y1))
Sep 4, 2009
Sep 4, 2009
340
Mar 8, 2011
Mar 8, 2011
341
def get_results(self, box, used_characters):
Apr 19, 2013
Apr 19, 2013
342
ship(0, 0, box)
Sep 4, 2009
Sep 4, 2009
343
return (self.width,
344
self.height + self.depth,
345
self.depth,
346
self.glyphs,
347
self.rects)
348
Aug 16, 2018
Aug 16, 2018
349
Aug 10, 2007
Aug 10, 2007
350
class MathtextBackendCairo(MathtextBackend):
Oct 24, 2008
Oct 24, 2008
351
"""
352
Store information to write a mathtext rendering to the Cairo
353
backend.
354
"""
355
Aug 10, 2007
Aug 10, 2007
356
def __init__(self):
357
self.glyphs = []
358
self.rects = []
Aug 14, 2007
Aug 14, 2007
359
Aug 10, 2007
Aug 10, 2007
360
def render_glyph(self, ox, oy, info):
361
oy = oy - info.offset - self.height
Feb 18, 2018
Feb 18, 2018
362
thetext = chr(info.num)
Aug 10, 2007
Aug 10, 2007
363
self.glyphs.append(
364
(info.font, info.fontsize, thetext, ox, oy))
365
366
def render_rect_filled(self, x1, y1, x2, y2):
367
self.rects.append(
368
(x1, y1 - self.height, x2 - x1, y2 - y1))
369
Mar 8, 2011
Mar 8, 2011
370
def get_results(self, box, used_characters):
Apr 19, 2013
Apr 19, 2013
371
ship(0, 0, box)
Aug 10, 2007
Aug 10, 2007
372
return (self.width,
Aug 30, 2007
Aug 30, 2007
373
self.height + self.depth,
374
self.depth,
Aug 10, 2007
Aug 10, 2007
375
self.glyphs,
376
self.rects)
Aug 14, 2007
Aug 14, 2007
377
Aug 16, 2018
Aug 16, 2018
378
May 25, 2019
May 25, 2019
379
class Fonts:
Jul 30, 2007
Jul 30, 2007
380
"""
Oct 24, 2008
Oct 24, 2008
381
An abstract base class for a system of fonts to use for mathtext.
Mar 31, 2005
Mar 31, 2005
382
Jul 30, 2007
Jul 30, 2007
383
The class must be able to take symbol keys and font file names and
384
return the character metrics. It also delegates to a backend class
385
to do the actual drawing.
386
"""
Mar 31, 2005
Mar 31, 2005
387
Jul 30, 2007
Jul 30, 2007
388
def __init__(self, default_font_prop, mathtext_backend):
Oct 24, 2008
Oct 24, 2008
389
"""
390
*default_font_prop*: A
391
:class:`~matplotlib.font_manager.FontProperties` object to use
392
for the default non-math font, or the base font for Unicode
393
(generic) font rendering.
394
395
*mathtext_backend*: A subclass of :class:`MathTextBackend`
396
used to delegate the actual rendering.
397
"""
Jul 30, 2007
Jul 30, 2007
398
self.default_font_prop = default_font_prop
Mar 8, 2011
Mar 8, 2011
399
self.mathtext_backend = mathtext_backend
Jul 30, 2007
Jul 30, 2007
400
self.used_characters = {}
Aug 14, 2007
Aug 14, 2007
401
Aug 29, 2007
Aug 29, 2007
402
def destroy(self):
Oct 24, 2008
Oct 24, 2008
403
"""
404
Fix any cyclical references before the object is about
405
to be destroyed.
406
"""
Aug 29, 2007
Aug 29, 2007
407
self.used_characters = None
Nov 26, 2007
Nov 26, 2007
408
Jul 9, 2008
Jul 9, 2008
409
def get_kern(self, font1, fontclass1, sym1, fontsize1,
410
font2, fontclass2, sym2, fontsize2, dpi):
May 26, 2019
May 26, 2019
411
r"""
Oct 24, 2008
Oct 24, 2008
412
Get the kerning distance for font between *sym1* and *sym2*.
413
414
*fontX*: one of the TeX font names::
Jun 30, 2006
Jun 30, 2006
415
Dec 26, 2008
Dec 26, 2008
416
tt, it, rm, cal, sf, bf or default/regular (non-math)
Aug 14, 2007
Aug 14, 2007
417
Oct 24, 2008
Oct 24, 2008
418
*fontclassX*: TODO
Jun 30, 2006
Jun 30, 2006
419
May 26, 2019
May 26, 2019
420
*symX*: a symbol in raw TeX form. e.g., '1', 'x' or '\sigma'
Oct 24, 2008
Oct 24, 2008
421
422
*fontsizeX*: the fontsize in points
423
424
*dpi*: the current dots-per-inch
Jul 30, 2007
Jul 30, 2007
425
"""
426
return 0.
Jun 30, 2006
Jun 30, 2006
427
Mar 1, 2016
Mar 1, 2016
428
def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True):
May 26, 2019
May 26, 2019
429
r"""
Oct 24, 2008
Oct 24, 2008
430
*font*: one of the TeX font names::
431
Dec 26, 2008
Dec 26, 2008
432
tt, it, rm, cal, sf, bf or default/regular (non-math)
Oct 24, 2008
Oct 24, 2008
433
434
*font_class*: TODO
435
May 26, 2019
May 26, 2019
436
*sym*: a symbol in raw TeX form. e.g., '1', 'x' or '\sigma'
Oct 24, 2008
Oct 24, 2008
437
438
*fontsize*: font size in points
439
440
*dpi*: current dots-per-inch
441
Mar 1, 2016
Mar 1, 2016
442
*math*: whether sym is a math character
Feb 29, 2016
Feb 29, 2016
443
Oct 24, 2008
Oct 24, 2008
444
Returns an object with the following attributes:
445
446
- *advance*: The advance distance (in points) of the glyph.
447
448
- *height*: The height of the glyph in points.
449
450
- *width*: The width of the glyph in points.
451
452
- *xmin*, *xmax*, *ymin*, *ymax* - the ink rectangle of the glyph
Mar 31, 2005
Mar 31, 2005
453
Oct 24, 2008
Oct 24, 2008
454
- *iceberg* - the distance from the baseline to the top of
455
the glyph. This corresponds to TeX's definition of
456
"height".
Aug 29, 2007
Aug 29, 2007
457
"""
Mar 1, 2016
Mar 1, 2016
458
info = self._get_info(font, font_class, sym, fontsize, dpi, math)
Jul 30, 2007
Jul 30, 2007
459
return info.metrics
Mar 31, 2005
Mar 31, 2005
460
Aug 30, 2007
Aug 30, 2007
461
def set_canvas_size(self, w, h, d):
Oct 24, 2008
Oct 24, 2008
462
"""
463
Set the size of the buffer used to render the math expression.
464
Only really necessary for the bitmap backends.
465
"""
Feb 18, 2018
Feb 18, 2018
466
self.width, self.height, self.depth = np.ceil([w, h, d])
467
self.mathtext_backend.set_canvas_size(
468
self.width, self.height, self.depth)
Mar 31, 2005
Mar 31, 2005
469
Nov 12, 2007
Nov 12, 2007
470
def render_glyph(self, ox, oy, facename, font_class, sym, fontsize, dpi):
Oct 24, 2008
Oct 24, 2008
471
"""
472
Draw a glyph at
473
474
- *ox*, *oy*: position
475
476
- *facename*: One of the TeX face names
477
478
- *font_class*:
479
480
- *sym*: TeX symbol name or single character
481
482
- *fontsize*: fontsize in points
483
484
- *dpi*: The dpi to draw at.
485
"""
Nov 12, 2007
Nov 12, 2007
486
info = self._get_info(facename, font_class, sym, fontsize, dpi)
Jul 30, 2007
Jul 30, 2007
487
realpath, stat_key = get_realpath_and_stat(info.font.fname)
488
used_characters = self.used_characters.setdefault(
Oct 2, 2008
Oct 2, 2008
489
stat_key, (realpath, set()))
Jun 17, 2008
Jun 17, 2008
490
used_characters[1].add(info.num)
Mar 8, 2011
Mar 8, 2011
491
self.mathtext_backend.render_glyph(ox, oy, info)
Mar 31, 2005
Mar 31, 2005
492
Jul 30, 2007
Jul 30, 2007
493
def render_rect_filled(self, x1, y1, x2, y2):
Oct 24, 2008
Oct 24, 2008
494
"""
495
Draw a filled rectangle from (*x1*, *y1*) to (*x2*, *y2*).
496
"""
Mar 8, 2011
Mar 8, 2011
497
self.mathtext_backend.render_rect_filled(x1, y1, x2, y2)
Mar 31, 2005
Mar 31, 2005
498
Jul 30, 2007
Jul 30, 2007
499
def get_xheight(self, font, fontsize, dpi):
Oct 24, 2008
Oct 24, 2008
500
"""
501
Get the xheight for the given *font* and *fontsize*.
502
"""
Jul 30, 2007
Jul 30, 2007
503
raise NotImplementedError()
Mar 31, 2005
Mar 31, 2005
504
Jul 30, 2007
Jul 30, 2007
505
def get_underline_thickness(self, font, fontsize, dpi):
Oct 24, 2008
Oct 24, 2008
506
"""
507
Get the line thickness that matches the given font. Used as a
508
base unit for drawing lines such as in a fraction or radical.
509
"""
Jul 30, 2007
Jul 30, 2007
510
raise NotImplementedError()
Aug 14, 2007
Aug 14, 2007
511
Jul 30, 2007
Jul 30, 2007
512
def get_used_characters(self):
Oct 24, 2008
Oct 24, 2008
513
"""
514
Get the set of characters that were used in the math
515
expression. Used by backends that need to subset fonts so
516
they know which glyphs to include.
517
"""
Jul 30, 2007
Jul 30, 2007
518
return self.used_characters
519
Aug 28, 2007
Aug 28, 2007
520
def get_results(self, box):
Oct 24, 2008
Oct 24, 2008
521
"""
522
Get the data needed by the backend to render the math
523
expression. The return value is backend-specific.
524
"""
Aug 16, 2018
Aug 16, 2018
525
result = self.mathtext_backend.get_results(
526
box, self.get_used_characters())
Mar 10, 2011
Mar 10, 2011
527
self.destroy()
528
return result
Jul 30, 2007
Jul 30, 2007
529
530
def get_sized_alternatives_for_symbol(self, fontname, sym):
Aug 29, 2007
Aug 29, 2007
531
"""
532
Override if your font provides multiple sizes of the same
Oct 24, 2008
Oct 24, 2008
533
symbol. Should return a list of symbols matching *sym* in
534
various sizes. The expression renderer will select the most
535
appropriate size for a given situation from this list.
Aug 29, 2007
Aug 29, 2007
536
"""
Jul 30, 2007
Jul 30, 2007
537
return [(fontname, sym)]
Aug 14, 2007
Aug 14, 2007
538
Aug 16, 2018
Aug 16, 2018
539
Jul 30, 2007
Jul 30, 2007
540
class TruetypeFonts(Fonts):
541
"""
542
A generic base class for all font setups that use Truetype fonts
Oct 24, 2008
Oct 24, 2008
543
(through FT2Font).
Jul 30, 2007
Jul 30, 2007
544
"""
545
def __init__(self, default_font_prop, mathtext_backend):
546
Fonts.__init__(self, default_font_prop, mathtext_backend)
Oct 15, 2008
Oct 15, 2008
547
self.glyphd = {}
548
self._fonts = {}
Aug 14, 2007
Aug 14, 2007
549
Oct 15, 2008
Oct 15, 2008
550
filename = findfont(default_font_prop)
Oct 28, 2015
Oct 28, 2015
551
default_font = get_font(filename)
Oct 15, 2008
Oct 15, 2008
552
self._fonts['default'] = default_font
Dec 26, 2008
Dec 26, 2008
553
self._fonts['regular'] = default_font
Aug 14, 2007
Aug 14, 2007
554
Aug 29, 2007
Aug 29, 2007
555
def destroy(self):
556
self.glyphd = None
557
Fonts.destroy(self)
Nov 26, 2007
Nov 26, 2007
558
Jul 26, 2007
Jul 26, 2007
559
def _get_font(self, font):
Jul 30, 2007
Jul 30, 2007
560
if font in self.fontmap:
561
basename = self.fontmap[font]
Jul 26, 2007
Jul 26, 2007
562
else:
Jul 30, 2007
Jul 30, 2007
563
basename = font
Nov 26, 2007
Nov 26, 2007
564
cached_font = self._fonts.get(basename)
Sep 18, 2013
Sep 18, 2013
565
if cached_font is None and os.path.exists(basename):
Oct 28, 2015
Oct 28, 2015
566
cached_font = get_font(basename)
Nov 26, 2007
Nov 26, 2007
567
self._fonts[basename] = cached_font
Oct 28, 2015
Oct 28, 2015
568
self._fonts[cached_font.postscript_name] = cached_font
569
self._fonts[cached_font.postscript_name.lower()] = cached_font
Jul 30, 2007
Jul 30, 2007
570
return cached_font
Jul 26, 2007
Jul 26, 2007
571
Oct 28, 2015
Oct 28, 2015
572
def _get_offset(self, font, glyph, fontsize, dpi):
573
if font.postscript_name == 'Cmex10':
May 9, 2011
May 9, 2011
574
return ((glyph.height/64.0/2.0) + (fontsize/3.0 * dpi/72.0))
Jul 26, 2007
Jul 26, 2007
575
return 0.
Aug 14, 2007
Aug 14, 2007
576
Mar 1, 2016
Mar 1, 2016
577
def _get_info(self, fontname, font_class, sym, fontsize, dpi, math=True):
Nov 12, 2007
Nov 12, 2007
578
key = fontname, font_class, sym, fontsize, dpi
Jul 30, 2007
Jul 30, 2007
579
bunch = self.glyphd.get(key)
580
if bunch is not None:
581
return bunch
Sep 24, 2004
Sep 24, 2004
582
Oct 28, 2015
Oct 28, 2015
583
font, num, symbol_name, fontsize, slanted = \
Mar 1, 2016
Mar 1, 2016
584
self._get_glyph(fontname, font_class, sym, fontsize, math)
Sep 24, 2004
Sep 24, 2004
585
Jul 26, 2007
Jul 26, 2007
586
font.set_size(fontsize, dpi)
Aug 7, 2007
Aug 7, 2007
587
glyph = font.load_char(
588
num,
Mar 8, 2011
Mar 8, 2011
589
flags=self.mathtext_backend.get_hinting_type())
Sep 24, 2004
Sep 24, 2004
590
591
xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox]
Oct 28, 2015
Oct 28, 2015
592
offset = self._get_offset(font, glyph, fontsize, dpi)
Feb 17, 2018
Feb 17, 2018
593
metrics = types.SimpleNamespace(
Jul 30, 2007
Jul 30, 2007
594
advance = glyph.linearHoriAdvance/65536.0,
Jul 30, 2007
Jul 30, 2007
595
height = glyph.height/64.0,
Jul 30, 2007
Jul 30, 2007
596
width = glyph.width/64.0,
597
xmin = xmin,
598
xmax = xmax,
599
ymin = ymin+offset,
600
ymax = ymax+offset,
Jul 26, 2007
Jul 26, 2007
601
# iceberg is the equivalent of TeX's "height"
Aug 3, 2007
Aug 3, 2007
602
iceberg = glyph.horiBearingY/64.0 + offset,
603
slanted = slanted
Sep 24, 2004
Sep 24, 2004
604
)
Jun 7, 2007
Jun 7, 2007
605
Feb 17, 2018
Feb 17, 2018
606
result = self.glyphd[key] = types.SimpleNamespace(
Jul 30, 2007
Jul 30, 2007
607
font = font,
608
fontsize = fontsize,
609
postscript_name = font.postscript_name,
610
metrics = metrics,
611
symbol_name = symbol_name,
612
num = num,
613
glyph = glyph,
614
offset = offset
615
)
Aug 21, 2007
Aug 21, 2007
616
return result
Sep 24, 2004
Sep 24, 2004
617
Oct 28, 2015
Oct 28, 2015
618
def get_xheight(self, fontname, fontsize, dpi):
619
font = self._get_font(fontname)
620
font.set_size(fontsize, dpi)
621
pclt = font.get_sfnt_table('pclt')
Jul 30, 2007
Jul 30, 2007
622
if pclt is None:
623
# Some fonts don't store the xHeight, so we do a poor man's xHeight
Aug 16, 2018
Aug 16, 2018
624
metrics = self.get_metrics(
625
fontname, rcParams['mathtext.default'], 'x', fontsize, dpi)
Jul 30, 2007
Jul 30, 2007
626
return metrics.iceberg
Apr 28, 2008
Apr 28, 2008
627
xHeight = (pclt['xHeight'] / 64.0) * (fontsize / 12.0) * (dpi / 100.0)
Jul 26, 2007
Jul 26, 2007
628
return xHeight
629
630
def get_underline_thickness(self, font, fontsize, dpi):
Oct 24, 2008
Oct 24, 2008
631
# This function used to grab underline thickness from the font
632
# metrics, but that information is just too un-reliable, so it
633
# is now hardcoded.
Jun 11, 2008
Jun 11, 2008
634
return ((0.75 / 12.0) * fontsize * dpi) / 72.0
Jul 26, 2007
Jul 26, 2007
635
Nov 12, 2007
Nov 12, 2007
636
def get_kern(self, font1, fontclass1, sym1, fontsize1,
637
font2, fontclass2, sym2, fontsize2, dpi):
Jul 30, 2007
Jul 30, 2007
638
if font1 == font2 and fontsize1 == fontsize2:
Nov 12, 2007
Nov 12, 2007
639
info1 = self._get_info(font1, fontclass1, sym1, fontsize1, dpi)
640
info2 = self._get_info(font2, fontclass2, sym2, fontsize2, dpi)
Jul 30, 2007
Jul 30, 2007
641
font = info1.font
Aug 16, 2018
Aug 16, 2018
642
return font.get_kerning(info1.num, info2.num, KERNING_DEFAULT) / 64
Jul 9, 2008
Jul 9, 2008
643
return Fonts.get_kern(self, font1, fontclass1, sym1, fontsize1,
644
font2, fontclass2, sym2, fontsize2, dpi)
Aug 14, 2007
Aug 14, 2007
645
Aug 16, 2018
Aug 16, 2018
646
Jul 30, 2007
Jul 30, 2007
647
class BakomaFonts(TruetypeFonts):
Sep 24, 2004
Sep 24, 2004
648
"""
Oct 24, 2008
Oct 24, 2008
649
Use the Bakoma TrueType fonts for rendering.
650
651
Symbols are strewn about a number of font files, each of which has
652
its own proprietary 8-bit encoding.
Sep 24, 2004
Sep 24, 2004
653
"""
Jun 11, 2019
Jun 11, 2019
654
_fontmap = {
655
'cal': 'cmsy10',
656
'rm': 'cmr10',
657
'tt': 'cmtt10',
658
'it': 'cmmi10',
659
'bf': 'cmb10',
660
'sf': 'cmss10',
661
'ex': 'cmex10',
662
}
Aug 14, 2007
Aug 14, 2007
663
Aug 6, 2007
Aug 6, 2007
664
def __init__(self, *args, **kwargs):
Jun 11, 2008
Jun 11, 2008
665
self._stix_fallback = StixFonts(*args, **kwargs)
666
Aug 6, 2007
Aug 6, 2007
667
TruetypeFonts.__init__(self, *args, **kwargs)
Mar 31, 2009
Mar 31, 2009
668
self.fontmap = {}
Feb 18, 2018
Feb 18, 2018
669
for key, val in self._fontmap.items():
Mar 31, 2009
Mar 31, 2009
670
fullpath = findfont(val)
671
self.fontmap[key] = fullpath
672
self.fontmap[val] = fullpath
Aug 14, 2007
Aug 14, 2007
673
Oct 2, 2008
Oct 2, 2008
674
_slanted_symbols = set(r"\int \oint".split())
Aug 14, 2007
Aug 14, 2007
675
Mar 1, 2016
Mar 1, 2016
676
def _get_glyph(self, fontname, font_class, sym, fontsize, math=True):
Nov 8, 2007
Nov 8, 2007
677
symbol_name = None
Oct 28, 2015
Oct 28, 2015
678
font = None
Oct 2, 2008
Oct 2, 2008
679
if fontname in self.fontmap and sym in latex_to_bakoma:
Jul 30, 2007
Jul 30, 2007
680
basename, num = latex_to_bakoma[sym]
Aug 6, 2007
Aug 6, 2007
681
slanted = (basename == "cmmi10") or sym in self._slanted_symbols
Oct 28, 2015
Oct 28, 2015
682
font = self._get_font(basename)
Jul 30, 2007
Jul 30, 2007
683
elif len(sym) == 1:
Aug 3, 2007
Aug 3, 2007
684
slanted = (fontname == "it")
Oct 28, 2015
Oct 28, 2015
685
font = self._get_font(fontname)
686
if font is not None:
Nov 12, 2007
Nov 12, 2007
687
num = ord(sym)
Oct 28, 2015
Oct 28, 2015
688
689
if font is not None:
690
gid = font.get_char_index(num)
691
if gid != 0:
692
symbol_name = font.get_glyph_name(gid)
Nov 8, 2007
Nov 8, 2007
693
694
if symbol_name is None:
Jun 16, 2008
Jun 16, 2008
695
return self._stix_fallback._get_glyph(
Mar 1, 2016
Mar 1, 2016
696
fontname, font_class, sym, fontsize, math)
Jul 16, 2006
Jul 16, 2006
697
Oct 28, 2015
Oct 28, 2015
698
return font, num, symbol_name, fontsize, slanted
Jul 30, 2007
Jul 30, 2007
699
700
# The Bakoma fonts contain many pre-sized alternatives for the
701
# delimiters. The AutoSizedChar class will use these alternatives
702
# and select the best (closest sized) glyph.
703
_size_alternatives = {
Jun 11, 2019
Jun 11, 2019
704
'(': [('rm', '('), ('ex', '\xa1'), ('ex', '\xb3'),
Jul 30, 2007
Jul 30, 2007
705
('ex', '\xb5'), ('ex', '\xc3')],
Jun 11, 2019
Jun 11, 2019
706
')': [('rm', ')'), ('ex', '\xa2'), ('ex', '\xb4'),
Jul 30, 2007
Jul 30, 2007
707
('ex', '\xb6'), ('ex', '\x21')],
Jun 11, 2019
Jun 11, 2019
708
'{': [('cal', '{'), ('ex', '\xa9'), ('ex', '\x6e'),
Jul 30, 2007
Jul 30, 2007
709
('ex', '\xbd'), ('ex', '\x28')],
Jun 11, 2019
Jun 11, 2019
710
'}': [('cal', '}'), ('ex', '\xaa'), ('ex', '\x6f'),
Jul 30, 2007
Jul 30, 2007
711
('ex', '\xbe'), ('ex', '\x29')],
Jul 30, 2007
Jul 30, 2007
712
# The fourth size of '[' is mysteriously missing from the BaKoMa
Jan 22, 2018
Jan 22, 2018
713
# font, so I've omitted it for both '[' and ']'
Jun 11, 2019
Jun 11, 2019
714
'[': [('rm', '['), ('ex', '\xa3'), ('ex', '\x68'),
Jul 30, 2007
Jul 30, 2007
715
('ex', '\x22')],
Jun 11, 2019
Jun 11, 2019
716
']': [('rm', ']'), ('ex', '\xa4'), ('ex', '\x69'),
Jul 30, 2007
Jul 30, 2007
717
('ex', '\x23')],
Jun 11, 2019
Jun 11, 2019
718
r'\lfloor': [('ex', '\xa5'), ('ex', '\x6a'),
Jul 30, 2007
Jul 30, 2007
719
('ex', '\xb9'), ('ex', '\x24')],
Jun 11, 2019
Jun 11, 2019
720
r'\rfloor': [('ex', '\xa6'), ('ex', '\x6b'),
Jul 30, 2007
Jul 30, 2007
721
('ex', '\xba'), ('ex', '\x25')],
Jun 11, 2019
Jun 11, 2019
722
r'\lceil': [('ex', '\xa7'), ('ex', '\x6c'),
Jul 30, 2007
Jul 30, 2007
723
('ex', '\xbb'), ('ex', '\x26')],
Jun 11, 2019
Jun 11, 2019
724
r'\rceil': [('ex', '\xa8'), ('ex', '\x6d'),
Jul 30, 2007
Jul 30, 2007
725
('ex', '\xbc'), ('ex', '\x27')],
Jun 11, 2019
Jun 11, 2019
726
r'\langle': [('ex', '\xad'), ('ex', '\x44'),
Jul 30, 2007
Jul 30, 2007
727
('ex', '\xbf'), ('ex', '\x2a')],
Jun 11, 2019
Jun 11, 2019
728
r'\rangle': [('ex', '\xae'), ('ex', '\x45'),
Jul 30, 2007
Jul 30, 2007
729
('ex', '\xc0'), ('ex', '\x2b')],
Jun 11, 2019
Jun 11, 2019
730
r'\__sqrt__': [('ex', '\x70'), ('ex', '\x71'),
Jul 30, 2007
Jul 30, 2007
731
('ex', '\x72'), ('ex', '\x73')],
Sep 5, 2007
Sep 5, 2007
732
r'\backslash': [('ex', '\xb2'), ('ex', '\x2f'),
Jul 30, 2007
Jul 30, 2007
733
('ex', '\xc2'), ('ex', '\x2d')],
Jun 11, 2019
Jun 11, 2019
734
r'/': [('rm', '/'), ('ex', '\xb1'), ('ex', '\x2e'),
Aug 1, 2007
Aug 1, 2007
735
('ex', '\xcb'), ('ex', '\x2c')],
Jun 11, 2019
Jun 11, 2019
736
r'\widehat': [('rm', '\x5e'), ('ex', '\x62'), ('ex', '\x63'),
Aug 1, 2007
Aug 1, 2007
737
('ex', '\x64')],
738
r'\widetilde': [('rm', '\x7e'), ('ex', '\x65'), ('ex', '\x66'),
Dec 4, 2008
Dec 4, 2008
739
('ex', '\x67')],
Jun 11, 2019
Jun 11, 2019
740
r'<': [('cal', 'h'), ('ex', 'D')],
741
r'>': [('cal', 'i'), ('ex', 'E')]
Jul 30, 2007
Jul 30, 2007
742
}
Jul 16, 2006
Jul 16, 2006
743
Jan 31, 2017
Jan 31, 2017
744
for alias, target in [(r'\leftparen', '('),
745
(r'\rightparent', ')'),
746
(r'\leftbrace', '{'),
747
(r'\rightbrace', '}'),
748
(r'\leftbracket', '['),
749
(r'\rightbracket', ']'),
Feb 24, 2012
Feb 24, 2012
750
(r'\{', '{'),
751
(r'\}', '}'),
752
(r'\[', '['),
753
(r'\]', ']')]:
Jul 30, 2007
Jul 30, 2007
754
_size_alternatives[alias] = _size_alternatives[target]
Aug 14, 2007
Aug 14, 2007
755
Jul 30, 2007
Jul 30, 2007
756
def get_sized_alternatives_for_symbol(self, fontname, sym):
Oct 24, 2008
Oct 24, 2008
757
return self._size_alternatives.get(sym, [(fontname, sym)])
Aug 14, 2007
Aug 14, 2007
758
Aug 16, 2018
Aug 16, 2018
759
Jul 30, 2007
Jul 30, 2007
760
class UnicodeFonts(TruetypeFonts):
Jul 30, 2007
Jul 30, 2007
761
"""
Oct 24, 2008
Oct 24, 2008
762
An abstract base class for handling Unicode fonts.
763
764
While some reasonably complete Unicode fonts (such as DejaVu) may
765
work in some situations, the only Unicode font I'm aware of with a
766
complete set of math symbols is STIX.
Jul 30, 2007
Jul 30, 2007
767
Oct 24, 2008
Oct 24, 2008
768
This class will "fallback" on the Bakoma fonts when a required
769
symbol can not be found in the font.
770
"""
Nov 5, 2007
Nov 5, 2007
771
use_cmex = True
Nov 26, 2007
Nov 26, 2007
772
Aug 6, 2007
Aug 6, 2007
773
def __init__(self, *args, **kwargs):
774
# This must come first so the backend's owner is set correctly
775
if rcParams['mathtext.fallback_to_cm']:
776
self.cm_fallback = BakomaFonts(*args, **kwargs)
777
else:
778
self.cm_fallback = None
779
TruetypeFonts.__init__(self, *args, **kwargs)
Mar 31, 2009
Mar 31, 2009
780
self.fontmap = {}
781
for texfont in "cal rm tt it bf sf".split():
782
prop = rcParams['mathtext.' + texfont]
Sep 5, 2007
Sep 5, 2007
783
font = findfont(prop)
Mar 31, 2009
Mar 31, 2009
784
self.fontmap[texfont] = font
785
prop = FontProperties('cmex10')
786
font = findfont(prop)
787
self.fontmap['ex'] = font
Aug 14, 2007
Aug 14, 2007
788
Oct 2, 2008
Oct 2, 2008
789
_slanted_symbols = set(r"\int \oint".split())
Nov 12, 2007
Nov 12, 2007
790
791
def _map_virtual_font(self, fontname, font_class, uniindex):
792
return fontname, uniindex
Nov 26, 2007
Nov 26, 2007
793
Mar 1, 2016
Mar 1, 2016
794
def _get_glyph(self, fontname, font_class, sym, fontsize, math=True):
Jul 30, 2007
Jul 30, 2007
795
found_symbol = False
Aug 14, 2007
Aug 14, 2007
796
Nov 5, 2007
Nov 5, 2007
797
if self.use_cmex:
798
uniindex = latex_to_cmex.get(sym)
799
if uniindex is not None:
800
fontname = 'ex'
801
found_symbol = True
Nov 26, 2007
Nov 26, 2007
802
Nov 5, 2007
Nov 5, 2007
803
if not found_symbol:
Sep 5, 2007
Sep 5, 2007
804
try:
Mar 1, 2016
Mar 1, 2016
805
uniindex = get_unicode_index(sym, math)
Sep 5, 2007
Sep 5, 2007
806
found_symbol = True
807
except ValueError:
Aug 6, 2007
Aug 6, 2007
808
uniindex = ord('?')
Oct 29, 2018
Oct 29, 2018
809
_log.warning(
810
"No TeX to unicode mapping for {!a}.".format(sym))
Jul 30, 2007
Jul 30, 2007
811
Nov 12, 2007
Nov 12, 2007
812
fontname, uniindex = self._map_virtual_font(
813
fontname, font_class, uniindex)
Nov 26, 2007
Nov 26, 2007
814
Apr 30, 2010
Apr 30, 2010
815
new_fontname = fontname
Jun 10, 2010
Jun 10, 2010
816
Jul 30, 2007
Jul 30, 2007
817
# Only characters in the "Letter" class should be italicized in 'it'
Aug 6, 2007
Aug 6, 2007
818
# mode. Greek capital letters should be Roman.
Jul 30, 2007
Jul 30, 2007
819
if found_symbol:
Aug 16, 2018
Aug 16, 2018
820
if fontname == 'it' and uniindex < 0x10000:
821
char = chr(uniindex)
Jun 8, 2019
Jun 8, 2019
822
if (unicodedata.category(char)[0] != "L"
Aug 16, 2018
Aug 16, 2018
823
or unicodedata.name(char).startswith("GREEK CAPITAL")):
824
new_fontname = 'rm'
Aug 6, 2007
Aug 6, 2007
825
Sep 5, 2007
Sep 5, 2007
826
slanted = (new_fontname == 'it') or sym in self._slanted_symbols
Nov 12, 2007
Nov 12, 2007
827
found_symbol = False
Oct 28, 2015
Oct 28, 2015
828
font = self._get_font(new_fontname)
829
if font is not None:
Nov 6, 2015
Nov 6, 2015
830
glyphindex = font.get_char_index(uniindex)
831
if glyphindex != 0:
Nov 12, 2007
Nov 12, 2007
832
found_symbol = True
Jul 30, 2007
Jul 30, 2007
833
834
if not found_symbol:
Aug 6, 2007
Aug 6, 2007
835
if self.cm_fallback:
Nov 5, 2015
Nov 5, 2015
836
if isinstance(self.cm_fallback, BakomaFonts):
Oct 29, 2018
Oct 29, 2018
837
_log.warning(
838
"Substituting with a symbol from Computer Modern.")
Nov 5, 2015
Nov 5, 2015
839
if (fontname in ('it', 'regular') and
840
isinstance(self.cm_fallback, StixFonts)):
841
return self.cm_fallback._get_glyph(
842
'rm', font_class, sym, fontsize)
843
else:
844
return self.cm_fallback._get_glyph(
Mar 22, 2016
Mar 22, 2016
845
fontname, font_class, sym, fontsize)
Aug 6, 2007
Aug 6, 2007
846
else:
Feb 18, 2018
Feb 18, 2018
847
if (fontname in ('it', 'regular')
848
and isinstance(self, StixFonts)):
Jun 11, 2008
Jun 11, 2008
849
return self._get_glyph('rm', font_class, sym, fontsize)
Oct 29, 2018
Oct 29, 2018
850
_log.warning("Font {!r} does not have a glyph for {!a} "
851
"[U+{:x}], substituting with a dummy "
852
"symbol.".format(new_fontname, sym, uniindex))
Nov 5, 2007
Nov 5, 2007
853
fontname = 'rm'
Oct 28, 2015
Oct 28, 2015
854
font = self._get_font(fontname)
Aug 16, 2018
Aug 16, 2018
855
uniindex = 0xA4 # currency char, for lack of anything better
Oct 28, 2015
Oct 28, 2015
856
glyphindex = font.get_char_index(uniindex)
Aug 6, 2007
Aug 6, 2007
857
slanted = False
Aug 14, 2007
Aug 14, 2007
858
Oct 28, 2015
Oct 28, 2015
859
symbol_name = font.get_glyph_name(glyphindex)
860
return font, uniindex, symbol_name, fontsize, slanted
Aug 6, 2007
Aug 6, 2007
861
Sep 5, 2007
Sep 5, 2007
862
def get_sized_alternatives_for_symbol(self, fontname, sym):
Aug 6, 2007
Aug 6, 2007
863
if self.cm_fallback:
Sep 5, 2007
Sep 5, 2007
864
return self.cm_fallback.get_sized_alternatives_for_symbol(
865
fontname, sym)
866
return [(fontname, sym)]
Nov 5, 2007
Nov 5, 2007
867
Nov 5, 2015
Nov 5, 2015
868
Nov 5, 2015
Nov 5, 2015
869
class DejaVuFonts(UnicodeFonts):
870
use_cmex = False
871
872
def __init__(self, *args, **kwargs):
873
# This must come first so the backend's owner is set correctly
874
if isinstance(self, DejaVuSerifFonts):
875
self.cm_fallback = StixFonts(*args, **kwargs)
876
else:
877
self.cm_fallback = StixSansFonts(*args, **kwargs)
Nov 5, 2015
Nov 5, 2015
878
self.bakoma = BakomaFonts(*args, **kwargs)
Nov 5, 2015
Nov 5, 2015
879
TruetypeFonts.__init__(self, *args, **kwargs)
880
self.fontmap = {}
881
# Include Stix sized alternatives for glyphs
882
self._fontmap.update({
Jun 11, 2019
Jun 11, 2019
883
1: 'STIXSizeOneSym',
884
2: 'STIXSizeTwoSym',
885
3: 'STIXSizeThreeSym',
886
4: 'STIXSizeFourSym',
887
5: 'STIXSizeFiveSym',
888
})
Feb 18, 2018
Feb 18, 2018
889
for key, name in self._fontmap.items():
Nov 5, 2015
Nov 5, 2015
890
fullpath = findfont(name)
891
self.fontmap[key] = fullpath
892
self.fontmap[name] = fullpath
893
Mar 1, 2016
Mar 1, 2016
894
def _get_glyph(self, fontname, font_class, sym, fontsize, math=True):
Apr 12, 2019
Apr 12, 2019
895
# Override prime symbol to use Bakoma.
Nov 5, 2015
Nov 5, 2015
896
if sym == r'\prime':
Feb 19, 2018
Feb 19, 2018
897
return self.bakoma._get_glyph(
898
fontname, font_class, sym, fontsize, math)
Nov 5, 2015
Nov 5, 2015
899
else:
Dec 4, 2015
Dec 4, 2015
900
# check whether the glyph is available in the display font
901
uniindex = get_unicode_index(sym)
902
font = self._get_font('ex')
903
if font is not None:
904
glyphindex = font.get_char_index(uniindex)
905
if glyphindex != 0:
Feb 19, 2018
Feb 19, 2018
906
return super()._get_glyph(
907
'ex', font_class, sym, fontsize, math)
Dec 4, 2015
Dec 4, 2015
908
# otherwise return regular glyph
Feb 19, 2018
Feb 19, 2018
909
return super()._get_glyph(
910
fontname, font_class, sym, fontsize, math)
Nov 5, 2015
Nov 5, 2015
911
912
Nov 5, 2015
Nov 5, 2015
913
class DejaVuSerifFonts(DejaVuFonts):
914
"""
915
A font handling class for the DejaVu Serif fonts
916
917
If a glyph is not found it will fallback to Stix Serif
918
"""
Jun 11, 2019
Jun 11, 2019
919
_fontmap = {
920
'rm': 'DejaVu Serif',
921
'it': 'DejaVu Serif:italic',
922
'bf': 'DejaVu Serif:weight=bold',
923
'sf': 'DejaVu Sans',
924
'tt': 'DejaVu Sans Mono',
925
'ex': 'DejaVu Serif Display',
926
0: 'DejaVu Serif',
927
}
Nov 5, 2015
Nov 5, 2015
928
Aug 16, 2018
Aug 16, 2018
929
Nov 5, 2015
Nov 5, 2015
930
class DejaVuSansFonts(DejaVuFonts):
931
"""
932
A font handling class for the DejaVu Sans fonts
933
934
If a glyph is not found it will fallback to Stix Sans
935
"""
Jun 11, 2019
Jun 11, 2019
936
_fontmap = {
937
'rm': 'DejaVu Sans',
938
'it': 'DejaVu Sans:italic',
939
'bf': 'DejaVu Sans:weight=bold',
940
'sf': 'DejaVu Sans',
941
'tt': 'DejaVu Sans Mono',
942
'ex': 'DejaVu Sans Display',
943
0: 'DejaVu Sans',
944
}
Nov 5, 2015
Nov 5, 2015
945
Aug 16, 2018
Aug 16, 2018
946
Nov 5, 2007
Nov 5, 2007
947
class StixFonts(UnicodeFonts):
Nov 26, 2007
Nov 26, 2007
948
"""
Oct 24, 2008
Oct 24, 2008
949
A font handling class for the STIX fonts.
950
951
In addition to what UnicodeFonts provides, this class:
952
953
- supports "virtual fonts" which are complete alpha numeric
954
character sets with different font styles at special Unicode
955
code points, such as "Blackboard".
956
957
- handles sized alternative characters for the STIXSizeX fonts.
Nov 26, 2007
Nov 26, 2007
958
"""
Jun 11, 2019
Jun 11, 2019
959
_fontmap = {
960
'rm': 'STIXGeneral',
961
'it': 'STIXGeneral:italic',
962
'bf': 'STIXGeneral:weight=bold',
963
'nonunirm': 'STIXNonUnicode',
964
'nonuniit': 'STIXNonUnicode:italic',
965
'nonunibf': 'STIXNonUnicode:weight=bold',
966
0: 'STIXGeneral',
967
1: 'STIXSizeOneSym',
968
2: 'STIXSizeTwoSym',
969
3: 'STIXSizeThreeSym',
970
4: 'STIXSizeFourSym',
971
5: 'STIXSizeFiveSym',
972
}
Nov 5, 2007
Nov 5, 2007
973
use_cmex = False
974
cm_fallback = False
Nov 28, 2007
Nov 28, 2007
975
_sans = False
Nov 5, 2007
Nov 5, 2007
976
977
def __init__(self, *args, **kwargs):
978
TruetypeFonts.__init__(self, *args, **kwargs)
Mar 31, 2009
Mar 31, 2009
979
self.fontmap = {}
Feb 18, 2018
Feb 18, 2018
980
for key, name in self._fontmap.items():
Mar 31, 2009
Mar 31, 2009
981
fullpath = findfont(name)
982
self.fontmap[key] = fullpath
983
self.fontmap[name] = fullpath
Nov 5, 2007
Nov 5, 2007
984
Nov 12, 2007
Nov 12, 2007
985
def _map_virtual_font(self, fontname, font_class, uniindex):
986
# Handle these "fonts" that are actually embedded in
987
# other fonts.
988
mapping = stix_virtual_fonts.get(fontname)
Aug 16, 2018
Aug 16, 2018
989
if (self._sans and mapping is None
990
and fontname not in ('regular', 'default')):
Nov 12, 2007
Nov 12, 2007
991
mapping = stix_virtual_fonts['sf']
992
doing_sans_conversion = True
993
else:
994
doing_sans_conversion = False
995
996
if mapping is not None:
997
if isinstance(mapping, dict):
Jan 25, 2017
Jan 25, 2017
998
try:
999
mapping = mapping[font_class]
1000
except KeyError: