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
+ PDF and postscript support embedded fonts in the output files allowing the
70
+ display program to correctly render the text, independent of what fonts are
71
+ installed on the viewer's computer, 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 a
81
+ vendored fork of `ttconv <https://github.com/sandflow/ttconv >`_ and `fontTools
82
+ <https://fonttools.readthedocs.io/en/latest/> `__.
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,70 +109,90 @@ 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
-
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.
131
-
132
- This also violates the *what-you-see-is-what-you-get * feature of Matplotlib.
133
-
134
- Are we reinventing the wheel?
135
- -----------------------------
136
- Internally, a feasible response to the question of 'reinventing the
137
- wheel would be, well, Yes *and No *. The font-matching algorithm used
138
- by Matplotlib has been *inspired * by web browsers, more specifically,
139
- `CSS Specifications <http://www.w3.org/TR/1998/REC-CSS2-19980512/ >`_.
140
-
141
- Currently, the simplest way (and the only way) to tell Matplotlib what fonts
142
- you want it to use for your document is via the **font.family ** rcParam,
143
- see :doc: `Customizing text properties </tutorials/text/text_props >`.
144
-
145
- This is similar to how one tells a browser to use multiple font families
146
- (specified in their order of preference) for their HTML webpages. By using
147
- **font-family ** in their stylesheet, users can essentially trigger a very
148
- useful feature provided by browers, known as Font-Fallback. For example, the
149
- following snippet in an HTML markup would:
150
-
151
- .. code-block :: html
152
-
153
- <style >
154
- someTag {
155
- font-family : Arial , Helvetica , sans-serif ;
156
- }
157
- </style >
158
-
159
- <!-- somewhere in the main body -->
160
- <someTag >
161
- some text
162
- </someTag >
163
-
164
-
165
- For every character/glyph in *"some text" *, the browser will iterate through
166
- the whole list of font-families, and check whether that character/glyph is
167
- available in that font-family. As soon as a font is found which has the
168
- required glyph(s), the browser uses that font to render that character, and
169
- subsequently moves on to the next character.
170
-
171
- How does Matplotlib achieve this?
172
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
173
- Currently, Matplotlib can't render a multi-font document. It was initially
174
- only designed to use a **single font ** throughout the document, i.e., no matter
175
- how many families you pass to **font.family ** rcParam, Matplotlib would use the
176
- very first font it's able to find on your system, and try to render all your
177
- characters/glyphs from that *and only that * font.
178
-
179
- .. note ::
180
- This is, because the internal font matching was written/adapted
181
- from a very old `CSS1 spec <http://www.w3.org/TR/1998/REC-CSS2-19980512/ >`_,
182
- **written in 1998 **!
183
-
184
- However, allowing multiple fonts for a single document (also enabling
185
- Font-Fallback) is one of the goals for 2021's Google Summer of Code project.
186
-
187
- `Read more on Matplotblog <https://matplotlib.org/matplotblog/ >`_!
188
112
113
+ Fonts in SVG
114
+ ------------
115
+
116
+ Text can output to SVG in two ways controlled by the :rc: `svg.fonttype `
117
+ rcparam:
118
+
119
+ - as a path (``'path' ``) in the SVG
120
+ - as string in the SVG with font styling on the element (``'none' ``)
121
+
122
+
123
+ When saving via ``'path' `` Matplotlib will compute the path of the glyphs used
124
+ as vector paths and write those to the output. The advantage of this is that
125
+ the SVG will look the same on all computers independent of what fonts are
126
+ installed. However the text will not be editable after the fact.
127
+ In contrast saving with ``'none' `` will result in smaller files and the
128
+ text will appear directly in the markup. However, the appearance may vary
129
+ based on the SVG viewer and what fonts are available.
130
+
131
+ Fonts in Agg
132
+ ------------
133
+
134
+ To output text to raster formats via Agg Matplotlib relies on `FreeType
135
+ <https://www.freetype.org/> `_. Because the exactly rendering of the glyphs
136
+ changes between FreeType versions we pin to a specific version for our image
137
+ comparison tests.
138
+
139
+
140
+ How Matplotlib selects fonts
141
+ ----------------------------
142
+
143
+ Internally using a Font in Matplotlib is a three step process:
144
+
145
+ 1. a `.FontProperties ` object is created (explicitly or implicitly)
146
+ 2. based on the `.FontProperties ` object the methods on `.FontManager ` are used
147
+ to select the closest the "best" font Matplotlib is aware of (except for
148
+ ``'none' `` mode of SVG).
149
+ 3. the Python proxy for the font object is used by the backend code to render
150
+ the text -- the exact details depend on the backend via `.font_manager.get_font `.
151
+
152
+ The algorithm to select the "best" font is a modified version of the algorithm
153
+ specified by the `CSS1 Specifications
154
+ <http://www.w3.org/TR/1998/REC-CSS2-19980512/> `_ which is used by web browsers.
155
+ This algorithm takes into account the font family name (e.g. "Arial", "Noto
156
+ Sans CJK", "Hack", ...), the size, style, and weight. In addition to family
157
+ names that map directly to fonts there are five "generic font family names" (
158
+ serif, monospace, fantasy, cursive, and sans-serif) that will internally be
159
+ mapped to any one of a set of fonts.
160
+
161
+ Currently the public API for doing step 2 is `.FontManager.findfont ` (and that
162
+ method on the global `.FontManager ` instance is aliased at the module level as
163
+ `.font_manager.findfont `) will only find a single font and return the absolute
164
+ path to the font on the filesystem.
165
+
166
+ Font Fallback
167
+ -------------
168
+
169
+ There is no font that covers the unicode space thus it is possible for the
170
+ users to require a mix of glyphs that can not be satisfied from a single font.
171
+ While it has been possible to use multiple fonts within a Figure, on distinct
172
+ `.Text ` instances, it was not previous possible to use multiple fonts in the
173
+ same `.Text ` instance (as a web browser does). As of Matplotlib 3.6 the Agg,
174
+ SVG, PDF, and PS backends will "fallback" through multiple fonts in a single
175
+ `.Text ` instance:
176
+
177
+
178
+ .. plot ::
179
+ :include-source:
180
+ :caption: The string "There are 几个汉字 in between!" rendered with 2 fonts.
181
+
182
+ fig, ax = plt.subplots()
183
+ ax.text(
184
+ .5, .5, "There are 几个汉字 in between!",
185
+ family=['DejaVu Sans', 'WenQuanYi Zen Hei'],
186
+ ha='center'
187
+ )
188
+
189
+
190
+ Internally this is implemented by setting The "font family" on
191
+ `.FontProperties ` objects to a list of font families. Using a (currently)
192
+ private API extract a list of paths to all of the fonts found and then
193
+ construct a single `.ft2font.FT2Font ` object that is aware of all of the fonts.
194
+ Each glyph of the string is rendered using the first font in the list that
195
+ contains that glyph.
196
+
197
+ A majority of this work was done by Aitik Gupta supported by Google Summer of
198
+ Code 2021.
0 commit comments