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+ 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
10898This 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.
0 commit comments