1
1
.. redirect-from :: /users/fonts
2
2
3
- Fonts in Matplotlib text engine
4
- ===============================
3
+ Fonts in Matplotlib
4
+ ===================
5
5
6
6
Matplotlib needs fonts to work with its text engine, some of which are shipped
7
- alongside the installation. However, users can configure the default fonts, or
8
- even provide their own custom fonts! For more details, see :doc: `Customizing
9
- text properties </tutorials/text/text_props>`.
7
+ alongside the installation. The default font is `DejaVu Sans
8
+ <https://dejavu-fonts.github.io> `_ which covers most European writing systems.
9
+ However, users can configure the default fonts, and provide their own custom
10
+ fonts. See :doc: `Customizing text properties </tutorials/text/text_props >` for
11
+ details and :ref: `font-nonlatin ` in particular for glyphs not supported by
12
+ DejaVu Sans.
10
13
11
- However, Matplotlib also provides an option to offload text rendering to a TeX
12
- engine (``usetex=True ``),
13
- see :doc: ` Text rendering with LaTeX </tutorials/text/usetex >`.
14
+ Matplotlib also provides an option to offload text rendering to a TeX engine
15
+ (``usetex=True ``), see :doc: ` Text rendering with LaTeX
16
+ </tutorials/text/usetex>`.
14
17
15
- Font specifications
16
- -------------------
17
- Fonts have a long and sometimes incompatible history in computing, leading to
18
- different platforms supporting different types of fonts. In practice, there are
19
- 3 types of font specifications Matplotlib supports (in addition to 'core
20
- fonts', more about which is explained later in the guide):
18
+ Fonts in PDF and PostScript
19
+ ---------------------------
20
+
21
+ Fonts have a long (and sometimes incompatible) history in computing, leading to
22
+ different platforms supporting different types of fonts. In practice, there
23
+ are 3 types of font specifications Matplotlib supports (in addition to 'core
24
+ fonts' in pdf which is explained later in the guide):
21
25
22
26
.. list-table :: Type of Fonts
23
27
:header-rows: 1
@@ -37,20 +41,19 @@ fonts', more about which is explained later in the guide):
37
41
- Hinting supported (virtual machine processes the "hints")
38
42
* - Non-subsetted through Matplotlib
39
43
- Subsetted via external module `ttconv <https://github.com/sandflow/ttconv >`_
40
- - Subsetted via external module `fonttools <https://github.com/fonttools/fonttools >`_
44
+ - Subsetted via external module `fonttools <https://github.com/fonttools/fonttools >`__
41
45
42
46
NOTE: Adobe will disable support for authoring with Type 1 fonts in
43
47
January 2023. `Read more here. <https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html >`_
44
48
45
- Special mentions
46
- ^^^^^^^^^^^^^^^^
49
+
47
50
Other font specifications which Matplotlib supports:
48
51
49
52
- Type 42 fonts (PS):
50
53
51
54
- PostScript wrapper around TrueType fonts
52
55
- 42 is the `Answer to Life, the Universe, and Everything! <https://en.wikipedia.org/wiki/Answer_to_Life,_the_Universe,_and_Everything >`_
53
- - Matplotlib uses an external library called `fonttools <https://github.com/fonttools/fonttools >`_
56
+ - Matplotlib uses an external library called `fonttools <https://github.com/fonttools/fonttools >`__
54
57
to subset these types of fonts
55
58
56
59
- OpenType fonts:
@@ -60,50 +63,37 @@ Other font specifications which Matplotlib supports:
60
63
- Generally contain a much larger character set!
61
64
- Limited Support with Matplotlib
62
65
63
- Subsetting
64
- ----------
65
- Matplotlib is able to generate documents in multiple different formats. Some of
66
- those formats (for example, PDF, PS/EPS, SVG) allow embedding font data in such
67
- a way that when these documents are visually scaled, the text does not appear
68
- pixelated.
69
-
70
- This can be achieved by embedding the *whole * font file within the
71
- output document. However, this can lead to very large documents, as some
72
- fonts (for instance, CJK - Chinese/Japanese/Korean fonts) can contain a large
73
- number of glyphs, and thus their embedded size can be quite huge.
74
-
75
- Font Subsetting can be used before generating documents, to embed only the
76
- *required * glyphs within the documents. Fonts can be considered as a collection
77
- of glyphs, so ultimately the goal is to find out *which * glyphs are required
78
- for a certain array of characters, and embed only those within the output.
79
-
80
- .. note ::
81
- The role of subsetter really shines when we encounter characters like **ä **
82
- (composed by calling subprograms for **a ** and **¨ **); since the subsetter
83
- has to find out *all * such subprograms being called by every glyph included
84
- in the subset, this is a generally difficult problem!
85
-
86
- Luckily, Matplotlib uses a fork of an external dependency called
87
- `ttconv <https://github.com/sandflow/ttconv >`_, which helps in embedding and
88
- subsetting font data. (however, recent versions have moved away from ttconv to
89
- pure Python for certain types: for more details visit
90
- `these <https://github.com/matplotlib/matplotlib/pull/18370 >`_, `links <https://github.com/matplotlib/matplotlib/pull/18181 >`_)
91
-
92
- | *Type 1 fonts are still non-subsetted* through Matplotlib. (though one will encounter these mostly via *usetex*/*dviread* in PDF backend)
93
- | **Type 3 and Type 42 fonts are subsetted**, with a fair amount of exceptions and bugs for the latter.
94
-
95
- What to use?
96
- ------------
97
- Practically, most fonts that are readily available on most operating systems or
98
- are readily available on the internet to download include *TrueType fonts * and
99
- its "extensions" such as MacOS-resource fork fonts and the newer OpenType
100
- fonts.
66
+ Font Subsetting
67
+ ~~~~~~~~~~~~~~~
68
+
69
+ The PDF and PostScript formats support embedding fonts in files allowing the
70
+ display program to correctly render the text, independent of what fonts are
71
+ installed on the viewer's computer and without the need to pre-rasterize the text.
72
+ This ensures that if the output is zoomed or resized the text does not become
73
+ pixelated. However, embedding full fonts in the file can lead to large output
74
+ files, particularly with fonts with many glyphs such as those that support CJK
75
+ (Chinese/Japanese/Korean).
76
+
77
+ The solution to this problem is to subset the fonts used in the document and
78
+ only embed the glyphs actually used. This gets both vector text and small
79
+ files sizes. Computing the subset of the font required and writing the new
80
+ (reduced) font are both complex problem and thus Matplotlib relies on
81
+ `fontTools <https://fonttools.readthedocs.io/en/latest/ >`__ and a vendored fork
82
+ of `ttconv <https://github.com/sandflow/ttconv >`_.
83
+
84
+ Currently Type 3, Type 42, and TrueType fonts are subseted. Type 1 fonts are not.
85
+
86
+
87
+ Core Fonts
88
+ ~~~~~~~~~~
101
89
102
- PS and PDF backends provide support for yet another type of fonts, which remove
103
- the need of subsetting altogether! These are called **Core Fonts **, and
104
- Matplotlib calls them via the keyword **AFM **; all that is supplied from
105
- Matplotlib to such documents are font metrics (specified in AFM format), and it
106
- is the job of the viewer applications to supply the glyph definitions.
90
+ In addition to the ability to embed fonts, as part of the `PostScript
91
+ <https://en.wikipedia.org/wiki/PostScript_fonts#Core_Font_Set> `_ and `PDF
92
+ specification
93
+ <https://docs.oracle.com/cd/E96927_01/TSG/FAQ/What%20are%20the%2014%20base%20fonts%20distributed%20with%20Acroba.html> `_
94
+ there are 14 Core Font that compliant viewers must ensure are available. If
95
+ you restrict your document to only these fonts you do not have to embed any
96
+ font information in the document but still get vector text.
107
97
108
98
This is especially helpful to generate *really lightweight * documents.::
109
99
@@ -119,14 +109,89 @@ This is especially helpful to generate *really lightweight* documents.::
119
109
fig.savefig("AFM_PDF.pdf", format="pdf")
120
110
fig.savefig("AFM_PS.ps", format="ps)
121
111
122
- .. note ::
123
- These core fonts are limited to PDF and PS backends only; they can not be
124
- rendered in other backends.
125
112
126
- Another downside to this is that while the font metrics are standardized,
127
- different PDF viewer applications will have different fonts to render these
128
- metrics. In other words, the **output might look different on different
129
- viewers **, as well as (let's say) Windows and Linux, if Linux tools included
130
- free versions of the proprietary fonts.
113
+ Fonts in SVG
114
+ ------------
115
+
116
+ Text can output to SVG in two ways controlled by :rc: `svg.fonttype `:
117
+
118
+ - as a path (``'path' ``) in the SVG
119
+ - as string in the SVG with font styling on the element (``'none' ``)
120
+
121
+
122
+ When saving via ``'path' `` Matplotlib will compute the path of the glyphs used
123
+ as vector paths and write those to the output. The advantage of this is that
124
+ the SVG will look the same on all computers independent of what fonts are
125
+ installed. However the text will not be editable after the fact.
126
+ In contrast saving with ``'none' `` will result in smaller files and the
127
+ text will appear directly in the markup. However, the appearance may vary
128
+ based on the SVG viewer and what fonts are available.
129
+
130
+ Fonts in Agg
131
+ ------------
131
132
132
- This also violates the *what-you-see-is-what-you-get * feature of Matplotlib.
133
+ To output text to raster formats via Agg, Matplotlib relies on `FreeType
134
+ <https://www.freetype.org/> `_. Because the exact rendering of the glyphs
135
+ changes between FreeType versions we pin to a specific version for our image
136
+ comparison tests.
137
+
138
+
139
+ How Matplotlib selects fonts
140
+ ----------------------------
141
+
142
+ Internally using a Font in Matplotlib is a three step process:
143
+
144
+ 1. a `.FontProperties ` object is created (explicitly or implicitly)
145
+ 2. based on the `.FontProperties ` object the methods on `.FontManager ` are used
146
+ to select the closest "best" font Matplotlib is aware of (except for
147
+ ``'none' `` mode of SVG).
148
+ 3. the Python proxy for the font object is used by the backend code to render
149
+ the text -- the exact details depend on the backend via `.font_manager.get_font `.
150
+
151
+ The algorithm to select the "best" font is a modified version of the algorithm
152
+ specified by the `CSS1 Specifications
153
+ <http://www.w3.org/TR/1998/REC-CSS2-19980512/> `_ which is used by web browsers.
154
+ This algorithm takes into account the font family name (e.g. "Arial", "Noto
155
+ Sans CJK", "Hack", ...), the size, style, and weight. In addition to family
156
+ names that map directly to fonts there are five "generic font family names" (
157
+ serif, monospace, fantasy, cursive, and sans-serif) that wi
6377
ll internally be
158
+ mapped to any one of a set of fonts.
159
+
160
+ Currently the public API for doing step 2 is `.FontManager.findfont ` (and that
161
+ method on the global `.FontManager ` instance is aliased at the module level as
162
+ `.font_manager.findfont `), which will only find a single font and return the absolute
163
+ path to the font on the filesystem.
164
+
165
+ Font Fallback
166
+ -------------
167
+
168
+ There is no font that covers the entire Unicode space thus it is possible for the
169
+ users to require a mix of glyphs that can not be satisfied from a single font.
170
+ While it has been possible to use multiple fonts within a Figure, on distinct
171
+ `.Text ` instances, it was not previous possible to use multiple fonts in the
172
+ same `.Text ` instance (as a web browser does). As of Matplotlib 3.6 the Agg,
173
+ SVG, PDF, and PS backends will "fallback" through multiple fonts in a single
174
+ `.Text ` instance:
175
+
176
+
177
+ .. plot ::
178
+ :include-source:
179
+ :caption: The string "There are 几个汉字 in between!" rendered with 2 fonts.
180
+
181
+ fig, ax = plt.subplots()
182
+ ax.text(
183
+ .5, .5, "There are 几个汉字 in between!",
184
+ family=['DejaVu Sans', 'WenQuanYi Zen Hei'],
185
+ ha='center'
186
+ )
187
+
188
+
189
+ Internally this is implemented by setting The "font family" on
190
+ `.FontProperties ` objects to a list of font families. A (currently)
191
+ private API extracts a list of paths to all of the fonts found and then
192
+ constructs a single `.ft2font.FT2Font ` object that is aware of all of the fonts.
193
+ Each glyph of the string is rendered using the first font in the list that
194
+ contains that glyph.
195
+
196
+ A majority of this work was done by Aitik Gupta supported by Google Summer of
197
+ Code 2021.
0 commit comments