Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ec5d0b0

Browse filesBrowse files
committed
DOC: update and reword fonts.rst
Account for the fact that font fallback is now merged and re-organize slightly.
1 parent 6f0d7f5 commit ec5d0b0
Copy full SHA for ec5d0b0

File tree

2 files changed

+139
-127
lines changed
Filter options

2 files changed

+139
-127
lines changed

‎doc/users/explain/fonts.rst

Copy file name to clipboard
+137-127Lines changed: 137 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
.. redirect-from:: /users/fonts
22

3-
Fonts in Matplotlib text engine
4-
===============================
3+
Fonts in Matplotlib
4+
===================
55

66
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.
1013

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>`.
1417

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):
2125

2226
.. list-table:: Type of Fonts
2327
:header-rows: 1
@@ -37,20 +41,19 @@ fonts', more about which is explained later in the guide):
3741
- Hinting supported (virtual machine processes the "hints")
3842
* - Non-subsetted through Matplotlib
3943
- 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>`__
4145

4246
NOTE: Adobe will disable support for authoring with Type 1 fonts in
4347
January 2023. `Read more here. <https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html>`_
4448

45-
Special mentions
46-
^^^^^^^^^^^^^^^^
49+
4750
Other font specifications which Matplotlib supports:
4851

4952
- Type 42 fonts (PS):
5053

5154
- PostScript wrapper around TrueType fonts
5255
- 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>`__
5457
to subset these types of fonts
5558

5659
- OpenType fonts:
@@ -60,50 +63,37 @@ Other font specifications which Matplotlib supports:
6063
- Generally contain a much larger character set!
6164
- Limited Support with Matplotlib
6265

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+
~~~~~~~~~~
10189

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.
10797

10898
This is especially helpful to generate *really lightweight* documents.::
10999

@@ -119,70 +109,90 @@ This is especially helpful to generate *really lightweight* documents.::
119109
fig.savefig("AFM_PDF.pdf", format="pdf")
120110
fig.savefig("AFM_PS.ps", format="ps)
121111

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/>`_!
188112

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.

‎tutorials/text/text_props.py

Copy file name to clipboardExpand all lines: tutorials/text/text_props.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@
215215
# matplotlib.rcParams['font.family'] = ['Family1', 'SerifFamily1', 'SerifFamily2', 'Family2']
216216
#
217217
#
218+
# .. _font-nonlatin:
219+
#
218220
# Text with non-latin glyphs
219221
# ==========================
220222
#

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.