-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Expand file tree
/
Copy pathfonts.py
More file actions
186 lines (148 loc) · 8.38 KB
/
fonts.py
File metadata and controls
186 lines (148 loc) · 8.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
r"""
.. redirect-from:: /users/fonts
.. redirect-from:: /users/explain/fonts
.. _fonts:
Fonts in Matplotlib
===================
Matplotlib needs fonts to work with its text engine, some of which are shipped
alongside the installation. The default font is `DejaVu Sans
<https://dejavu-fonts.github.io>`_ which covers most European writing systems.
However, users can configure the default fonts, and provide their own custom
fonts. See :ref:`Customizing text properties <text_props>` for
details and :ref:`font-nonlatin` in particular for glyphs not supported by
DejaVu Sans.
Matplotlib also provides an option to offload text rendering to a TeX engine
(``usetex=True``), see :ref:`Text rendering with LaTeX
<usetex>`.
Fonts in PDF and PostScript
---------------------------
Fonts have a long (and sometimes incompatible) history in computing, leading to
different platforms supporting different types of fonts. In practice,
Matplotlib supports three font specifications (in addition to pdf 'core fonts',
which are explained later in the guide):
.. table:: Types of Fonts
+--------------------------+----------------------------+-------------------------------+
| Type 1 (PDF with usetex) | Type 3 (PDF/PS) | TrueType (PDF) / Type 42 (PS) |
+==========================+============================+===============================+
| Old font types introduced by Adobe. | Newer font type introduced by |
| | Apple; commonly used today. |
+--------------------------+----------------------------+-------------------------------+
| Restricted subset of | Full PostScript language, | Includes a virtual machine |
| PostScript, charstrings | allows embedding arbitrary | that can execute code. |
| are in bytecode. | code. | |
+--------------------------+----------------------------+-------------------------------+
| Supports font hinting. | Does not support font | Supports font hinting, |
| | hinting. | through the virtual machine. |
+--------------------------+----------------------------+-------------------------------+
| Subsetted by code in | Subsetted via external module |
| `matplotlib._type1font`. | `fontTools <https://github.com/fonttools/fonttools>`__. |
+--------------------------+----------------------------+-------------------------------+
.. note::
Adobe disabled__ support for authoring with Type 1 fonts in January 2023.
Matplotlib uses Type 1 fonts for compatibility with TeX; when the usetex
feature is used with the PDF backend, Matplotlib reads the fonts used by
the TeX engine, which are usually Type 1.
__ https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html
Matplotlib also provides limited support for OpenType fonts, a newer standard
developed jointly by Adobe and Microsoft; such fonts generally contain a much
larger character set.
Font subsetting
^^^^^^^^^^^^^^^
The PDF and PostScript formats support embedding fonts in files, allowing the
display program to correctly render the text, independent of what fonts are
installed on the viewer's computer and without the need to pre-rasterize the text.
This ensures that if the output is zoomed or resized the text does not become
pixelated. However, embedding full fonts in the file can lead to large output
files, particularly with fonts with many glyphs such as those that support CJK
(Chinese/Japanese/Korean).
To keep the output size reasonable while using vector fonts,
Matplotlib embeds only the glyphs that are actually used in the document.
This is known as font subsetting.
Computing the font subset and writing the reduced font are both complex problems,
which Matplotlib solves in most cases by using the
`fontTools <https://fonttools.readthedocs.io/en/latest/>`__ library.
Core Fonts
^^^^^^^^^^
In addition to the ability to embed fonts, as part of the `PostScript
<https://en.wikipedia.org/wiki/PostScript_fonts#Core_Font_Set>`_ and `PDF
specification
<https://docs.oracle.com/cd/E96927_01/TSG/FAQ/What%20are%20the%2014%20base%20fonts%20distributed%20with%20Acroba.html>`_
there are 14 Core Fonts that compliant viewers must ensure are available. If
you restrict your document to only these fonts you do not have to embed any
font information in the document but still get vector text.
This is especially helpful to generate *really lightweight* documents::
# trigger core fonts for PDF backend
plt.rcParams["pdf.use14corefonts"] = True
# trigger core fonts for PS backend
plt.rcParams["ps.useafm"] = True
chars = "AFM ftw!"
fig, ax = plt.subplots()
ax.text(0.5, 0.5, chars)
fig.savefig("AFM_PDF.pdf", format="pdf")
fig.savefig("AFM_PS.ps", format="ps")
Fonts in SVG
------------
Text can output to SVG in two ways controlled by :rc:`svg.fonttype`:
- as a path (``'path'``) in the SVG
- as string in the SVG with font styling on the element (``'none'``)
When saving via ``'path'`` Matplotlib will compute the path of the glyphs used
as vector paths and write those to the output. The advantage of doing so is
that the SVG will look the same on all computers independent of what fonts are
installed. However the text will not be editable after the fact.
In contrast, saving with ``'none'`` will result in smaller files and the
text will appear directly in the markup. However, the appearance may vary
based on the SVG viewer and what fonts are available.
Fonts in Agg
------------
To output text to raster formats via Agg, Matplotlib relies on `FreeType
<https://www.freetype.org/>`_. Because the exact rendering of the glyphs
changes between FreeType versions we pin to a specific version for our image
comparison tests.
How Matplotlib selects fonts
----------------------------
Internally, using a font in Matplotlib is a three step process:
1. a `.FontProperties` object is created (explicitly or implicitly)
2. based on the `.FontProperties` object the methods on `.FontManager` are used
to select the closest "best" font Matplotlib is aware of (except for
``'none'`` mode of SVG).
3. the Python proxy for the font object is used by the backend code to render
the text -- the exact details depend on the backend via `.font_manager.get_font`.
The algorithm to select the "best" font is a modified version of the algorithm
specified by the `CSS1 Specifications
<http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ which is used by web browsers.
This algorithm takes into account the font family name (e.g. "Arial", "Noto
Sans CJK", "Hack", ...), the size, style, and weight. In addition to family
names that map directly to fonts there are five "generic font family names"
(serif, monospace, fantasy, cursive, and sans-serif) that will internally be
mapped to any one of a set of fonts.
Currently the public API for doing step 2 is `.FontManager.findfont` (and that
method on the global `.FontManager` instance is aliased at the module level as
`.font_manager.findfont`), which will only find a single font and return the absolute
path to the font on the filesystem.
Font fallback
-------------
There is no font that covers the entire Unicode space thus it is possible for the
users to require a mix of glyphs that cannot be satisfied from a single font.
While it has been possible to use multiple fonts within a Figure, on distinct
`.Text` instances, it was not previous possible to use multiple fonts in the
same `.Text` instance (as a web browser does). As of Matplotlib 3.6 the Agg,
SVG, PDF, and PS backends will "fallback" through multiple fonts in a single
`.Text` instance:
.. plot::
:include-source:
:caption: The string "There are 几个汉字 in between!" rendered with 2 fonts.
fig, ax = plt.subplots()
ax.text(
.5, .5, "There are 几个汉字 in between!",
family=['DejaVu Sans', 'Noto Sans CJK JP', 'Noto Sans TC'],
ha='center'
)
Internally this is implemented by setting The "font family" on
`.FontProperties` objects to a list of font families. A (currently)
private API extracts a list of paths to all of the fonts found and then
constructs a single `.ft2font.FT2Font` object that is aware of all of the fonts.
Each glyph of the string is rendered using the first font in the list that
contains that glyph.
A majority of this work was done by Aitik Gupta supported by Google Summer of
Code 2021.
""" # noqa: E501