11.. redirect-from :: /users/fonts
22
3- Fonts in Matplotlib text engine
4- ===============================
3+ Fonts in Matplotlib
4+ ===================
55
66Matplotlib 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
4246NOTE: Adobe will disable support for authoring with Type 1 fonts in
4347January 2023. `Read more here. <https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html >`_
4448
45- Special mentions
46- ^^^^^^^^^^^^^^^^
49+
4750Other 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+ 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+ ~~~~~~~~~~
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
10898This is especially helpful to generate *really lightweight * documents.::
10999
@@ -119,14 +109,89 @@ 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.
125112
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+ ------------
131132
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 will 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